diff --git a/.gitignore b/.gitignore index af441a4..f53b832 100644 --- a/.gitignore +++ b/.gitignore @@ -9,4 +9,5 @@ db.sqlite-journal # assets /depell/src/*.gz /depell/src/*.wasm +#**/*-sv.rs /bytecode/src/instrs.rs diff --git a/Cargo.lock b/Cargo.lock index 2992a05..f1ffd7c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -276,15 +276,15 @@ dependencies = [ [[package]] name = "bytes" -version = "1.7.2" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" +checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da" [[package]] name = "cc" -version = "1.1.30" +version = "1.1.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b16803a61b81d9eabb7eae2588776c4c1e584b738ede45fdbb4c972cec1e9945" +checksum = "baee610e9452a8f6f0a1b6194ec09ff9e2d85dea54432acdae41aa0761c95d70" dependencies = [ "jobserver", "libc", @@ -721,9 +721,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hyper" -version = "1.4.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05" +checksum = "bbbff0a806a4728c99295b254c8838933b5b082d75e3cb70c8dab21fdfbcfa9a" dependencies = [ "bytes", "futures-channel", @@ -741,9 +741,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41296eb09f183ac68eec06e03cdbea2e759633d4067b2f6552fc2e009bcad08b" +checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" dependencies = [ "bytes", "futures-util", @@ -979,18 +979,18 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pin-project" -version = "1.1.6" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf123a161dde1e524adf36f90bc5d8d3462824a9c43553ad07a8183161189ec" +checksum = "be57f64e946e500c8ee36ef6331845d40a93055567ec57e8fae13efd33759b95" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.6" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4502d8515ca9f32f1fb543d987f63d95a14934883db45bdb48060b6b69257f8" +checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c" dependencies = [ "proc-macro2", "quote", @@ -999,9 +999,9 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" +checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" [[package]] name = "pin-utils" @@ -1073,9 +1073,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.11.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", @@ -1162,9 +1162,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.14" +version = "0.23.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "415d9944693cb90382053259f89fbb077ea730ad7273047ec63b19bc9b160ba8" +checksum = "eee87ff5d9b36712a58574e12e9f0ea80f915a5b0ac518d322b24a465617925e" dependencies = [ "aws-lc-rs", "once_cell", @@ -1185,9 +1185,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e696e35370c65c9c541198af4543ccd580cf17fc25d8e05c5a242b202488c55" +checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b" [[package]] name = "rustls-webpki" @@ -1203,9 +1203,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" +checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" [[package]] name = "ryu" @@ -1241,9 +1241,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.128" +version = "1.0.132" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" +checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" dependencies = [ "itoa", "memchr", @@ -1366,9 +1366,9 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "tokio" -version = "1.40.0" +version = "1.41.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" +checksum = "22cfb5bee7a6a52939ca9224d6ac897bb669134078daa8735560897f69de4d33" dependencies = [ "backtrace", "bytes", diff --git a/depell/wasm-hbc/src/lib.rs b/depell/wasm-hbc/src/lib.rs index 89a3569..a0505d8 100644 --- a/depell/wasm-hbc/src/lib.rs +++ b/depell/wasm-hbc/src/lib.rs @@ -6,7 +6,7 @@ use { alloc::{string::String, vec::Vec}, hblang::{ parser::FileId, - son::{Codegen, CodegenCtx}, + son::{hbvm::HbvmBackend, Codegen, CodegenCtx}, }, }; @@ -55,8 +55,9 @@ unsafe fn compile_and_run(mut fuel: usize) { files }; + let mut ctx = CodegenCtx::default(); + let files = { - let mut ctx = hblang::parser::Ctx::default(); let paths = files.iter().map(|f| f.path).collect::>(); let mut loader = |path: &str, _: &str, kind| match kind { hblang::parser::FileKind::Module => Ok(paths.binary_search(&path).unwrap() as FileId), @@ -69,7 +70,7 @@ unsafe fn compile_and_run(mut fuel: usize) { f.path, // since 'free' does nothing this is fine String::from_raw_parts(f.code.as_mut_ptr(), f.code.len(), f.code.len()), - &mut ctx, + &mut ctx.parser, &mut loader, ) }) @@ -77,9 +78,15 @@ unsafe fn compile_and_run(mut fuel: usize) { }; let mut ct = { - let mut ctx = CodegenCtx::default(); - let mut c = Codegen::new(&files, &mut ctx); - c.generate(root as FileId); + let mut backend = HbvmBackend::default(); + Codegen::new(&mut backend, &files, &mut ctx).generate(root as FileId); + + if !ctx.parser.errors.borrow().is_empty() { + log::error!("{}", ctx.parser.errors.borrow()); + return; + } + + let mut c = Codegen::new(&mut backend, &files, &mut ctx); c.assemble_comptime() }; diff --git a/f.txt b/f.txt deleted file mode 100644 index 5ca0531..0000000 --- a/f.txt +++ /dev/null @@ -1,47 +0,0 @@ -start: 0 - 46-c65535: +: [0, 38, 45] [47] - 45-c65535: &: [0, 43, 44] [46] - 44-c65535: cint: #255 [0] [45] - 43-c65535: load: [0, 42, 23] [45] - 42-c65535: +: [0, 22, 18] [43] - 39-c65535: stre: [0, 0, 36, 37] [47] - 39-c65535: stre: [0, 0, 36, 37] [47] - 37-c65535: stre: [0, 35, 36, 3] [38, 39] - 36-c65535: stck: [0, 3] [37, 38, 39] - 35-c65535: load: [0, 24, 34] [37] - 34-c65535: stre: [0, 31, 33, 30] [35, 47] - 33-c65535: +: [0, 24, 32] [34] - 32-c65535: cint: #16 [0] [33] - 31-c65535: cint: #4 [0] [34] - 30-c65535: stre: [0, 27, 29, 26] [34] - 29-c65535: +: [0, 24, 28] [30] - 28-c65535: cint: #8 [0] [29] - 27-c65535: cint: #2 [0] [30] - 26-c65535: stre: [0, 25, 24, 3] [30] - 25-c65535: cint: #1 [0] [26] - 24-c65535: stck: [0, 3] [26, 29, 33, 35] - 23-c65535: stre: [0, 21, 22, 3] [43, 47] - 22-c65535: stck: [0, 3] [23, 42] - 21-c65535: load: [0, 6, 20] [23] - 20-c65535: stre: [0, 17, 19, 14] [21, 47] - 19-c65535: +: [0, 6, 18] [20] - 18-c65535: cint: #3 [0] [19, 42] - 17-c65535: cint: #1 [0] [20] - 14-c65535: stre: [0, 5, 13, 11] [20] - 13-c65535: +: [0, 6, 12] [14] - 12-c65535: cint: #2 [0] [13] - 11-c65535: stre: [0, 7, 10, 8] [14] - 10-c65535: +: [0, 6, 9] [11] - 9-c65535: cint: #1 [0] [10] - 8-c65535: stre: [0, 7, 6, 3] [11] - 7-c65535: cint: #0 [0] [8, 11] - 6-c65535: stck: [0, 3] [8, 10, 13, 19, 21] - 5-c65535: cint: #511 [0] [14] - 4-c65535: loops: [0] [] - 3-c65535: mem: [0] [6, 8, 22, 23, 24, 26, 36, 37, 47] - 2-c65535: ctrl: entry [0] [38] -b2: 0 0 [38] - 38-c65535: call: 1 0 [2, 36, 37] [46, 47] - 47-c65535: ret: [38, 46, 3, 20, 23, 34, 39] [1] - - diff --git a/f1.txt b/f1.txt deleted file mode 100644 index e885f9e..0000000 --- a/f1.txt +++ /dev/null @@ -1,38 +0,0 @@ - -start: 0 - 34-c65535: stre: [0, 31, 51, 30] [39, 47, 38] - 51-c65535: +: [0, 36, 32] [34] - 32-c65535: cint: #16 [0] [51] - 31-c65535: cint: #4 [0] [34] - 30-c65535: stre: [0, 27, 50, 26] [34] - 50-c65535: +: [0, 36, 28] [30] - 28-c65535: cint: #8 [0] [50] - 27-c65535: cint: #2 [0] [30] - 26-c65535: stre: [0, 25, 36, 3] [30] - 25-c65535: cint: #1 [0] [26, 46] - 39-c65535: stre: [0, 0, 36, 34] [47] - 36-c65535: stck: [0, 3] [51, 38, 39, 26, 50] - 22-c65535: stck: [0, 3] [49, 42, 8, 48] - 39-c65535: stre: [0, 0, 36, 34] [47] - 20-c65535: stre: [0, 17, 42, 14] [47, 47] - 46-c65535: +: [0, 38, 25] [47] - 18-c65535: cint: #3 [0] [42] - 17-c65535: cint: #1 [0] [20] - 14-c65535: stre: [0, 5, 49, 11] [20] - 49-c65535: +: [0, 22, 12] [14] - 12-c65535: cint: #2 [0] [49] - 11-c65535: stre: [0, 7, 48, 8] [14] - 48-c65535: +: [0, 22, 9] [11] - 9-c65535: cint: #1 [0] [48] - 8-c65535: stre: [0, 7, 22, 3] [11] - 7-c65535: cint: #0 [0] [8, 11] - 42-c65535: +: [0, 22, 18] [20] - 5-c65535: cint: #511 [0] [14] - 4-c65535: loops: [0] [] - 3-c65535: mem: [0] [47, 8, 22, 26, 36] - 2-c65535: ctrl: entry [0] [38] -b2: 0 0 [38] - 38-c65535: call: 1 0 [2, 36, 34] [46, 47] - 47-c65535: ret: [38, 46, 3, 20, 20, 34, 39] [1] - - diff --git a/f2.txt b/f2.txt deleted file mode 100644 index cf1429e..0000000 --- a/f2.txt +++ /dev/null @@ -1,39 +0,0 @@ -start: 0 - 42-c65535: +: [0, 22, 18] [43, 20] - 36-c65535: stck: [0, 3] [51, 38, 39, 26, 50] - 39-c65535: stre: [0, 0, 36, 34] [47] - 34-c65535: stre: [0, 31, 51, 30] [39, 47, 38] - 51-c65535: +: [0, 36, 32] [34] - 32-c65535: cint: #16 [0] [51] - 31-c65535: cint: #4 [0] [34] - 30-c65535: stre: [0, 27, 50, 26] [34] - 50-c65535: +: [0, 36, 28] [30] - 28-c65535: cint: #8 [0] [50] - 27-c65535: cint: #2 [0] [30] - 26-c65535: stre: [0, 25, 36, 3] [30] - 25-c65535: cint: #1 [0] [26] - 39-c65535: stre: [0, 0, 36, 34] [47] - 45-c65535: &: [0, 43, 44] [46] - 22-c65535: stck: [0, 3] [49, 42, 8, 48] - 44-c65535: cint: #255 [0] [45] - 20-c65535: stre: [0, 17, 42, 14] [47, 47, 43] - 46-c65535: +: [0, 38, 45] [47] - 18-c65535: cint: #3 [0] [42] - 17-c65535: cint: #1 [0] [20] - 14-c65535: stre: [0, 5, 49, 11] [20] - 49-c65535: +: [0, 22, 12] [14] - 12-c65535: cint: #2 [0] [49] - 11-c65535: stre: [0, 7, 48, 8] [14] - 48-c65535: +: [0, 22, 9] [11] - 9-c65535: cint: #1 [0] [48] - 8-c65535: stre: [0, 7, 22, 3] [11] - 7-c65535: cint: #0 [0] [8, 11] - 43-c65535: load: [0, 42, 20] [45] - 5-c65535: cint: #511 [0] [14] - 4-c65535: loops: [0] [] - 3-c65535: mem: [0] [47, 8, 22, 26, 36] - 2-c65535: ctrl: entry [0] [38] -b2: 0 0 [38] - 38-c65535: call: 1 0 [2, 36, 34] [46, 47] - 47-c65535: ret: [38, 46, 3, 20, 20, 34, 39] [1] - diff --git a/lang/README.md b/lang/README.md index e853f92..b5bc9de 100644 --- a/lang/README.md +++ b/lang/README.md @@ -58,6 +58,27 @@ main := fn(): f32 { } ``` +#### advanced_floating_point_arithmetic +```hb + +SIN_TABLE := [f32].(0.0, 0.02454122852291229, 0.04906767432741801, 0.07356456359966743, 0.0980171403295606, 0.1224106751992162, 0.1467304744553617, 0.1709618887603012, 0.1950903220161282, 0.2191012401568698, 0.2429801799032639, 0.2667127574748984, 0.2902846772544623, 0.3136817403988915, 0.3368898533922201, 0.3598950365349881, 0.3826834323650898, 0.4052413140049899, 0.4275550934302821, 0.4496113296546065, 0.4713967368259976, 0.492898192229784, 0.5141027441932217, 0.5349976198870972, 0.5555702330196022, 0.5758081914178453, 0.5956993044924334, 0.6152315905806268, 0.6343932841636455, 0.6531728429537768, 0.6715589548470183, 0.6895405447370668, 0.7071067811865475, 0.7242470829514669, 0.7409511253549591, 0.7572088465064845, 0.773010453362737, 0.7883464276266062, 0.8032075314806448, 0.8175848131515837, 0.8314696123025452, 0.844853565249707, 0.8577286100002721, 0.8700869911087113, 0.8819212643483549, 0.8932243011955153, 0.9039892931234433, 0.9142097557035307, 0.9238795325112867, 0.9329927988347388, 0.9415440651830208, 0.9495281805930367, 0.9569403357322089, 0.9637760657954398, 0.970031253194544, 0.9757021300385286, 0.9807852804032304, 0.9852776423889412, 0.989176509964781, 0.99247953459871, 0.9951847266721968, 0.9972904566786902, 0.9987954562051724, 0.9996988186962042, 1.0, 0.9996988186962042, 0.9987954562051724, 0.9972904566786902, 0.9951847266721969, 0.99247953459871, 0.989176509964781, 0.9852776423889412, 0.9807852804032304, 0.9757021300385286, 0.970031253194544, 0.9637760657954398, 0.9569403357322089, 0.9495281805930367, 0.9415440651830208, 0.9329927988347388, 0.9238795325112867, 0.9142097557035307, 0.9039892931234434, 0.8932243011955152, 0.881921264348355, 0.8700869911087115, 0.8577286100002721, 0.8448535652497072, 0.8314696123025455, 0.8175848131515837, 0.8032075314806449, 0.7883464276266063, 0.7730104533627371, 0.7572088465064847, 0.740951125354959, 0.7242470829514669, 0.7071067811865476, 0.6895405447370671, 0.6715589548470186, 0.6531728429537766, 0.6343932841636455, 0.6152315905806269, 0.5956993044924335, 0.5758081914178454, 0.5555702330196022, 0.5349976198870972, 0.5141027441932218, 0.4928981922297841, 0.4713967368259979, 0.4496113296546069, 0.427555093430282, 0.4052413140049899, 0.3826834323650899, 0.3598950365349883, 0.3368898533922203, 0.3136817403988914, 0.2902846772544624, 0.2667127574748985, 0.2429801799032641, 0.21910124015687, 0.1950903220161286, 0.1709618887603012, 0.1467304744553618, 0.1224106751992163, 0.09801714032956083, 0.07356456359966773, 0.04906767432741797, 0.02454122852291233, 0.0, -0.02454122852291208, -0.04906767432741772, -0.0735645635996675, -0.09801714032956059, -0.1224106751992161, -0.1467304744553616, -0.170961888760301, -0.1950903220161284, -0.2191012401568698, -0.2429801799032638, -0.2667127574748983, -0.2902846772544621, -0.3136817403988912, -0.3368898533922201, -0.3598950365349881, -0.3826834323650897, -0.4052413140049897, -0.4275550934302818, -0.4496113296546067, -0.4713967368259976, -0.4928981922297839, -0.5141027441932216, -0.5349976198870969, -0.555570233019602, -0.5758081914178453, -0.5956993044924332, -0.6152315905806267, -0.6343932841636453, -0.6531728429537765, -0.6715589548470184, -0.6895405447370668, -0.7071067811865475, -0.7242470829514668, -0.7409511253549589, -0.7572088465064842, -0.7730104533627367, -0.7883464276266059, -0.8032075314806451, -0.8175848131515838, -0.8314696123025452, -0.844853565249707, -0.857728610000272, -0.8700869911087113, -0.8819212643483549, -0.8932243011955152, -0.9039892931234431, -0.9142097557035305, -0.9238795325112865, -0.932992798834739, -0.9415440651830208, -0.9495281805930367, -0.9569403357322088, -0.9637760657954398, -0.970031253194544, -0.9757021300385285, -0.9807852804032303, -0.9852776423889411, -0.9891765099647809, -0.9924795345987101, -0.9951847266721969, -0.9972904566786902, -0.9987954562051724, -0.9996988186962042, -1.0, -0.9996988186962042, -0.9987954562051724, -0.9972904566786902, -0.9951847266721969, -0.9924795345987101, -0.9891765099647809, -0.9852776423889412, -0.9807852804032304, -0.9757021300385286, -0.970031253194544, -0.96377606579544, -0.9569403357322089, -0.9495281805930368, -0.9415440651830209, -0.9329927988347391, -0.9238795325112866, -0.9142097557035306, -0.9039892931234433, -0.8932243011955153, -0.881921264348355, -0.8700869911087115, -0.8577286100002722, -0.8448535652497072, -0.8314696123025455, -0.817584813151584, -0.8032075314806453, -0.7883464276266061, -0.7730104533627369, -0.7572088465064846, -0.7409511253549591, -0.724247082951467, -0.7071067811865477, -0.6895405447370672, -0.6715589548470187, -0.6531728429537771, -0.6343932841636459, -0.6152315905806274, -0.5956993044924332, -0.5758081914178452, -0.5555702330196022, -0.5349976198870973, -0.5141027441932219, -0.4928981922297843, -0.4713967368259979, -0.449611329654607, -0.4275550934302825, -0.4052413140049904, -0.3826834323650904, -0.359895036534988, -0.33688985339222, -0.3136817403988915, -0.2902846772544625, -0.2667127574748986, -0.2429801799032642, -0.2191012401568702, -0.1950903220161287, -0.1709618887603018, -0.1467304744553624, -0.122410675199216, -0.09801714032956051, -0.07356456359966741, -0.04906767432741809, -0.02454122852291245) + +sin := fn(theta: f32): f32 { + PI := 3.14159265358979323846 + TABLE_SIZE := @as(i32, 256) + si := @fti(theta * 0.5 * @itf(TABLE_SIZE) / PI) + d := theta - @floatcast(@itf(si)) * 2.0 * PI / @itf(TABLE_SIZE) + ci := si + TABLE_SIZE / 4 & TABLE_SIZE - 1 + si &= TABLE_SIZE - 1 + return SIN_TABLE[@bitcast(si)] + (SIN_TABLE[@bitcast(ci)] - 0.5 * SIN_TABLE[@bitcast(si)] * d) * d +} + +main := fn(): int { + // expected result: 826 + return @fti(sin(1000.0) * 1000.0) +} +``` + #### functions ```hb main := fn(): uint { @@ -178,13 +199,18 @@ main := fn(): uint { if d == null return 69 - f := @as(?Foo, null) + sf := new_foo() + if sf == null return 999 + str := "foo\0" + use_foo(sf, str) + + f := no_foo() if decide() f = .(a, 1) if f == null return 34 - bar := @as(?Bar, .(a, 1)) + bar := new_bar(a) if decide() bar = null @@ -199,8 +225,13 @@ main := fn(): uint { } Foo := struct {a: ^uint, b: uint} +no_foo := fn(): ?Foo return null +new_foo := fn(): ?Foo return .(&0, 0) +use_foo := fn(foo: Foo, str: ^u8): void { +} Bar := struct {a: ?^uint, b: uint} +new_bar := fn(a: ?^uint): ?Bar return .(a, 1) decide := fn(): bool return true ``` @@ -593,6 +624,130 @@ main := fn(): uint { ### Purely Testing Examples +#### nullable_structure +```hb +Structure := struct {} + +BigStructure := struct {a: uint, b: uint} + +MidStructure := struct {a: u8} + +returner_fn := fn(): ?Structure { + return .() +} +returner_bn := fn(): ?BigStructure { + return .(0, 0) +} +returner_cn := fn(): ?MidStructure { + return .(0) +} + +main := fn(): int { + ret := returner_fn() + bet := returner_bn() + cet := returner_cn() + if ret != null & bet != null & cet != null { + return 1 + } + + return 0 +} +``` + +#### needless_unwrap +```hb +main := fn(): uint { + always_nn := @as(?^uint, &0) + ptr := @unwrap(always_nn) + always_n := @as(?^uint, null) + ptr = @unwrap(always_n) + return *ptr +} +``` + +#### optional_from_eca +```hb +main := fn(): uint { + a := @as(?uint, @eca(0, 0, 0, 0)) + + if a == null { + die + } + return a +} +``` + +#### returning_optional_issues +```hb +BMP := 0 + +get_format := fn(): ?uint { + return BMP +} + +main := fn(): uint { + fmt := get_format() + if fmt == null { + return 1 + } else { + return fmt + } +} +``` + +#### inlining_issues +```hb +main := fn(): void { + @use("main.hb").put_filled_rect(.(&.(0), 100, 100), .(0, 0), .(0, 0), .(1)) +} + +// in module: memory.hb + +SetMsg := packed struct {a: u8, count: u32, size: u32, src: ^u8, dest: ^u8} +set := fn($Expr: type, src: ^Expr, dest: ^Expr, count: uint): void { + return @eca(8, 2, &SetMsg.(5, @intcast(count), @intcast(@sizeof(Expr)), @bitcast(src), @bitcast(dest)), @sizeof(SetMsg)) +} + +// in module: main.hb + +Color := struct {r: u8} + +Vec2 := fn($Ty: type): type return struct {x: Ty, y: Ty} + +memory := @use("memory.hb") + +Surface := struct { + buf: ^Color, + width: uint, + height: uint, +} + +indexptr := fn(surface: Surface, x: uint, y: uint): ^Color { + return surface.buf + y * surface.width + x +} + +put_filled_rect := fn(surface: Surface, pos: Vec2(uint), tr: Vec2(uint), color: Color): void { + top_start_idx := @inline(indexptr, surface, pos.x, pos.y) + bottom_start_idx := @inline(indexptr, surface, pos.x, pos.y + tr.y - 1) + rows_to_fill := tr.y + + loop if rows_to_fill <= 1 break else { + @inline(memory.set, Color, &color, top_start_idx, @bitcast(tr.x)) + @inline(memory.set, Color, &color, bottom_start_idx, @bitcast(tr.x)) + + top_start_idx += surface.width + bottom_start_idx -= surface.width + rows_to_fill -= 2 + } + + if rows_to_fill == 1 { + @inline(memory.set, Color, &color, top_start_idx, @bitcast(tr.x)) + } + + return +} +``` + #### only_break_loop ```hb memory := @use("memory.hb") @@ -1008,9 +1163,9 @@ create_back_buffer := fn(total_pages: int): ^u32 { remaining := total_pages - 0xFF loop if remaining <= 0 break else { if remaining < 0xFF { - _f := request_page(@intcast(remaining)) + _ = request_page(@intcast(remaining)) } else { - _f := request_page(0xFF) + _ = request_page(0xFF) } remaining -= 0xFF } @@ -1079,6 +1234,28 @@ main := fn(): void { ### Just Testing Optimizations +#### null_check_test +```hb +get_ptr := fn(): ?^uint { + value := 0 + return &value +} + +main := fn(): uint { + ptr := get_ptr() + + if ptr == null { + return 0 + } + + loop if *ptr != 10 { + *ptr += 1 + } else break + + return *ptr +} +``` + #### const_folding_with_arg ```hb main := fn(arg: uint): uint { @@ -1309,3 +1486,23 @@ opaque := fn(): Foo { return .(3, 2) } ``` + +#### more_if_opts +```hb +main := fn(): uint { + opq1 := opaque() + opq2 := opaque() + a := 0 + + if opq1 == null { + } else a = *opq1 + if opq1 != null a = *opq1 + //if opq1 == null | opq2 == null { + //} else a = *opq1 + //if opq1 != null & opq2 != null a = *opq1 + + return a +} + +opaque := fn(): ?^uint return null +``` diff --git a/lang/src/fmt.rs b/lang/src/fmt.rs index 57a2dcb..773561b 100644 --- a/lang/src/fmt.rs +++ b/lang/src/fmt.rs @@ -346,6 +346,7 @@ impl<'a> Formatter<'a> { self.fmt(val, f) } Expr::Return { val: None, .. } => f.write_str("return"), + Expr::Wildcard { .. } => f.write_str("_"), Expr::Ident { pos, is_ct, .. } => { if is_ct { f.write_str("$")?; diff --git a/lang/src/fs.rs b/lang/src/fs.rs index 6a628f7..da9c3ce 100644 --- a/lang/src/fs.rs +++ b/lang/src/fs.rs @@ -1,7 +1,7 @@ use { crate::{ parser::{self, Ast, Ctx, FileKind}, - son, + son::{self, hbvm::HbvmBackend}, }, alloc::{string::String, vec::Vec}, core::{fmt::Write, num::NonZeroUsize, ops::Deref}, @@ -81,16 +81,27 @@ pub fn run_compiler(root_file: &str, options: Options, out: &mut Vec) -> std } 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)?; } } 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(); } 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(&parsed.ast, &mut ctx); + let mut codegen = son::Codegen::new(&mut backend, &parsed.ast, &mut ctx); codegen.push_embeds(parsed.embeds); codegen.generate(0); @@ -100,12 +111,12 @@ pub fn run_compiler(root_file: &str, options: Options, out: &mut Vec) -> std return Err(std::io::Error::other("compilation faoled")); } + codegen.assemble(out); + if options.dump_asm { - codegen - .disasm(unsafe { std::mem::transmute::<&mut Vec, &mut String>(out) }) - .map_err(|e| io::Error::other(e.to_string()))?; - } else { - codegen.assemble(out); + let mut disasm = String::new(); + codegen.disasm(&mut disasm, out).map_err(|e| io::Error::other(e.to_string()))?; + *out = disasm.into_bytes(); } } diff --git a/lang/src/fuzz.rs b/lang/src/fuzz.rs index 54fe874..dc881e8 100644 --- a/lang/src/fuzz.rs +++ b/lang/src/fuzz.rs @@ -2,7 +2,7 @@ use { crate::{ lexer::TokenKind, parser, - son::{Codegen, CodegenCtx}, + son::{hbvm::HbvmBackend, Codegen, CodegenCtx}, }, alloc::string::String, core::{fmt::Write, hash::BuildHasher, ops::Range}, @@ -133,7 +133,8 @@ pub fn fuzz(seed_range: Range) { assert!(ctx.parser.errors.get_mut().is_empty()); - let mut cdg = Codegen::new(core::slice::from_ref(&parsed), &mut ctx); + let mut backend = HbvmBackend::default(); + let mut cdg = Codegen::new(&mut backend, core::slice::from_ref(&parsed), &mut ctx); cdg.generate(0); } } diff --git a/lang/src/lexer.rs b/lang/src/lexer.rs index 9996357..f68a63e 100644 --- a/lang/src/lexer.rs +++ b/lang/src/lexer.rs @@ -148,6 +148,7 @@ pub enum TokenKind { RBrack = b']', Xor = b'^', Tick = b'`', + Under = b'_', // Unused = a-z LBrace = b'{', Bor = b'|', @@ -308,6 +309,7 @@ gen_token_kind! { Null = b"null", Idk = b"idk", Die = b"die", + Under = b"_", #[punkt] Ctor = ".{", Tupl = ".(", diff --git a/lang/src/lib.rs b/lang/src/lib.rs index 5d03b58..fc69e91 100644 --- a/lang/src/lib.rs +++ b/lang/src/lib.rs @@ -219,26 +219,6 @@ mod ctx_map { } } -mod task { - use super::Offset; - - pub fn unpack(offset: Offset) -> Result { - if offset >> 31 != 0 { - Err((offset & !(1 << 31)) as usize) - } else { - Ok(offset) - } - } - - pub fn is_done(offset: Offset) -> bool { - unpack(offset).is_ok() - } - - pub fn id(index: usize) -> Offset { - 1 << 31 | index as u32 - } -} - #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug)] pub struct Ident(u32); @@ -567,6 +547,7 @@ mod ty { };)* } + #[expect(dead_code)] impl Id { $(pub const $name: Self = Kind::Builtin($name).compress();)* } @@ -758,7 +739,7 @@ pub enum SymKey<'a> { } #[derive(Clone, Copy)] -struct Sig { +pub struct Sig { args: ty::Tuple, ret: ty::Id, } @@ -769,10 +750,7 @@ struct Func { base: Option, expr: ExprRef, sig: Option, - offset: Offset, - // TODO: change to indices into common vec - relocs: Vec, - code: Vec, + comp_state: [CompState; 2], } impl Default for Func { @@ -783,13 +761,19 @@ impl Default for Func { base: None, expr: Default::default(), sig: None, - offset: u32::MAX, - relocs: Default::default(), - code: Default::default(), + comp_state: Default::default(), } } } +#[derive(Default, PartialEq, Eq)] +enum CompState { + #[default] + Dead, + Queued(usize), + Compiled, +} + #[derive(Clone, Copy)] struct TypedReloc { target: ty::Id, @@ -801,7 +785,6 @@ struct Global { file: FileId, name: Ident, ty: ty::Id, - offset: Offset, data: Vec, } @@ -809,7 +792,6 @@ impl Default for Global { fn default() -> Self { Self { ty: Default::default(), - offset: u32::MAX, data: Default::default(), file: u32::MAX, name: Default::default(), @@ -947,9 +929,6 @@ impl IdentInterner { #[derive(Default)] struct TypesTmp { fields: Vec, - frontier: Vec, - globals: Vec, - funcs: Vec, args: Vec, } @@ -968,6 +947,7 @@ pub struct TypeIns { struct FTask { file: FileId, id: ty::Func, + ct: bool, } struct StringRef(ty::Global); @@ -982,7 +962,7 @@ impl ctx_map::CtxEntry for StringRef { } #[derive(Default)] -struct Types { +pub struct Types { syms: ctx_map::CtxMap, names: IdentInterner, strings: ctx_map::CtxMap, @@ -1222,12 +1202,6 @@ impl Types { &self.ins.fields[self.struct_field_range(strct)] } - fn reassemble(&mut self, buf: &mut Vec) { - self.ins.funcs.iter_mut().for_each(|f| f.offset = u32::MAX); - self.ins.globals.iter_mut().for_each(|g| g.offset = u32::MAX); - self.assemble(buf) - } - fn parama(&self, ret: impl Into) -> (Option, ParamAlloc) { let mut iter = ParamAlloc(1..12); let ret = iter.next(ret.into(), self); @@ -1395,9 +1369,6 @@ impl Types { self.ins.slices.clear(); debug_assert_eq!(self.tmp.fields.len(), 0); - debug_assert_eq!(self.tmp.frontier.len(), 0); - debug_assert_eq!(self.tmp.globals.len(), 0); - debug_assert_eq!(self.tmp.funcs.len(), 0); debug_assert_eq!(self.tmp.args.len(), 0); debug_assert_eq!(self.tasks.len(), 0); diff --git a/lang/src/parser.rs b/lang/src/parser.rs index e8c9e57..d9bd81f 100644 --- a/lang/src/parser.rs +++ b/lang/src/parser.rs @@ -392,6 +392,7 @@ impl<'a, 'b> Parser<'a, 'b> { let (id, is_first) = self.resolve_ident(token); E::Ident { pos, is_ct: token.kind == T::CtIdent, id, is_first } } + T::Under => E::Wildcard { pos }, T::If => E::If { pos, cond: self.ptr_expr()?, @@ -800,6 +801,9 @@ generate_expr! { pos: Pos, val: Option<&'a Self>, }, + Wildcard { + pos: Pos, + }, /// note: ':unicode:' is any utf-8 character except ascii /// `'[a-zA-Z_:unicode:][a-zA-Z0-9_:unicode:]*'` Ident { @@ -1163,14 +1167,16 @@ fn report_to(file: &str, path: &str, pos: Pos, msg: &dyn fmt::Display, out: &mut let line = &file[file[..pos as usize].rfind('\n').map_or(0, |i| i + 1) ..file[pos as usize..].find('\n').map_or(file.len(), |i| i + pos as usize)]; - col += line.matches('\t').count() * 3; + col += line.chars().take_while(|c| c.is_whitespace()).filter(|&c| c == '\t').count() * 3; + let mut has_non_whitespace = false; for char in line.chars() { - if char == '\t' { + if char == '\t' && !has_non_whitespace { _ = out.write_str(" "); } else { _ = out.write_char(char); } + has_non_whitespace |= !char.is_whitespace(); } _ = out.write_char('\n'); for _ in 0..col - 1 { diff --git a/lang/src/regalloc.rs b/lang/src/regalloc.rs index ecaccf7..139597f 100644 --- a/lang/src/regalloc.rs +++ b/lang/src/regalloc.rs @@ -1,150 +1,2 @@ -use {crate::reg::Reg, alloc::vec::Vec, core::ops::Range}; -type Nid = u16; -pub trait Ctx { - fn uses_of(&self, nid: Nid) -> impl Iterator; - fn params_of(&self, nid: Nid) -> impl Iterator; - fn args_of(&self, nid: Nid) -> impl Iterator; - fn dom_of(&self, nid: Nid) -> Nid; -} - -pub struct Env<'a, C: Ctx> { - ctx: &'a C, - func: &'a Func, - res: &'a mut Res, -} - -impl<'a, C: Ctx> Env<'a, C> { - pub fn new(ctx: &'a C, func: &'a Func, res: &'a mut Res) -> Self { - Self { ctx, func, res } - } - - pub fn run(&mut self) { - self.res.reg_to_node.clear(); - self.res.reg_to_node.resize(self.func.instrs.len(), 0); - - let mut bundle = Bundle::new(self.func.instrs.len()); - for &inst in &self.func.instrs { - for uinst in self.ctx.uses_of(inst) { - let mut cursor = self.ctx.dom_of(uinst); - while cursor != self.ctx.dom_of(inst) { - let mut range = self.func.blocks - [self.func.id_to_block[cursor as usize] as usize] - .range - .clone(); - range.start = range.start.max(inst as usize); - range.end = range.end.min(uinst as usize); - bundle.add(range); - cursor = self.ctx.dom_of(cursor); - } - } - - match self.res.bundles.iter_mut().enumerate().find(|(_, b)| !b.overlaps(&bundle)) { - Some((i, other)) => { - other.merge(&bundle); - bundle.clear(); - self.res.reg_to_node[inst as usize] = i as Reg; - } - None => { - self.res.reg_to_node[inst as usize] = self.res.bundles.len() as Reg; - self.res.bundles.push(bundle); - bundle = Bundle::new(self.func.instrs.len()); - } - } - } - } -} - -pub struct Res { - bundles: Vec, - pub reg_to_node: Vec, -} - -pub struct Bundle { - //unit_range: Range, - //set: BitSet, - taken: Vec, -} - -impl Bundle { - fn new(size: usize) -> Self { - Self { taken: vec![false; size] } - } - - fn add(&mut self, range: Range) { - 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); - } - - //fn overlaps(&self, other: &Self) -> bool { - // if self.unit_range.start >= other.unit_range.end - // || self.unit_range.end <= other.unit_range.start - // { - // return false; - // } - - // let [mut a, mut b] = [self, other]; - // if a.unit_range.start > b.unit_range.start { - // mem::swap(&mut a, &mut b); - // } - // let [mut tmp_a, mut tmp_b] = [0; 2]; - // let [units_a, units_b] = [a.set.units(&mut tmp_a), b.set.units(&mut tmp_b)]; - // let len = a.unit_range.len().min(b.unit_range.len()); - // let [units_a, units_b] = - // [&units_a[b.unit_range.start - a.unit_range.start..][..len], &units_b[..len]]; - // units_a.iter().zip(units_b).any(|(&a, &b)| a & b != 0) - //} - - //fn merge(mut self, mut other: Self) -> Self { - // debug_assert!(!self.overlaps(&other)); - - // if self.unit_range.start > other.unit_range.start { - // mem::swap(&mut self, &mut other); - // } - - // let final_range = self.unit_range.start..self.unit_range.end.max(other.unit_range.end); - - // self.set.reserve(final_range.len()); - - // let mut tmp = 0; - // let other_units = other.set.units(&mut tmp); - - // match self.set.units_mut() { - // Ok(units) => { - // units[other.unit_range.start - self.unit_range.start..] - // .iter_mut() - // .zip(other_units) - // .for_each(|(a, b)| *a |= b); - // } - // Err(view) => view.add_mask(tmp), - // } - - // self - //} -} - -pub struct Func { - pub blocks: Vec, - pub instrs: Vec, - pub id_to_instr: Vec, - pub id_to_block: Vec, -} - -pub struct Block { - pub range: Range, - pub start_id: Nid, - pub eld_id: Nid, -} diff --git a/lang/src/son.rs b/lang/src/son.rs index 707ae74..b5aab41 100644 --- a/lang/src/son.rs +++ b/lang/src/son.rs @@ -1,5 +1,8 @@ use { - self::{hbvm::Comptime, strong_ref::StrongRef}, + self::{ + hbvm::{Comptime, HbvmBackend}, + strong_ref::StrongRef, + }, crate::{ ctx_map::CtxEntry, debug, @@ -9,16 +12,15 @@ use { idfl::{self}, CtorField, Expr, FileId, Pos, }, - task, ty::{self, Arg, ArrayLen, Loc, Tuple}, utils::{BitSet, Vc}, - FTask, Func, Global, Ident, Offset, OffsetIter, OptLayout, Reloc, Sig, StringRef, SymKey, - TypeParser, TypedReloc, Types, + CompState, FTask, Func, Global, Ident, Offset, OffsetIter, OptLayout, Sig, StringRef, + SymKey, TypeParser, Types, }, alloc::{string::String, vec::Vec}, core::{ assert_matches::debug_assert_matches, - cell::RefCell, + cell::{Cell, RefCell}, fmt::{self, Debug, Display, Write}, format_args as fa, mem, ops::{self, Deref}, @@ -39,7 +41,39 @@ const GLOBAL_ACLASS: usize = 1; pub mod hbvm; type Nid = u16; -type AClassId = u16; +type AClassId = i16; + +pub struct AssemblySpec { + entry: u32, + code_length: u64, + data_length: u64, +} + +pub trait Backend { + fn assemble_reachable( + &mut self, + from: ty::Func, + types: &Types, + to: &mut Vec, + ) -> AssemblySpec; + fn disasm<'a>( + &'a self, + sluce: &[u8], + eca_handler: &mut dyn FnMut(&mut &[u8]), + types: &'a Types, + files: &'a [parser::Ast], + output: &mut String, + ) -> Result<(), hbbytecode::DisasmError<'a>>; + fn emit_body(&mut self, id: ty::Func, ci: &mut Nodes, tys: &Types, files: &[parser::Ast]); + + fn emit_ct_body(&mut self, id: ty::Func, ci: &mut Nodes, tys: &Types, files: &[parser::Ast]) { + self.emit_body(id, ci, tys, files); + } + + fn assemble_bin(&mut self, from: ty::Func, types: &Types, to: &mut Vec) { + self.assemble_reachable(from, types, to); + } +} type Lookup = crate::ctx_map::CtxMap; @@ -71,37 +105,29 @@ macro_rules! inference { } #[derive(Clone)] -struct Nodes { +pub struct Nodes { values: Vec>, - visited: BitSet, free: Nid, lookup: Lookup, - complete: bool, } impl Default for Nodes { fn default() -> Self { - Self { - values: Default::default(), - free: Nid::MAX, - lookup: Default::default(), - visited: Default::default(), - complete: false, - } + Self { values: Default::default(), free: Nid::MAX, lookup: Default::default() } } } impl Nodes { - fn loop_depth(&mut self, target: Nid) -> LoopDepth { - if self[target].loop_depth != 0 { - return self[target].loop_depth; + fn loop_depth(&self, target: Nid) -> LoopDepth { + if self[target].loop_depth.get() != 0 { + return self[target].loop_depth.get(); } - self[target].loop_depth = match self[target].kind { + self[target].loop_depth.set(match self[target].kind { Kind::Entry | Kind::Then | Kind::Else | Kind::Call { .. } | Kind::Return | Kind::If => { let dpth = self.loop_depth(self[target].inputs[0]); - if self[target].loop_depth != 0 { - return self[target].loop_depth; + if self[target].loop_depth.get() != 0 { + return self[target].loop_depth.get(); } dpth } @@ -113,10 +139,10 @@ impl Nodes { } Kind::Loop => { let depth = Self::loop_depth(self, self[target].inputs[0]) + 1; - self[target].loop_depth = depth; + self[target].loop_depth.set(depth); let mut cursor = self[target].inputs[1]; while cursor != target { - self[cursor].loop_depth = depth; + self[cursor].loop_depth.set(depth); let next = if self[cursor].kind == Kind::Region { self.loop_depth(self[cursor].inputs[0]); self[cursor].inputs[1] @@ -130,8 +156,8 @@ impl Nodes { .iter() .find(|&&n| self[n].kind != self[cursor].kind) .unwrap(); - if self[other].loop_depth == 0 { - self[other].loop_depth = depth - 1; + if self[other].loop_depth.get() == 0 { + self[other].loop_depth.set(depth - 1); } } cursor = next; @@ -140,25 +166,26 @@ impl Nodes { } Kind::Start | Kind::End | Kind::Die => 1, u => unreachable!("{u:?}"), - }; + }); - self[target].loop_depth + self[target].loop_depth.get() } - fn idepth(&mut self, target: Nid) -> IDomDepth { + fn idepth(&self, target: Nid) -> IDomDepth { if target == VOID { return 0; } - if self[target].depth == 0 { - self[target].depth = match self[target].kind { + if self[target].depth.get() == 0 { + let depth = match self[target].kind { Kind::End | Kind::Start => unreachable!("{:?}", self[target].kind), Kind::Region => { self.idepth(self[target].inputs[0]).max(self.idepth(self[target].inputs[1])) } _ => self.idepth(self[target].inputs[0]), } + 1; + self[target].depth.set(depth); } - self[target].depth + self[target].depth.get() } fn fix_loops(&mut self) { @@ -182,15 +209,15 @@ impl Nodes { } } - fn push_up_impl(&mut self, node: Nid) { - if !self.visited.set(node) { + fn push_up_impl(&mut self, node: Nid, visited: &mut BitSet) { + if !visited.set(node) { return; } for i in 1..self[node].inputs.len() { let inp = self[node].inputs[i]; if !self[inp].kind.is_pinned() { - self.push_up_impl(inp); + self.push_up_impl(inp, visited); } } @@ -214,8 +241,10 @@ impl Nodes { return; } - let index = self[0].outputs.iter().position(|&p| p == node).unwrap(); - self[0].outputs.remove(index); + let current = self[node].inputs[0]; + + let index = self[current].outputs.iter().position(|&p| p == node).unwrap(); + self[current].outputs.remove(index); self[node].inputs[0] = deepest; debug_assert!( !self[deepest].outputs.contains(&node) @@ -227,32 +256,32 @@ impl Nodes { self[deepest].outputs.push(node); } - fn collect_rpo(&mut self, node: Nid, rpo: &mut Vec) { - if !self.is_cfg(node) || !self.visited.set(node) { + fn collect_rpo(&mut self, node: Nid, rpo: &mut Vec, visited: &mut BitSet) { + if !self.is_cfg(node) || !visited.set(node) { return; } for i in 0..self[node].outputs.len() { - self.collect_rpo(self[node].outputs[i], rpo); + self.collect_rpo(self[node].outputs[i], rpo, visited); } rpo.push(node); } - fn push_up(&mut self, rpo: &mut Vec) { + fn push_up(&mut self, rpo: &mut Vec, visited: &mut BitSet) { debug_assert!(rpo.is_empty()); - self.collect_rpo(VOID, rpo); + self.collect_rpo(VOID, rpo, visited); for &node in rpo.iter().rev() { self.loop_depth(node); for i in 0..self[node].inputs.len() { - self.push_up_impl(self[node].inputs[i]); + self.push_up_impl(self[node].inputs[i], visited); } if matches!(self[node].kind, Kind::Loop | Kind::Region) { for i in 0..self[node].outputs.len() { let usage = self[node].outputs[i]; if self[usage].kind == Kind::Phi { - self.push_up_impl(usage); + self.push_up_impl(usage, visited); } } } @@ -261,13 +290,13 @@ impl Nodes { debug_assert_eq!( self.iter() .map(|(n, _)| n) - .filter(|&n| !self.visited.get(n) + .filter(|&n| !visited.get(n) && !matches!(self[n].kind, Kind::Arg | Kind::Mem | Kind::Loops)) .collect::>(), vec![], "{:?}", self.iter() - .filter(|&(n, nod)| !self.visited.get(n) + .filter(|&(n, nod)| !visited.get(n) && !matches!(nod.kind, Kind::Arg | Kind::Mem | Kind::Loops)) .collect::>() ); @@ -292,20 +321,20 @@ impl Nodes { } } - fn push_down(&mut self, node: Nid) { - if !self.visited.set(node) { + fn push_down(&mut self, node: Nid, visited: &mut BitSet) { + if !visited.set(node) { return; } for usage in self[node].outputs.clone() { if self.is_forward_edge(usage, node) && self[node].kind == Kind::Stre { - self.push_down(usage); + self.push_down(usage, visited); } } for usage in self[node].outputs.clone() { if self.is_forward_edge(usage, node) { - self.push_down(usage); + self.push_down(usage, visited); } } @@ -410,11 +439,12 @@ impl Nodes { } fn bind(&mut self, from: Nid, to: Nid) { + debug_assert_ne!(to, 0); self[from].outputs.push(to); self[to].inputs.push(from); } - fn use_block(&mut self, target: Nid, from: Nid) -> Nid { + fn use_block(&self, target: Nid, from: Nid) -> Nid { if self[from].kind != Kind::Phi { return self.idom(from); } @@ -423,7 +453,7 @@ impl Nodes { self[self[from].inputs[0]].inputs[index - 1] } - fn idom(&mut self, target: Nid) -> Nid { + fn idom(&self, target: Nid) -> Nid { match self[target].kind { Kind::Start => VOID, Kind::End => unreachable!(), @@ -435,7 +465,7 @@ impl Nodes { } } - fn common_dom(&mut self, mut a: Nid, mut b: Nid) -> Nid { + fn common_dom(&self, mut a: Nid, mut b: Nid) -> Nid { while a != b { let [ldepth, rdepth] = [self.idepth(a), self.idepth(b)]; if ldepth >= rdepth { @@ -484,12 +514,7 @@ impl Nodes { } } - fn graphviz_low( - &self, - tys: &Types, - files: &[parser::Ast], - out: &mut String, - ) -> core::fmt::Result { + fn graphviz_low(&self, disp: ty::Display, out: &mut String) -> core::fmt::Result { use core::fmt::Write; writeln!(out)?; @@ -512,17 +537,16 @@ impl Nodes { if node.ty != ty::Id::VOID { writeln!( out, - " node{i}[label=\"{i} {} {} {} {}\" color={color}]", + " node{i}[label=\"{i} {} {} {}\" color={color}]", node.kind, - ty::Display::new(tys, files, node.ty), + disp.rety(node.ty), node.aclass, - node.mem, )?; } else { writeln!( out, - " node{i}[label=\"{i} {} {} {}\" color={color}]", - node.kind, node.aclass, node.mem, + " node{i}[label=\"{i} {} {}\" color={color}]", + node.kind, node.aclass, )?; } @@ -542,17 +566,17 @@ impl Nodes { Ok(()) } - fn graphviz(&self, tys: &Types, files: &[parser::Ast]) { + fn graphviz(&self, disp: ty::Display) { let out = &mut String::new(); - _ = self.graphviz_low(tys, files, out); + _ = self.graphviz_low(disp, out); log::info!("{out}"); } - fn graphviz_in_browser(&self, _tys: &Types, _files: &[parser::Ast]) { + fn graphviz_in_browser(&self, disp: ty::Display) { #[cfg(all(debug_assertions, feature = "std"))] { let out = &mut String::new(); - _ = self.graphviz_low(_tys, _files, out); + _ = self.graphviz_low(disp, out); if !std::process::Command::new("brave") .arg(format!("https://dreampuf.github.io/GraphvizOnline/#{out}")) .status() @@ -564,24 +588,22 @@ impl Nodes { } } - fn gcm(&mut self, rpo: &mut Vec) { + fn gcm(&mut self, rpo: &mut Vec, visited: &mut BitSet) { self.fix_loops(); - self.visited.clear(self.values.len()); - self.push_up(rpo); - self.visited.clear(self.values.len()); - self.push_down(VOID); + visited.clear(self.values.len()); + self.push_up(rpo, visited); + visited.clear(self.values.len()); + self.push_down(VOID, visited); } fn clear(&mut self) { self.values.clear(); self.lookup.clear(); self.free = Nid::MAX; - self.complete = false; } fn new_node_nop(&mut self, ty: ty::Id, kind: Kind, inps: impl Into) -> Nid { - let node = - Node { ralloc_backref: u16::MAX, inputs: inps.into(), kind, ty, ..Default::default() }; + let node = Node { inputs: inps.into(), kind, ty, ..Default::default() }; if node.kind == Kind::Phi && node.ty != ty::Id::VOID { debug_assert_ne!( @@ -735,11 +757,11 @@ impl Nodes { self.push_adjacent_nodes(new, stack); } - debug_assert_matches!( - self.iter() - .find(|(i, n)| n.lock_rc != 0 && n.kind.is_peeped() && !stack.contains(i)), - None - ); + //debug_assert_matches!( + // self.iter() + // .find(|(i, n)| n.lock_rc != 0 && n.kind.is_peeped() && !stack.contains(i)), + // None + //); } stack.drain(..).for_each(|s| _ = self.unlock_remove(s)); @@ -762,8 +784,22 @@ impl Nodes { stack.iter().skip(prev_len).for_each(|&n| self.lock(n)); } - pub fn aclass_index(&self, region: Nid) -> (usize, Nid) { - (self[region].aclass as _, self[region].mem) + fn aclass_index(&self, region: Nid) -> (usize, Nid) { + if self[region].aclass >= 0 { + (self[region].aclass as _, region) + } else { + ( + self[self[region].aclass.unsigned_abs() - 1].aclass as _, + self[region].aclass.unsigned_abs() - 1, + ) + } + } + + fn pass_aclass(&mut self, from: Nid, to: Nid) { + debug_assert!(self[from].aclass >= 0); + if from != to { + self[to].aclass = -(from as AClassId + 1); + } } fn peephole(&mut self, target: Nid) -> Option { @@ -881,39 +917,16 @@ impl Nodes { } K::If => { if self[target].ty == ty::Id::VOID { - let &[ctrl, cond] = self[target].inputs.as_slice() else { unreachable!() }; - if let K::CInt { value } = self[cond].kind { - let ty = if value == 0 { - ty::Id::LEFT_UNREACHABLE - } else { - ty::Id::RIGHT_UNREACHABLE - }; - return Some(self.new_node_nop(ty, K::If, [ctrl, cond])); - } - - 'b: { - let mut cursor = ctrl; - let ty = loop { - if cursor == ENTRY { - break 'b; - } - - // TODO: do more inteligent checks on the condition - if self[cursor].kind == Kind::Then - && self[self[cursor].inputs[0]].inputs[1] == cond - { - break ty::Id::RIGHT_UNREACHABLE; - } - if self[cursor].kind == Kind::Else - && self[self[cursor].inputs[0]].inputs[1] == cond - { - break ty::Id::LEFT_UNREACHABLE; - } - - cursor = self.idom(cursor); - }; - - return Some(self.new_node_nop(ty, K::If, [ctrl, cond])); + match self.try_opt_cond(target) { + CondOptRes::Unknown => {} + CondOptRes::Known { value, .. } => { + let ty = if value { + ty::Id::RIGHT_UNREACHABLE + } else { + ty::Id::LEFT_UNREACHABLE + }; + return Some(self.new_node_nop(ty, K::If, self[target].inputs.clone())); + } } } } @@ -961,11 +974,18 @@ impl Nodes { let mut new_inps = Vc::from(&self[target].inputs[..2]); 'a: for &n in self[target].inputs.clone().iter().skip(2) { - if self[n].kind != Kind::Stre || self[n].inputs.len() != 4 { + if self[n].kind != Kind::Stre { new_inps.push(n); continue; } + if let Some(&load) = + self[n].outputs.iter().find(|&&n| self[n].kind == Kind::Load) + { + self[load].peep_triggers.push(target); + continue; + } + let mut cursor = n; let class = self.aclass_index(self[cursor].inputs[2]); @@ -976,12 +996,19 @@ impl Nodes { cursor = self[cursor].inputs[3]; while cursor != MEM { - if self.aclass_index(self[cursor].inputs[2]) != class - || self[cursor].inputs.len() != 4 - { + debug_assert_eq!(self[cursor].kind, Kind::Stre); + if self.aclass_index(self[cursor].inputs[2]) != class { new_inps.push(n); continue 'a; } + + if let Some(&load) = + self[cursor].outputs.iter().find(|&&n| self[n].kind == Kind::Load) + { + self[load].peep_triggers.push(target); + continue 'a; + } + cursor = self[cursor].inputs[3]; } } @@ -1161,8 +1188,11 @@ impl Nodes { return Some(prev_store); } - if value != VOID - && self[target].inputs.len() == 4 + if let Some(&load) = + self[target].outputs.iter().find(|&&n| self[n].kind == Kind::Load) + { + self[load].peep_triggers.push(target); + } else if value != VOID && self[value].kind != Kind::Load && self[store].kind == Kind::Stre && self[store].inputs[2] == region @@ -1230,6 +1260,58 @@ impl Nodes { None } + fn try_opt_cond(&self, target: Nid) -> CondOptRes { + let &[ctrl, cond, ..] = self[target].inputs.as_slice() else { unreachable!() }; + if let Kind::CInt { value } = self[cond].kind { + return CondOptRes::Known { value: value != 0, pin: None }; + } + + let mut cursor = ctrl; + while cursor != ENTRY { + let ctrl = &self[cursor]; + // TODO: do more inteligent checks on the condition + if matches!(ctrl.kind, Kind::Then | Kind::Else) { + let other_cond = self[ctrl.inputs[0]].inputs[1]; + if let Some(value) = self.matches_cond(cond, other_cond) { + return CondOptRes::Known { + value: (ctrl.kind == Kind::Then) ^ !value, + pin: Some(cursor), + }; + } + } + + cursor = self.idom(cursor); + } + + CondOptRes::Unknown + } + + fn matches_cond(&self, to_match: Nid, matches: Nid) -> Option { + use TokenKind as K; + let [tn, mn] = [&self[to_match], &self[matches]]; + match (tn.kind, mn.kind) { + _ if to_match == matches => Some(true), + (Kind::BinOp { op: K::Ne }, Kind::BinOp { op: K::Eq }) + | (Kind::BinOp { op: K::Eq }, Kind::BinOp { op: K::Ne }) + if tn.inputs[1..] == mn.inputs[1..] => + { + Some(false) + } + (_, Kind::BinOp { op: K::Band }) => self + .matches_cond(to_match, mn.inputs[1]) + .or(self.matches_cond(to_match, mn.inputs[2])), + (_, Kind::BinOp { op: K::Bor }) => match ( + self.matches_cond(to_match, mn.inputs[1]), + self.matches_cond(to_match, mn.inputs[2]), + ) { + (None, Some(a)) | (Some(a), None) => Some(a), + (Some(b), Some(a)) if a == b => Some(a), + _ => None, + }, + _ => None, + } + } + fn is_const(&self, id: Nid) -> bool { matches!(self[id].kind, Kind::CInt { .. }) } @@ -1272,7 +1354,6 @@ impl Nodes { self[prev].outputs.swap_remove(index); self[with].outputs.push(target); self.remove(prev); - target } } @@ -1291,11 +1372,8 @@ impl Nodes { #[expect(clippy::format_in_format_args)] fn basic_blocks_instr(&mut self, out: &mut String, node: Nid) -> core::fmt::Result { - if self[node].kind != Kind::Loop && self[node].kind != Kind::Region { - write!(out, " {node:>2}-c{:>2}: ", self[node].ralloc_backref)?; - } match self[node].kind { - Kind::Start => unreachable!(), + Kind::Assert { .. } | Kind::Start => unreachable!("{} {out}", self[node].kind), Kind::End => return Ok(()), Kind::If => write!(out, " if: "), Kind::Region | Kind::Loop => writeln!(out, " goto: {node}"), @@ -1312,7 +1390,7 @@ impl Nodes { write!(out, "{:>4}: ", op.name()) } Kind::Call { func, args: _ } => { - write!(out, "call: {func} {} ", self[node].depth) + write!(out, "call: {func} {} ", self[node].depth.get()) } Kind::Global { global } => write!(out, "glob: {global:<5}"), Kind::Entry => write!(out, "ctrl: {:<5}", "entry"), @@ -1337,12 +1415,17 @@ impl Nodes { Ok(()) } - fn basic_blocks_low(&mut self, out: &mut String, mut node: Nid) -> core::fmt::Result { + fn basic_blocks_low( + &mut self, + out: &mut String, + mut node: Nid, + visited: &mut BitSet, + ) -> core::fmt::Result { let iter = |nodes: &Nodes, node| nodes[node].outputs.clone().into_iter().rev(); - while self.visited.set(node) { + while visited.set(node) { match self[node].kind { Kind::Start => { - writeln!(out, "start: {}", self[node].depth)?; + writeln!(out, "start: {}", self[node].depth.get())?; let mut cfg_index = Nid::MAX; for o in iter(self, node) { self.basic_blocks_instr(out, o)?; @@ -1354,14 +1437,16 @@ impl Nodes { } Kind::End => break, Kind::If => { - self.basic_blocks_low(out, self[node].outputs[0])?; + self.basic_blocks_low(out, self[node].outputs[0], visited)?; node = self[node].outputs[1]; } Kind::Region => { writeln!( out, "region{node}: {} {} {:?}", - self[node].depth, self[node].loop_depth, self[node].inputs + self[node].depth.get(), + self[node].loop_depth.get(), + self[node].inputs )?; let mut cfg_index = Nid::MAX; for o in iter(self, node) { @@ -1376,7 +1461,9 @@ impl Nodes { writeln!( out, "loop{node}: {} {} {:?}", - self[node].depth, self[node].loop_depth, self[node].outputs + self[node].depth.get(), + self[node].loop_depth.get(), + self[node].outputs )?; let mut cfg_index = Nid::MAX; for o in iter(self, node) { @@ -1394,7 +1481,9 @@ impl Nodes { writeln!( out, "b{node}: {} {} {:?}", - self[node].depth, self[node].loop_depth, self[node].outputs + self[node].depth.get(), + self[node].loop_depth.get(), + self[node].outputs )?; let mut cfg_index = Nid::MAX; for o in iter(self, node) { @@ -1429,8 +1518,8 @@ impl Nodes { fn basic_blocks(&mut self) { let mut out = String::new(); - self.visited.clear(self.values.len()); - self.basic_blocks_low(&mut out, VOID).unwrap(); + let mut visited = BitSet::default(); + self.basic_blocks_low(&mut out, VOID, &mut visited).unwrap(); log::info!("{out}"); } @@ -1438,7 +1527,7 @@ impl Nodes { self[o].kind.is_cfg() } - fn check_final_integrity(&self, tys: &Types, files: &[parser::Ast]) { + fn check_final_integrity(&self, disp: ty::Display) { if !cfg!(debug_assertions) { return; } @@ -1459,10 +1548,14 @@ impl Nodes { log::error!("is unreachable but still present {id} {:?}", node.kind); failed = true; } + if node.outputs.contains(&id) && !matches!(node.kind, Kind::Loop | Kind::End) { + log::error!("node depends on it self and its not a loop {id} {:?}", node); + failed = true; + } } if failed { - self.graphviz_in_browser(tys, files); + self.graphviz_in_browser(disp); panic!() } } @@ -1482,8 +1575,8 @@ impl Nodes { let lvalue = lvar.value(); let inps = [node, lvalue, VOID]; lvar.set_value(self.new_node_nop(lvar.ty, Kind::Phi, inps), self); - self[lvar.value()].aclass = self[lvalue].aclass; - self[lvar.value()].mem = self[lvalue].mem; + + self.pass_aclass(self.aclass_index(lvalue).1, lvar.value()); } var.set_value(lvar.value(), self); } @@ -1545,6 +1638,20 @@ impl Nodes { dominated = self.idom(dominated); } } + + fn is_data_dep(&self, nid: Nid, n: Nid) -> bool { + match self[n].kind { + _ if self.is_cfg(n) && !matches!(self[n].kind, Kind::Call { .. }) => false, + Kind::Stre => self[n].inputs[3] != nid, + Kind::Load => self[n].inputs[2] != nid, + _ => self[n].inputs[0] != nid || self[n].inputs[1..].contains(&nid), + } + } +} + +enum CondOptRes { + Unknown, + Known { value: bool, pin: Option }, } impl ops::Index for Nodes { @@ -1561,6 +1668,12 @@ impl ops::IndexMut for Nodes { } } +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub enum AssertKind { + NullCheck, + UnwrapCheck, +} + #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Default)] #[repr(u8)] pub enum Kind { @@ -1610,6 +1723,11 @@ pub enum Kind { func: ty::Func, args: ty::Tuple, }, + // [ctrl, cond, value] + Assert { + kind: AssertKind, + pos: Pos, + }, // [ctrl] Stck, // [ctrl, memory] @@ -1634,6 +1752,7 @@ impl Kind { | Self::Then | Self::Else | Self::Call { .. } + | Self::Assert { .. } | Self::If | Self::Region | Self::Loop @@ -1644,6 +1763,10 @@ impl Kind { matches!(self, Self::Return | Self::If | Self::End | Self::Die) } + fn starts_basic_block(&self) -> bool { + matches!(self, Self::Region | Self::Loop | Self::Start | Kind::Then | Kind::Else) + } + fn is_peeped(&self) -> bool { !matches!(self, Self::End | Self::Arg | Self::Mem | Self::Loops) } @@ -1671,13 +1794,10 @@ pub struct Node { peep_triggers: Vc, clobbers: BitSet, ty: ty::Id, - offset: Offset, - ralloc_backref: RallocBRef, - depth: IDomDepth, + depth: Cell, lock_rc: LockRc, - loop_depth: LoopDepth, + loop_depth: Cell, aclass: AClassId, - mem: Nid, antidep: Nid, } @@ -1703,9 +1823,12 @@ impl Node { fn is_mem(&self) -> bool { matches!(self.kind, Kind::Stre | Kind::Load | Kind::Stck) } + + fn is_data_phi(&self) -> bool { + self.kind == Kind::Phi && self.ty != ty::Id::VOID + } } -type RallocBRef = u16; type LoopDepth = u16; type LockRc = u16; type IDomDepth = u16; @@ -1892,7 +2015,7 @@ impl Scope { } #[derive(Default, Clone)] -struct ItemCtx { +pub struct ItemCtx { file: FileId, ret: Option, task_base: usize, @@ -1902,25 +2025,19 @@ struct ItemCtx { inline_ret: Option<(Value, StrongRef, Scope)>, nodes: Nodes, ctrl: StrongRef, - call_count: u16, loops: Vec, scope: Scope, - ret_relocs: Vec, - relocs: Vec, - jump_relocs: Vec<(Nid, Reloc)>, - code: Vec, } impl ItemCtx { fn init(&mut self, file: FileId, ret: Option, task_base: usize) { debug_assert_eq!(self.loops.len(), 0); debug_assert_eq!(self.scope.vars.len(), 0); - debug_assert_eq!(self.ret_relocs.len(), 0); - debug_assert_eq!(self.relocs.len(), 0); - debug_assert_eq!(self.jump_relocs.len(), 0); - debug_assert_eq!(self.code.len(), 0); - - self.call_count = 0; + debug_assert_eq!(self.scope.aclasses.len(), 0); + debug_assert!(self.inline_ret.is_none()); + debug_assert_eq!(self.inline_depth, 0); + debug_assert_eq!(self.inline_var_base, 0); + debug_assert_eq!(self.inline_aclass_base, 0); self.file = file; self.ret = ret; @@ -1952,7 +2069,9 @@ impl ItemCtx { mem::take(&mut self.ctrl).soft_remove(&mut self.nodes); self.nodes.iter_peeps(1000, stack); + } + fn unlock(&mut self) { self.nodes.unlock(MEM); self.nodes.unlock(NEVER); self.nodes.unlock(LOOPS); @@ -1970,21 +2089,21 @@ struct Ctx { } impl Ctx { - pub fn with_ty(self, ty: ty::Id) -> Self { + fn with_ty(self, ty: ty::Id) -> Self { Self { ty: Some(ty) } } } #[derive(Default)] -struct Pool { +pub struct Pool { cis: Vec, used_cis: usize, - ralloc: Regalloc, nid_stack: Vec, + nid_set: BitSet, } impl Pool { - pub fn push_ci( + fn push_ci( &mut self, file: FileId, ret: Option, @@ -2001,7 +2120,7 @@ impl Pool { self.used_cis += 1; } - pub fn pop_ci(&mut self, target: &mut ItemCtx) { + fn pop_ci(&mut self, target: &mut ItemCtx) { self.used_cis -= 1; mem::swap(&mut self.cis[self.used_cis], target); } @@ -2027,33 +2146,6 @@ impl Pool { } } -struct Regalloc { - env: regalloc2::MachineEnv, - ctx: regalloc2::Ctx, -} - -impl Default for Regalloc { - fn default() -> Self { - Self { - env: regalloc2::MachineEnv { - preferred_regs_by_class: [ - (1..13).map(|i| regalloc2::PReg::new(i, regalloc2::RegClass::Int)).collect(), - vec![], - vec![], - ], - non_preferred_regs_by_class: [ - (13..64).map(|i| regalloc2::PReg::new(i, regalloc2::RegClass::Int)).collect(), - vec![], - vec![], - ], - scratch_by_class: Default::default(), - fixed_stack_slots: Default::default(), - }, - ctx: Default::default(), - } - } -} - #[derive(Default, Clone, Copy, PartialEq, Eq, Debug)] struct Value { ty: ty::Id, @@ -2067,20 +2159,20 @@ impl Value { Some(Self { ty: ty::Id::NEVER, var: false, ptr: false, id: NEVER }); const VOID: Value = Self { ty: ty::Id::VOID, var: false, ptr: false, id: VOID }; - pub fn new(id: Nid) -> Self { + fn new(id: Nid) -> Self { Self { id, ..Default::default() } } - pub fn var(id: usize) -> Self { + fn var(id: usize) -> Self { Self { id: u16::MAX - (id as Nid), var: true, ..Default::default() } } - pub fn ptr(id: Nid) -> Self { + fn ptr(id: Nid) -> Self { Self { id, ptr: true, ..Default::default() } } #[inline(always)] - pub fn ty(self, ty: impl Into) -> Self { + fn ty(self, ty: impl Into) -> Self { Self { ty: ty.into(), ..self } } } @@ -2091,6 +2183,7 @@ pub struct CodegenCtx { tys: Types, pool: Pool, ct: Comptime, + ct_backend: HbvmBackend, } impl CodegenCtx { @@ -2127,10 +2220,16 @@ pub struct Codegen<'a> { ci: ItemCtx, pool: &'a mut Pool, ct: &'a mut Comptime, + ct_backend: &'a mut HbvmBackend, + backend: &'a mut dyn Backend, } impl<'a> Codegen<'a> { - pub fn new(files: &'a [parser::Ast], ctx: &'a mut CodegenCtx) -> Self { + pub fn new( + backend: &'a mut dyn Backend, + files: &'a [parser::Ast], + ctx: &'a mut CodegenCtx, + ) -> Self { Self { files, errors: Errors(&ctx.parser.errors), @@ -2138,9 +2237,46 @@ impl<'a> Codegen<'a> { ci: Default::default(), pool: &mut ctx.pool, ct: &mut ctx.ct, + ct_backend: &mut ctx.ct_backend, + backend, } } + pub fn generate(&mut self, entry: FileId) { + self.find_type(0, entry, entry, Err("main"), self.files); + if self.tys.ins.funcs.is_empty() { + return; + } + self.make_func_reachable(0); + self.complete_call_graph(); + } + + pub fn assemble_comptime(&mut self) -> Comptime { + self.ct.code.clear(); + self.ct_backend.assemble_bin(0, self.tys, &mut self.ct.code); + self.ct.reset(); + core::mem::take(self.ct) + } + + pub fn assemble(&mut self, buf: &mut Vec) { + self.backend.assemble_bin(0, self.tys, buf); + } + + pub fn disasm(&mut self, output: &mut String, bin: &[u8]) -> Result<(), DisasmError> { + self.backend.disasm(bin, &mut |_| {}, self.tys, self.files, output) + } + + pub fn push_embeds(&mut self, embeds: Vec>) { + self.tys.ins.globals = embeds + .into_iter() + .map(|data| Global { + ty: self.tys.make_array(ty::Id::U8, data.len() as _), + data, + ..Default::default() + }) + .collect(); + } + fn emit_and_eval(&mut self, file: FileId, ret: ty::Id, ret_loc: &mut [u8]) -> u64 { let mut rets = self.ci.nodes[NEVER].inputs.iter().filter(|&&i| self.ci.nodes[i].kind == Kind::Return); @@ -2158,52 +2294,37 @@ impl<'a> Codegen<'a> { return 1; } - self.ci.emit_ct_body(self.tys, self.files, Sig { args: Tuple::empty(), ret }, self.pool); - - let func = Func { + let fuc = self.tys.ins.funcs.len() as ty::Func; + self.tys.ins.funcs.push(Func { file, - relocs: mem::take(&mut self.ci.relocs), - code: mem::take(&mut self.ci.code), + sig: Some(Sig { args: Tuple::empty(), ret }), ..Default::default() - }; + }); + + self.ct_backend.emit_ct_body(fuc, &mut self.ci.nodes, self.tys, self.files); // TODO: return them back - let fuc = self.tys.ins.funcs.len() as ty::Func; - self.tys.ins.funcs.push(func); - self.tys.dump_reachable(fuc, &mut self.ct.code); - self.dump_ct_asm(); + let entry = self.ct_backend.assemble_reachable(fuc, self.tys, &mut self.ct.code).entry; - self.ct.run(ret_loc, self.tys.ins.funcs[fuc as usize].offset) - } - - fn dump_ct_asm(&self) { #[cfg(debug_assertions)] { let mut vc = String::new(); - if let Err(e) = self.tys.disasm(&self.ct.code, self.files, &mut vc, |_| {}) { + if let Err(e) = + self.ct_backend.disasm(&self.ct.code, &mut |_| {}, self.tys, self.files, &mut vc) + { panic!("{e} {}", vc); } else { log::trace!("{}", vc); } } - } - pub fn push_embeds(&mut self, embeds: Vec>) { - self.tys.ins.globals = embeds - .into_iter() - .map(|data| Global { - ty: self.tys.make_array(ty::Id::U8, data.len() as _), - data, - ..Default::default() - }) - .collect(); + self.ct.run(ret_loc, entry) } fn new_stack(&mut self, ty: ty::Id) -> Nid { let stck = self.ci.nodes.new_node_nop(ty, Kind::Stck, [VOID, MEM]); self.ci.nodes[stck].aclass = self.ci.scope.aclasses.len() as _; - self.ci.nodes[stck].mem = stck; self.ci.scope.aclasses.push(AClass::new(&mut self.ci.nodes)); stck } @@ -2220,25 +2341,46 @@ impl<'a> Codegen<'a> { let (value_index, value_region) = self.ci.nodes.aclass_index(value); if value_index != 0 { - // simply switch the class to the default one - let aclass = &mut self.ci.scope.aclasses[value_index]; - self.ci.nodes.load_loop_aclass(value_index, aclass, &mut self.ci.loops); - let last_store = aclass.last_store.get(); - let mut cursor = last_store; - let mut first_store = cursor; - while cursor != MEM { - first_store = cursor; - cursor = self.ci.nodes[cursor].inputs[3]; - } + self.ci.nodes[value_region].aclass = 0; + //// simply switch the class to the default one + //let aclass = &mut self.ci.scope.aclasses[value_index]; + //self.ci.nodes.load_loop_aclass(value_index, aclass, &mut self.ci.loops); + //let last_store = aclass.last_store.get(); + //let mut cursor = last_store; + //let mut first_store = cursor; + //while cursor != MEM { + // first_store = cursor; + // debug_assert_matches!( + // self.ci.nodes[cursor].kind, + // Kind::Stre, + // "{:?}", + // self.ci.nodes[cursor] + // ); + // cursor = self.ci.nodes[cursor].inputs[3]; + //} + //if last_store != MEM { + // let base_class = self.ci.scope.aclasses[0].last_store.get(); + // if base_class != MEM { + // self.ci.nodes.modify_input(first_store, 3, base_class); + // } + // self.ci.scope.aclasses[0].last_store.set(last_store, &mut self.ci.nodes); + //} + + self.ci.nodes.load_loop_aclass(0, &mut self.ci.scope.aclasses[0], &mut self.ci.loops); + self.ci.nodes.load_loop_aclass( + value_index, + &mut self.ci.scope.aclasses[value_index], + &mut self.ci.loops, + ); + let base_class = self.ci.scope.aclasses[0].last_store.get(); + let last_store = self.ci.scope.aclasses[value_index].last_store.get(); + if base_class != MEM && last_store != MEM { + self.ci.nodes.bind(base_class, last_store); + } if last_store != MEM { - let base_class = self.ci.scope.aclasses[0].last_store.get(); - if base_class != MEM { - self.ci.nodes.modify_input(first_store, 3, base_class); - } self.ci.scope.aclasses[0].last_store.set(last_store, &mut self.ci.nodes); } - self.ci.nodes[value_region].aclass = 0; } let (index, _) = self.ci.nodes.aclass_index(region); @@ -2255,12 +2397,12 @@ impl<'a> Codegen<'a> { debug_assert_ne!(region, VOID); debug_assert_ne!({ self.ci.nodes[region].ty }, ty::Id::VOID, "{:?}", { self.ci.nodes[region].lock_rc = Nid::MAX; - self.ci.nodes.graphviz_in_browser(self.tys, self.files); + self.ci.nodes.graphviz_in_browser(self.ty_display(ty::Id::VOID)); }); debug_assert!( self.ci.nodes[region].kind != Kind::Load || self.ci.nodes[region].ty.is_pointer(), "{:?} {} {}", - self.ci.nodes.graphviz_in_browser(self.tys, self.files), + self.ci.nodes.graphviz_in_browser(self.ty_display(ty::Id::VOID)), self.file().path, self.ty_display(self.ci.nodes[region].ty) ); @@ -2272,37 +2414,12 @@ impl<'a> Codegen<'a> { self.ci.nodes.new_node(ty, Kind::Load, vc) } - pub fn generate(&mut self, entry: FileId) { - self.find_type(0, entry, entry, Err("main"), self.files); - if self.tys.ins.funcs.is_empty() { - return; - } - self.make_func_reachable(0); - self.complete_call_graph(); - } - - pub fn assemble_comptime(&mut self) -> Comptime { - self.ct.code.clear(); - self.tys.reassemble(&mut self.ct.code); - self.ct.reset(); - core::mem::take(self.ct) - } - - pub fn assemble(&mut self, buf: &mut Vec) { - self.tys.reassemble(buf); - } - - pub fn disasm(&mut self, output: &mut String) -> Result<(), DisasmError> { - let mut bin = Vec::new(); - self.assemble(&mut bin); - self.tys.disasm(&bin, self.files, output, |_| {}) - } - fn make_func_reachable(&mut self, func: ty::Func) { + let state_slot = self.ct.active() as usize; let fuc = &mut self.tys.ins.funcs[func as usize]; - if fuc.offset == u32::MAX { - fuc.offset = task::id(self.tys.tasks.len() as _); - self.tys.tasks.push(Some(FTask { file: fuc.file, id: func })); + if fuc.comp_state[state_slot] == CompState::Dead { + fuc.comp_state[state_slot] = CompState::Queued(self.tys.tasks.len() as _); + self.tys.tasks.push(Some(FTask { file: fuc.file, id: func, ct: self.ct.active() })); } } @@ -2343,18 +2460,10 @@ impl<'a> Codegen<'a> { Expr::Idk { pos } => { inference!(ty, ctx, self, pos, "value", "@as(, idk)"); - if matches!(ty.expand(), ty::Kind::Struct(_) | ty::Kind::Slice(_)) { + if ty.loc(self.tys) == Loc::Stack { Some(Value::ptr(self.new_stack(ty)).ty(ty)) } else { - self.report( - pos, - fa!( - "type '{}' cannot be uninitialized, use a zero \ - value instead ('null' in case of pointers)", - self.ty_display(ty) - ), - ); - Value::NEVER + Some(self.ci.nodes.new_const_lit(ty, 0)) } } Expr::Bool { value, .. } => Some(self.ci.nodes.new_const_lit(ty::Id::BOOL, value)), @@ -2429,20 +2538,24 @@ impl<'a> Codegen<'a> { } Expr::Return { pos, val } => { let mut value = if let Some(val) = val { - self.expr_ctx(val, Ctx { ty: self.ci.ret })? + self.raw_expr_ctx(val, Ctx { ty: self.ci.ret })? } else { Value { ty: ty::Id::VOID, ..Default::default() } }; + self.strip_var(&mut value); let expected = *self.ci.ret.get_or_insert(value.ty); self.assert_ty(pos, &mut value, expected, "return value"); + self.strip_ptr(&mut value); if self.ci.inline_depth == 0 { debug_assert_ne!(self.ci.ctrl.get(), VOID); let mut inps = Vc::from([self.ci.ctrl.get(), value.id]); for (i, aclass) in self.ci.scope.aclasses.iter_mut().enumerate() { self.ci.nodes.load_loop_aclass(i, aclass, &mut self.ci.loops); - inps.push(aclass.last_store.get()); + if aclass.last_store.get() != MEM { + inps.push(aclass.last_store.get()); + } } self.ci.ctrl.set( @@ -2501,7 +2614,7 @@ impl<'a> Codegen<'a> { Expr::Field { target, name, pos } => { let mut vtarget = self.raw_expr(target)?; self.strip_var(&mut vtarget); - self.unwrap_opt(pos, &mut vtarget); + self.implicit_unwrap(pos, &mut vtarget); let tty = vtarget.ty; if let ty::Kind::Module(m) = tty.expand() { @@ -2575,7 +2688,7 @@ impl<'a> Codegen<'a> { let ctx = Ctx { ty: ctx.ty.map(|ty| self.tys.make_ptr(ty)) }; let mut vl = self.expr_ctx(val, ctx)?; - self.unwrap_opt(val.pos(), &mut vl); + self.implicit_unwrap(val.pos(), &mut vl); let Some(base) = self.tys.base_of(vl.ty) else { self.report( @@ -2615,6 +2728,10 @@ impl<'a> Codegen<'a> { self.assign_pattern(left, right); Some(Value::VOID) } + Expr::BinOp { left: Expr::Wildcard { .. }, op: TokenKind::Assign, right, .. } => { + self.expr(right)?; + Some(Value::VOID) + } Expr::BinOp { left, pos, op: TokenKind::Assign, right } => { let dest = self.raw_expr(left)?; let mut value = self.expr_ctx(right, Ctx::default().with_ty(dest.ty))?; @@ -2653,7 +2770,11 @@ impl<'a> Codegen<'a> { self.strip_var(&mut cmped); let Some(ty) = self.tys.inner_of(cmped.ty) else { - return Some(self.ci.nodes.new_const_lit(ty::Id::BOOL, 1)); + self.report( + left.pos(), + fa!("'{}' is never null, remove this check", self.ty_display(cmped.ty)), + ); + return Value::NEVER; }; Some(Value::new(self.gen_null_check(cmped, ty, op)).ty(ty::BOOL)) @@ -2663,7 +2784,7 @@ impl<'a> Codegen<'a> { { let mut lhs = self.raw_expr_ctx(left, ctx)?; self.strip_var(&mut lhs); - self.unwrap_opt(left.pos(), &mut lhs); + self.implicit_unwrap(left.pos(), &mut lhs); match lhs.ty.expand() { _ if lhs.ty.is_pointer() @@ -2677,13 +2798,12 @@ impl<'a> Codegen<'a> { self.ci.nodes.unlock(lhs.id); let mut rhs = rhs?; self.strip_var(&mut rhs); - self.unwrap_opt(right.pos(), &mut rhs); - let (ty, aclass, mem) = self.binop_ty(pos, &mut lhs, &mut rhs, op); + self.implicit_unwrap(right.pos(), &mut rhs); + let (ty, aclass) = self.binop_ty(pos, &mut lhs, &mut rhs, op); let inps = [VOID, lhs.id, rhs.id]; let bop = self.ci.nodes.new_node_lit(ty.bin_ret(op), Kind::BinOp { op }, inps); - self.ci.nodes[bop.id].aclass = aclass as _; - self.ci.nodes[bop.id].mem = mem; + self.ci.nodes.pass_aclass(aclass, bop.id); Some(bop) } ty::Kind::Struct(s) if op.is_homogenous() => { @@ -2733,12 +2853,11 @@ impl<'a> Codegen<'a> { let inps = [VOID, idx.id, size]; let offset = self.ci.nodes.new_node(ty::Id::INT, Kind::BinOp { op: TokenKind::Mul }, inps); - let (aclass, mem) = self.ci.nodes.aclass_index(bs.id); + let aclass = self.ci.nodes.aclass_index(bs.id).1; let inps = [VOID, bs.id, offset]; let ptr = self.ci.nodes.new_node(ty::Id::INT, Kind::BinOp { op: TokenKind::Add }, inps); - self.ci.nodes[ptr].aclass = aclass as _; - self.ci.nodes[ptr].mem = mem; + self.ci.nodes.pass_aclass(aclass, ptr); Some(Value::ptr(ptr).ty(elem)) } @@ -2808,7 +2927,7 @@ impl<'a> Codegen<'a> { let mut val = self.raw_expr(expr)?; self.strip_var(&mut val); - let Some(ty) = self.tys.inner_of(val.ty) else { + if !val.ty.is_optional() { self.report( expr.pos(), fa!( @@ -2819,8 +2938,7 @@ impl<'a> Codegen<'a> { return Value::NEVER; }; - self.unwrap_opt_unchecked(ty, val.ty, &mut val); - val.ty = ty; + self.explicit_unwrap(expr.pos(), &mut val); Some(val) } Expr::Directive { name: "intcast", args: [expr], pos } => { @@ -2882,7 +3000,7 @@ impl<'a> Codegen<'a> { ); } - if self.tys.size_of(val.ty) < self.tys.size_of(ty) { + if self.tys.size_of(val.ty) != self.tys.size_of(ty) { Some( self.ci .nodes @@ -2896,8 +3014,7 @@ impl<'a> Codegen<'a> { let val = self.expr(expr)?; let ret_ty = match val.ty { - ty::Id::F64 => ty::Id::INT, - ty::Id::F32 => ty::Id::I32, + ty::Id::F32 | ty::Id::F64 => ty::Id::INT, _ => { self.report( expr.pos(), @@ -2918,7 +3035,7 @@ impl<'a> Codegen<'a> { let (ret_ty, expected) = match val.ty.simple_size().unwrap() { 8 => (ty::Id::F64, ty::Id::INT), - _ => (ty::Id::F32, ty::Id::I32), + _ => (ty::Id::F32, ty::Id::INT), }; self.assert_ty(expr.pos(), &mut val, expected, "converted integer"); @@ -2980,7 +3097,6 @@ impl<'a> Codegen<'a> { alt_value.or(Some(Value::new(self.ci.ctrl.get()).ty(ty))) } Expr::Call { func, args, .. } => { - self.ci.call_count += 1; let ty = self.ty(func); let ty::Kind::Func(mut fu) = ty.expand() else { self.report( @@ -3021,9 +3137,11 @@ impl<'a> Codegen<'a> { let Some(arg) = args.next() else { break }; let Arg::Value(ty) = ty else { continue }; - let mut value = self.expr_ctx(arg, Ctx::default().with_ty(ty))?; + let mut value = self.raw_expr_ctx(arg, Ctx::default().with_ty(ty))?; + self.strip_var(&mut value); debug_assert_ne!(self.ci.nodes[value.id].kind, Kind::Stre); self.assert_ty(arg.pos(), &mut value, ty, fa!("argument {}", carg.name)); + self.strip_ptr(&mut value); self.add_clobbers(value, &mut clobbered_aliases); self.ci.nodes.lock(value.id); @@ -3845,11 +3963,10 @@ impl<'a> Codegen<'a> { } let off = self.ci.nodes.new_const(ty::Id::INT, off); - let (aclass, mem) = self.ci.nodes.aclass_index(val); + let aclass = self.ci.nodes.aclass_index(val).1; let inps = [VOID, val, off]; let seted = self.ci.nodes.new_node(ty::Id::INT, Kind::BinOp { op: TokenKind::Add }, inps); - self.ci.nodes[seted].aclass = aclass as _; - self.ci.nodes[seted].mem = mem; + self.ci.nodes.pass_aclass(aclass, seted); seted } @@ -3912,10 +4029,12 @@ impl<'a> Codegen<'a> { self.errors.borrow().len() == prev_err_len } - fn emit_func(&mut self, FTask { file, id }: FTask) { + fn emit_func(&mut self, FTask { file, id, ct }: FTask) { let func = &mut self.tys.ins.funcs[id as usize]; debug_assert_eq!(func.file, file); - func.offset = u32::MAX - 1; + let cct = self.ct.active(); + debug_assert_eq!(cct, ct); + func.comp_state[cct as usize] = CompState::Compiled; let sig = func.sig.expect("to emmit only concrete functions"); let ast = &self.files[file as usize]; let expr = func.expr.get(ast); @@ -3981,17 +4100,101 @@ impl<'a> Codegen<'a> { self.ci.scope.vars.drain(..).for_each(|v| v.remove_ignore_arg(&mut self.ci.nodes)); - self.ci.finalize(&mut self.pool.nid_stack, self.tys, self.files); - - if self.errors.borrow().len() == prev_err_len { - self.ci.emit_body(self.tys, self.files, sig, self.pool); - self.tys.ins.funcs[id as usize].code.append(&mut self.ci.code); - self.tys.ins.funcs[id as usize].relocs.append(&mut self.ci.relocs); + if self.finalize(prev_err_len) { + let backend = if !cct { &mut *self.backend } else { &mut *self.ct_backend }; + backend.emit_body(id, &mut self.ci.nodes, self.tys, self.files); } self.pool.pop_ci(&mut self.ci); } + fn finalize(&mut self, prev_err_len: usize) -> bool { + use {AssertKind as AK, CondOptRes as CR}; + + self.ci.finalize(&mut self.pool.nid_stack, self.tys, self.files); + + let mut to_remove = vec![]; + for (id, node) in self.ci.nodes.iter() { + let Kind::Assert { kind, pos } = node.kind else { continue }; + + let res = self.ci.nodes.try_opt_cond(id); + + // TODO: highlight the pin position + let msg = match (kind, res) { + (AK::UnwrapCheck, CR::Known { value: false, .. }) => { + "unwrap is not needed since the value is (provably) never null, \ + remove it, or replace with '@as(, )'" + } + (AK::UnwrapCheck, CR::Known { value: true, .. }) => { + "unwrap is incorrect since the value is (provably) always null, \ + make sure your logic is correct" + } + (AK::NullCheck, CR::Known { value: true, .. }) => { + "the value is always null, some checks might need to be inverted" + } + (AK::NullCheck, CR::Unknown) => { + "can't prove the value is not 'null', \ + use '@unwrap()' if you believe compiler is stupid, \ + or explicitly check for null and handle it \ + ('if == null { /* handle */ } else { /* use opt */ }')" + } + (AK::NullCheck, CR::Known { value: false, pin }) => { + to_remove.push((id, pin)); + continue; + } + (AK::UnwrapCheck, CR::Unknown) => { + to_remove.push((id, None)); + continue; + } + }; + self.report(pos, msg); + } + to_remove.into_iter().for_each(|(n, pin)| { + let pin = pin.unwrap_or_else(|| { + let mut pin = self.ci.nodes[n].inputs[0]; + while matches!(self.ci.nodes[pin].kind, Kind::Assert { .. }) { + pin = self.ci.nodes[n].inputs[0]; + } + pin + }); + for mut out in self.ci.nodes[n].outputs.clone() { + if self.ci.nodes.is_cfg(out) { + let index = self.ci.nodes[out].inputs.iter().position(|&p| p == n).unwrap(); + self.ci.nodes.modify_input(out, index, self.ci.nodes[n].inputs[0]); + } else { + if !self.ci.nodes[out].kind.is_pinned() { + out = self.ci.nodes.modify_input(out, 0, pin); + } + let index = + self.ci.nodes[out].inputs[1..].iter().position(|&p| p == n).unwrap() + 1; + self.ci.nodes.modify_input(out, index, self.ci.nodes[n].inputs[2]); + } + } + debug_assert!( + self.ci.nodes.values[n as usize] + .as_ref() + .map_or(true, |n| !matches!(n.kind, Kind::Assert { .. })), + "{:?} {:?}", + self.ci.nodes[n], + self.ci.nodes[n].outputs.iter().map(|&o| &self.ci.nodes[o]).collect::>(), + ); + }); + + self.ci.unlock(); + + if self.errors.borrow().len() == prev_err_len { + self.ci.nodes.check_final_integrity(self.ty_display(ty::Id::VOID)); + self.ci.nodes.graphviz(self.ty_display(ty::Id::VOID)); + self.ci.nodes.gcm(&mut self.pool.nid_stack, &mut self.pool.nid_set); + self.ci.nodes.basic_blocks(); + self.ci.nodes.graphviz(self.ty_display(ty::Id::VOID)); + } else { + self.ci.nodes.graphviz_in_browser(self.ty_display(ty::Id::VOID)); + } + + self.errors.borrow().len() == prev_err_len + } + fn ty(&mut self, expr: &Expr) -> ty::Id { self.parse_ty(self.ci.file, expr, None, self.files) } @@ -4012,7 +4215,7 @@ impl<'a> Codegen<'a> { lhs: &mut Value, rhs: &mut Value, op: TokenKind, - ) -> (ty::Id, usize, Nid) { + ) -> (ty::Id, Nid) { if let Some(upcasted) = lhs.ty.try_upcast(rhs.ty) { let to_correct = if lhs.ty != upcasted { Some((lhs, rhs)) @@ -4034,26 +4237,23 @@ impl<'a> Codegen<'a> { self.ci.nodes.new_node(upcasted, Kind::BinOp { op: TokenKind::Mul }, [ VOID, oper.id, cnst, ]); - return ( - upcasted, - self.ci.nodes[other.id].aclass as _, - self.ci.nodes[other.id].mem, - ); + return (upcasted, self.ci.nodes.aclass_index(other.id).1); } } - (upcasted, DEFAULT_ACLASS, VOID) + (upcasted, VOID) } else { let ty = self.ty_display(lhs.ty); let expected = self.ty_display(rhs.ty); self.report(pos, fa!("'{ty} {op} {expected}' is not supported")); - (ty::Id::NEVER, DEFAULT_ACLASS, VOID) + (ty::Id::NEVER, VOID) } } fn wrap_in_opt(&mut self, val: &mut Value) { debug_assert!(!val.var); + let was_ptr = val.ptr; let oty = self.tys.make_opt(val.ty); if let Some((uninit, ..)) = self.tys.nieche_of(val.ty) { @@ -4069,8 +4269,8 @@ impl<'a> Codegen<'a> { Loc::Reg => { self.strip_ptr(val); // registers have inverted offsets so that accessing the inner type is a noop - let flag_offset = self.tys.size_of(oty) - flag_offset - 1; - let fill = self.ci.nodes.new_const(oty, 1i64 << (flag_offset * 8 - 1)); + let flag_offset = self.tys.size_of(oty) * 8 - flag_offset * 8 - 1; + let fill = self.ci.nodes.new_const(oty, 1i64 << flag_offset); val.id = self .ci .nodes @@ -4089,34 +4289,38 @@ impl<'a> Codegen<'a> { val.ty = oty; } } + + if !was_ptr { + self.strip_ptr(val); + } } - fn unwrap_opt(&mut self, pos: Pos, opt: &mut Value) { + fn implicit_unwrap(&mut self, pos: Pos, opt: &mut Value) { + self.unwrap_low(pos, opt, AssertKind::NullCheck); + } + + fn explicit_unwrap(&mut self, pos: Pos, opt: &mut Value) { + self.unwrap_low(pos, opt, AssertKind::UnwrapCheck); + } + + fn unwrap_low(&mut self, pos: Pos, opt: &mut Value, kind: AssertKind) { let Some(ty) = self.tys.inner_of(opt.ty) else { return }; let null_check = self.gen_null_check(*opt, ty, TokenKind::Eq); - // TODO: extract the if check int a fucntion - let ctrl = self.ci.nodes.new_node(ty::Id::VOID, Kind::If, [self.ci.ctrl.get(), null_check]); - let ctrl_ty = self.ci.nodes[ctrl].ty; - self.ci.nodes.remove(ctrl); let oty = mem::replace(&mut opt.ty, ty); - match ctrl_ty { - ty::Id::LEFT_UNREACHABLE => { - self.unwrap_opt_unchecked(ty, oty, opt); - } - ty::Id::RIGHT_UNREACHABLE => { - self.report(pos, "the value is always null, some checks might need to be inverted"); - } - _ => { - self.report( - pos, - "can't prove the value is not 'null', \ - use '@unwrap()' if you believe compiler is stupid, \ - or explicitly check for null and handle it \ - ('if == null { /* handle */ } else { /* use opt */ }')", - ); - } - } + self.unwrap_opt_unchecked(ty, oty, opt); + + // TODO: extract the if check int a fucntion + self.ci.ctrl.set( + self.ci.nodes.new_node(oty, Kind::Assert { kind, pos }, [ + self.ci.ctrl.get(), + null_check, + opt.id, + ]), + &mut self.ci.nodes, + ); + self.ci.nodes.pass_aclass(self.ci.nodes.aclass_index(opt.id).1, self.ci.ctrl.get()); + opt.id = self.ci.ctrl.get(); } fn unwrap_opt_unchecked(&mut self, ty: ty::Id, oty: ty::Id, opt: &mut Value) { @@ -4136,6 +4340,7 @@ impl<'a> Codegen<'a> { fn gen_null_check(&mut self, mut cmped: Value, ty: ty::Id, op: TokenKind) -> Nid { let OptLayout { flag_ty, flag_offset, .. } = self.tys.opt_layout(ty); + debug_assert!(cmped.ty.is_optional()); match cmped.ty.loc(self.tys) { Loc::Reg => { @@ -4146,6 +4351,7 @@ impl<'a> Codegen<'a> { Loc::Stack => { cmped.id = self.offset(cmped.id, flag_offset); cmped.ty = flag_ty; + debug_assert!(cmped.ptr); self.strip_ptr(&mut cmped); let inps = [VOID, cmped.id, self.ci.nodes.new_const(flag_ty, 0)]; self.ci.nodes.new_node(ty::Id::BOOL, Kind::BinOp { op }, inps) @@ -4195,7 +4401,7 @@ impl<'a> Codegen<'a> { if let Some(inner) = self.tys.inner_of(src.ty) && inner.try_upcast(expected) == Some(expected) { - self.unwrap_opt(pos, src); + self.implicit_unwrap(pos, src); return self.assert_ty(pos, src, expected, hint); } @@ -4238,6 +4444,7 @@ impl TypeParser for Codegen<'_> { } fn eval_const(&mut self, file: FileId, expr: &Expr, ret: ty::Id) -> u64 { + self.ct.activate(); let mut scope = mem::take(&mut self.ci.scope.vars); self.pool.push_ci(file, Some(ret), self.tys.tasks.len(), &mut self.ci); self.ci.scope.vars = scope; @@ -4247,17 +4454,14 @@ impl TypeParser for Codegen<'_> { self.expr(&Expr::Return { pos: expr.pos(), val: Some(expr) }); scope = mem::take(&mut self.ci.scope.vars); - self.ci.finalize(&mut self.pool.nid_stack, self.tys, self.files); - let res = if self.errors.borrow().len() == prev_err_len { - self.emit_and_eval(file, ret, &mut []) - } else { - 1 - }; + let res = + if self.finalize(prev_err_len) { self.emit_and_eval(file, ret, &mut []) } else { 1 }; self.pool.pop_ci(&mut self.ci); self.ci.scope.vars = scope; + self.ct.deactivate(); res } @@ -4270,18 +4474,21 @@ impl TypeParser for Codegen<'_> { } fn on_reuse(&mut self, existing: ty::Id) { + let state_slot = self.ct.active() as usize; if let ty::Kind::Func(id) = existing.expand() && let func = &mut self.tys.ins.funcs[id as usize] - && let Err(idx) = task::unpack(func.offset) + && let CompState::Queued(idx) = func.comp_state[state_slot] && idx < self.tys.tasks.len() { - func.offset = task::id(self.tys.tasks.len()); + func.comp_state[state_slot] = CompState::Queued(self.tys.tasks.len()); let task = self.tys.tasks[idx].take(); self.tys.tasks.push(task); } } fn eval_global(&mut self, file: FileId, name: Ident, expr: &Expr) -> ty::Id { + self.ct.activate(); + let gid = self.tys.ins.globals.len() as ty::Global; self.tys.ins.globals.push(Global { file, name, ..Default::default() }); @@ -4291,10 +4498,8 @@ impl TypeParser for Codegen<'_> { self.expr(&(Expr::Return { pos: expr.pos(), val: Some(expr) })); - self.ci.finalize(&mut self.pool.nid_stack, self.tys, self.files); - let ret = self.ci.ret.expect("for return type to be infered"); - if self.errors.borrow().len() == prev_err_len { + if self.finalize(prev_err_len) { let mut mem = vec![0u8; self.tys.size_of(ret) as usize]; self.emit_and_eval(file, ret, &mut mem); self.tys.ins.globals[gid as usize].data = mem; @@ -4303,6 +4508,7 @@ impl TypeParser for Codegen<'_> { self.pool.pop_ci(&mut self.ci); self.tys.ins.globals[gid as usize].ty = ret; + self.ct.deactivate(); ty.compress() } @@ -4320,7 +4526,7 @@ impl TypeParser for Codegen<'_> { #[cfg(test)] mod tests { use { - super::CodegenCtx, + super::{hbvm::HbvmBackend, CodegenCtx}, alloc::{string::String, vec::Vec}, core::fmt::Write, }; @@ -4332,7 +4538,8 @@ mod tests { let mut ctx = CodegenCtx::default(); let (ref files, embeds) = crate::test_parse_files(ident, input, &mut ctx.parser); - let mut codegen = super::Codegen::new(files, &mut ctx); + let mut backend = HbvmBackend::default(); + let mut codegen = super::Codegen::new(&mut backend, files, &mut ctx); codegen.push_embeds(embeds); codegen.generate(0); @@ -4346,21 +4553,21 @@ mod tests { } let mut out = Vec::new(); - codegen.tys.reassemble(&mut out); + codegen.assemble(&mut out); - let err = codegen.tys.disasm(&out, codegen.files, output, |_| {}); + let err = codegen.disasm(output, &out); if let Err(e) = err { writeln!(output, "!!! asm is invalid: {e}").unwrap(); - return; + } else { + super::hbvm::test_run_vm(&out, output); } - - super::hbvm::test_run_vm(&out, output); } crate::run_tests! { generate: // Tour Examples main_fn; arithmetic; + advanced_floating_point_arithmetic; floating_point_arithmetic; functions; comments; @@ -4388,6 +4595,10 @@ mod tests { fb_driver; // Purely Testing Examples; + nullable_structure; + needless_unwrap; + inlining_issues; + null_check_test; only_break_loop; reading_idk; nonexistent_ident_import; @@ -4427,5 +4638,8 @@ mod tests { aliasing_overoptimization; global_aliasing_overptimization; overwrite_aliasing_overoptimization; + more_if_opts; + optional_from_eca; + returning_optional_issues; } } diff --git a/lang/src/son/hbvm.rs b/lang/src/son/hbvm.rs index 4411cd4..194664b 100644 --- a/lang/src/son/hbvm.rs +++ b/lang/src/son/hbvm.rs @@ -1,573 +1,265 @@ use { - super::{ItemCtx, Nid, Nodes, Pool, RallocBRef, Regalloc, ARG_START, NEVER, VOID}, + super::{AssemblySpec, Backend, Nid, Node, Nodes}, crate::{ lexer::TokenKind, parser, reg, - son::{write_reloc, Kind, MEM}, - task, - ty::{self, Arg, Loc}, - utils::{BitSet, Vc}, - HashMap, Offset, PLoc, Reloc, Sig, Size, TypedReloc, Types, + son::{debug_assert_matches, write_reloc, Kind, MEM}, + ty::{self, Loc}, + Offset, Reloc, Size, TypedReloc, Types, }, - alloc::{borrow::ToOwned, boxed::Box, collections::BTreeMap, string::String, vec::Vec}, - core::{assert_matches::debug_assert_matches, mem}, + alloc::{boxed::Box, collections::BTreeMap, string::String, vec::Vec}, + core::mem, hbbytecode::{self as instrs, *}, }; -impl Types { - pub fn assemble(&mut self, to: &mut Vec) { +mod my_regalloc; +mod their_regalloc; + +struct FuncDt { + offset: Offset, + // TODO: change to indices into common vec + relocs: Vec, + code: Vec, +} + +impl Default for FuncDt { + fn default() -> Self { + Self { offset: u32::MAX, relocs: Default::default(), code: Default::default() } + } +} + +struct GlobalDt { + offset: Offset, +} + +impl Default for GlobalDt { + fn default() -> Self { + Self { offset: u32::MAX } + } +} + +#[derive(Default)] +struct Assembler { + frontier: Vec, + globals: Vec, + funcs: Vec, +} + +#[derive(Default)] +pub struct HbvmBackend { + funcs: Vec, + globals: Vec, + asm: Assembler, + ralloc: their_regalloc::Regalloc, + ralloc_my: my_regalloc::Res, + + ret_relocs: Vec, + relocs: Vec, + jump_relocs: Vec<(Nid, Reloc)>, + code: Vec, + offsets: Vec, +} + +impl HbvmBackend { + fn emit(&mut self, instr: (usize, [u8; instrs::MAX_SIZE])) { + emit(&mut self.code, instr); + } +} + +impl Backend for HbvmBackend { + fn assemble_bin(&mut self, entry: ty::Func, types: &Types, to: &mut Vec) { to.extend([0u8; HEADER_SIZE]); binary_prelude(to); - let exe = self.dump_reachable(0, to); - Reloc::new(HEADER_SIZE, 3, 4).apply_jump(to, self.ins.funcs[0].offset, 0); + let AssemblySpec { code_length, data_length, entry } = + self.assemble_reachable(entry, types, to); + + let exe = AbleOsExecutableHeader { + magic_number: [0x15, 0x91, 0xD2], + executable_version: 0, + code_length, + data_length, + debug_length: 0, + config_length: 0, + metadata_length: 0, + }; + Reloc::new(HEADER_SIZE, 3, 4).apply_jump(to, entry, 0); unsafe { *to.as_mut_ptr().cast::() = exe } } - pub fn dump_reachable(&mut self, from: ty::Func, to: &mut Vec) -> AbleOsExecutableHeader { - debug_assert!(self.tmp.frontier.is_empty()); - debug_assert!(self.tmp.funcs.is_empty()); - debug_assert!(self.tmp.globals.is_empty()); + fn assemble_reachable( + &mut self, + from: ty::Func, + types: &Types, + to: &mut Vec, + ) -> AssemblySpec { + debug_assert!(self.asm.frontier.is_empty()); + debug_assert!(self.asm.funcs.is_empty()); + debug_assert!(self.asm.globals.is_empty()); - self.tmp.frontier.push(ty::Kind::Func(from).compress()); - while let Some(itm) = self.tmp.frontier.pop() { + self.globals.resize_with(types.ins.globals.len(), Default::default); + + self.asm.frontier.push(ty::Kind::Func(from).compress()); + while let Some(itm) = self.asm.frontier.pop() { match itm.expand() { ty::Kind::Func(func) => { - let fuc = &mut self.ins.funcs[func as usize]; - if task::is_done(fuc.offset) { + let fuc = &mut self.funcs[func as usize]; + debug_assert!(!fuc.code.is_empty()); + if fuc.offset != u32::MAX { continue; } fuc.offset = 0; - self.tmp.funcs.push(func); - self.tmp.frontier.extend(fuc.relocs.iter().map(|r| r.target)); + self.asm.funcs.push(func); + self.asm.frontier.extend(fuc.relocs.iter().map(|r| r.target)); } ty::Kind::Global(glob) => { - let glb = &mut self.ins.globals[glob as usize]; - if task::is_done(glb.offset) { + let glb = &mut self.globals[glob as usize]; + if glb.offset != u32::MAX { continue; } glb.offset = 0; - self.tmp.globals.push(glob); + self.asm.globals.push(glob); } _ => unreachable!(), } } - for &func in &self.tmp.funcs { - let fuc = &mut self.ins.funcs[func as usize]; + let init_len = to.len(); + + for &func in &self.asm.funcs { + let fuc = &mut self.funcs[func as usize]; fuc.offset = to.len() as _; debug_assert!(!fuc.code.is_empty()); to.extend(&fuc.code); } - let code_length = to.len(); + let code_length = to.len() - init_len; - for global in self.tmp.globals.drain(..) { - let global = &mut self.ins.globals[global as usize]; - global.offset = to.len() as _; - to.extend(&global.data); + 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); } - let data_length = to.len() - code_length; + let data_length = to.len() - code_length - init_len; - for func in self.tmp.funcs.drain(..) { - let fuc = &self.ins.funcs[func as usize]; + for func in self.asm.funcs.drain(..) { + let fuc = &self.funcs[func as usize]; for rel in &fuc.relocs { let offset = match rel.target.expand() { - ty::Kind::Func(fun) => self.ins.funcs[fun as usize].offset, - ty::Kind::Global(glo) => self.ins.globals[glo as usize].offset, + ty::Kind::Func(fun) => self.funcs[fun as usize].offset, + ty::Kind::Global(glo) => self.globals[glo as usize].offset, _ => unreachable!(), }; rel.reloc.apply_jump(to, offset, fuc.offset); } } - AbleOsExecutableHeader { - magic_number: [0x15, 0x91, 0xD2], - executable_version: 0, - code_length: code_length.saturating_sub(HEADER_SIZE) as _, + AssemblySpec { + code_length: code_length as _, data_length: data_length as _, - debug_length: 0, - config_length: 0, - metadata_length: 0, + entry: self.funcs[from as usize].offset, } } - pub fn disasm<'a>( + fn disasm<'a>( &'a self, mut sluce: &[u8], + eca_handler: &mut dyn FnMut(&mut &[u8]), + types: &'a Types, files: &'a [parser::Ast], output: &mut String, - eca_handler: impl FnMut(&mut &[u8]), ) -> Result<(), hbbytecode::DisasmError<'a>> { use hbbytecode::DisasmItem; - let functions = self + let functions = types .ins .funcs .iter() - .filter(|f| task::is_done(f.offset)) - .map(|f| { + .zip(&self.funcs) + .filter(|(_, f)| f.offset != u32::MAX) + .map(|(f, fd)| { let name = if f.file != u32::MAX { let file = &files[f.file as usize]; file.ident_str(f.name) } else { "target_fn" }; - (f.offset, (name, f.code.len() as u32, DisasmItem::Func)) + (fd.offset, (name, fd.code.len() as u32, DisasmItem::Func)) }) - .chain(self.ins.globals.iter().filter(|g| task::is_done(g.offset)).map(|g| { - let name = if g.file == u32::MAX { - core::str::from_utf8(&g.data).unwrap_or("invalid utf-8") - } else { - let file = &files[g.file as usize]; - file.ident_str(g.name) - }; - (g.offset, (name, g.data.len() as Size, DisasmItem::Global)) - })) + .chain( + types + .ins + .globals + .iter() + .zip(&self.globals) + .filter(|(_, g)| g.offset != u32::MAX) + .map(|(g, gd)| { + let name = if g.file == u32::MAX { + core::str::from_utf8(&g.data).unwrap_or("invalid utf-8") + } else { + let file = &files[g.file as usize]; + file.ident_str(g.name) + }; + (gd.offset, (name, g.data.len() as Size, DisasmItem::Global)) + }), + ) .collect::>(); hbbytecode::disasm(&mut sluce, &functions, output, eca_handler) } -} -impl ItemCtx { - fn emit(&mut self, instr: (usize, [u8; instrs::MAX_SIZE])) { - emit(&mut self.code, instr); - } - - fn emit_body_code( + fn emit_ct_body( &mut self, - sig: Sig, + id: ty::Func, + nodes: &mut Nodes, tys: &Types, files: &[parser::Ast], - ralloc: &mut Regalloc, - ) -> usize { - let mut nodes = mem::take(&mut self.nodes); - - let fuc = Function::new(&mut nodes, tys, sig); - log::info!("{:?}", fuc); - if self.call_count != 0 { - mem::swap( - &mut ralloc.env.preferred_regs_by_class, - &mut ralloc.env.non_preferred_regs_by_class, - ); - }; - - let options = regalloc2::RegallocOptions { - verbose_log: false, - validate_ssa: cfg!(debug_assertions), - algorithm: regalloc2::Algorithm::Ion, - }; - regalloc2::run_with_ctx(&fuc, &ralloc.env, &options, &mut ralloc.ctx).unwrap_or_else( - |err| { - if let regalloc2::RegAllocError::SSA(vreg, inst) = err { - fuc.nodes[vreg.vreg() as Nid].lock_rc = Nid::MAX; - fuc.nodes[fuc.instrs[inst.index()].nid].lock_rc = Nid::MAX - 1; - } - fuc.nodes.graphviz_in_browser(tys, files); - panic!("{err}") - }, - ); - - if self.call_count != 0 { - mem::swap( - &mut ralloc.env.preferred_regs_by_class, - &mut ralloc.env.non_preferred_regs_by_class, - ); - }; - - let mut saved_regs = HashMap::::default(); - let mut atr = |allc: regalloc2::Allocation| { - debug_assert!(allc.is_reg()); - let hvenc = regalloc2::PReg::from_index(allc.index()).hw_enc() as u8; - if hvenc <= 12 { - return hvenc; - } - let would_insert = saved_regs.len() as u8 + reg::RET_ADDR + 1; - *saved_regs.entry(hvenc).or_insert(would_insert) - }; - - 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(..) | PLoc::Ref(..) => continue, - }; - self.emit(instrs::st(rg, reg::STACK_PTR, fuc.nodes[arg].offset as _, size)); - if fuc.nodes[arg].lock_rc == 0 { - self.emit(instrs::addi64(rg, reg::STACK_PTR, fuc.nodes[arg].offset as _)); - } - } - - for (i, block) in fuc.blocks.iter().enumerate() { - let blk = regalloc2::Block(i as _); - fuc.nodes[block.nid].offset = self.code.len() as _; - for instr_or_edit in ralloc.ctx.output.block_insts_and_edits(&fuc, blk) { - let inst = match instr_or_edit { - regalloc2::InstOrEdit::Inst(inst) => inst, - regalloc2::InstOrEdit::Edit(®alloc2::Edit::Move { from, to }) => { - self.emit(instrs::cp(atr(to), atr(from))); - continue; - } - }; - - let nid = fuc.instrs[inst.index()].nid; - if nid == NEVER { - continue; - }; - let allocs = ralloc.ctx.output.inst_allocs(inst); - let node = &fuc.nodes[nid]; - - let mut extend = |base: ty::Id, dest: ty::Id, from: usize, to: usize| { - 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(allocs[to]), atr(allocs[from])) - } - _ => { - let mask = (1u64 << (bsize * 8)) - 1; - instrs::andi(atr(allocs[to]), atr(allocs[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] = allocs else { unreachable!() }; - let &[_, lh, rh] = fuc.nodes[cnd].inputs.as_slice() else { - unreachable!() - }; - - self.emit(extend(fuc.nodes[lh].ty, fuc.nodes[lh].ty.extend(), 0, 0)); - self.emit(extend(fuc.nodes[rh].ty, fuc.nodes[rh].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(allocs[0]), reg::ZERO, 0)); - } - } - Kind::Loop | Kind::Region => { - if node.ralloc_backref as usize != i + 1 { - let rel = Reloc::new(self.code.len(), 1, 4); - self.jump_relocs.push((nid, rel)); - self.emit(instrs::jmp(0)); - } - } - Kind::Return => { - match retl { - Some(PLoc::Reg(r, size)) if sig.ret.loc(tys) == Loc::Stack => { - self.emit(instrs::ld(r, atr(allocs[0]), 0, size)) - } - None | Some(PLoc::Reg(..)) => {} - Some(PLoc::WideReg(r, size)) => { - self.emit(instrs::ld(r, atr(allocs[0]), 0, size)) - } - Some(PLoc::Ref(_, size)) => { - let [src, dst] = [atr(allocs[0]), atr(allocs[1])]; - 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.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(allocs[0]), - (f64::from_bits(value as _) as f32).to_bits(), - ), - ty::Id::F64 => instrs::li64(atr(allocs[0]), value as _), - _ => unreachable!(), - }); - } - Kind::CInt { value } => self.emit(match tys.size_of(node.ty) { - 1 => instrs::li8(atr(allocs[0]), value as _), - 2 => instrs::li16(atr(allocs[0]), value as _), - 4 => instrs::li32(atr(allocs[0]), value as _), - _ => instrs::li64(atr(allocs[0]), value as _), - }), - Kind::UnOp { op } => { - let op = op - .unop(node.ty, fuc.nodes[node.inputs[1]].ty) - .expect("TODO: unary operator not supported"); - let &[dst, oper] = allocs else { unreachable!() }; - self.emit(op(atr(dst), atr(oper))); - } - Kind::BinOp { .. } if node.lock_rc != 0 => {} - Kind::BinOp { op } => { - let &[.., lh, rh] = node.inputs.as_slice() else { unreachable!() }; - - if let Kind::CInt { value } = fuc.nodes[rh].kind - && fuc.nodes[rh].lock_rc != 0 - && let Some(op) = op.imm_binop(node.ty) - { - let &[dst, lhs] = allocs else { unreachable!() }; - self.emit(op(atr(dst), atr(lhs), value as _)); - } else if let Some(op) = - op.binop(node.ty).or(op.float_cmp(fuc.nodes[lh].ty)) - { - let &[dst, lhs, rhs] = allocs else { unreachable!() }; - self.emit(op(atr(dst), atr(lhs), atr(rhs))); - } else if let Some(against) = op.cmp_against() { - let op_ty = fuc.nodes[lh].ty; - - self.emit(extend(fuc.nodes[lh].ty, fuc.nodes[lh].ty.extend(), 0, 0)); - self.emit(extend(fuc.nodes[rh].ty, fuc.nodes[rh].ty.extend(), 1, 1)); - let &[dst, lhs, rhs] = allocs else { unreachable!() }; - - 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(dst), atr(lhs), atr(rhs))); - self.emit(instrs::not(atr(dst), atr(dst))); - } else if op_ty.is_integer() { - let op_fn = - if op_ty.is_signed() { instrs::cmps } else { instrs::cmpu }; - self.emit(op_fn(atr(dst), atr(lhs), atr(rhs))); - self.emit(instrs::cmpui(atr(dst), atr(dst), against)); - if matches!(op, TokenKind::Eq | TokenKind::Lt | TokenKind::Gt) { - self.emit(instrs::not(atr(dst), atr(dst))); - } - } else { - todo!("unhandled operator: {op}"); - } - } else { - todo!("unhandled operator: {op}"); - } - } - Kind::Call { args, func } => { - let (ret, mut parama) = tys.parama(node.ty); - let has_ret = ret.is_some() as usize; - let mut args = args.args(); - let mut allocs = allocs[has_ret..].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 &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(..) | PLoc::Reg(..) => continue, - }; - 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)); - } - - if let Some(PLoc::WideReg(r, size)) = ret { - debug_assert_eq!( - fuc.nodes[*node.inputs.last().unwrap()].kind, - Kind::Stck - ); - let stck = fuc.nodes[*node.inputs.last().unwrap()].offset; - self.emit(instrs::st(r, reg::STACK_PTR, stck as _, size)); - } - if let Some(PLoc::Reg(r, size)) = ret - && node.ty.loc(tys) == Loc::Stack - { - debug_assert_eq!( - fuc.nodes[*node.inputs.last().unwrap()].kind, - Kind::Stck - ); - let stck = fuc.nodes[*node.inputs.last().unwrap()].offset; - self.emit(instrs::st(r, reg::STACK_PTR, stck as _, size)); - } - } - 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(allocs[0]), 0, 0)); - } - Kind::Stck => { - let base = reg::STACK_PTR; - let offset = fuc.nodes[nid].offset; - self.emit(instrs::addi64(atr(allocs[0]), 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, fuc.nodes[region].offset + offset), - _ => (atr(allocs[1]), offset), - }; - self.emit(instrs::ld(atr(allocs[0]), 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 (base, offset, src) = match nd.kind { - Kind::Stck if node.ty.loc(tys) == Loc::Reg => { - (reg::STACK_PTR, nd.offset + offset, allocs[0]) - } - _ => (atr(allocs[0]), offset, allocs[1]), - }; - - 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::Start - | Kind::Entry - | Kind::Mem - | Kind::End - | Kind::Loops - | Kind::Then - | Kind::Else - | Kind::Phi - | Kind::Arg => unreachable!(), - } - } - } - - self.nodes = nodes; - - saved_regs.len() - } - - pub fn emit_ct_body( - &mut self, - tys: &mut Types, - files: &[parser::Ast], - sig: Sig, - pool: &mut Pool, ) { - self.emit_body(tys, files, sig, pool); - self.code.truncate(self.code.len() - instrs::jala(0, 0, 0).0); - self.emit(instrs::tx()); + self.emit_body(id, nodes, tys, files); + let fd = &mut self.funcs[id as usize]; + fd.code.truncate(fd.code.len() - instrs::jala(0, 0, 0).0); + emit(&mut fd.code, instrs::tx()); } - pub fn emit_body(&mut self, tys: &mut Types, files: &[parser::Ast], sig: Sig, pool: &mut Pool) { - self.nodes.check_final_integrity(tys, files); - self.nodes.graphviz(tys, files); - self.nodes.gcm(&mut pool.nid_stack); - self.nodes.basic_blocks(); - self.nodes.graphviz(tys, files); + 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(); debug_assert!(self.code.is_empty()); - let tail = mem::take(&mut self.call_count) == 0; - '_open_function: { - self.emit(instrs::addi64(reg::STACK_PTR, reg::STACK_PTR, 0)); - self.emit(instrs::st(reg::RET_ADDR + tail as u8, reg::STACK_PTR, 0, 0)); - } + self.offsets.clear(); + self.offsets.resize(nodes.values.len(), Offset::MAX); let mut stack_size = 0; '_compute_stack: { - let mems = mem::take(&mut self.nodes[MEM].outputs); + let mems = mem::take(&mut nodes[MEM].outputs); for &stck in mems.iter() { - if !matches!(self.nodes[stck].kind, Kind::Stck | Kind::Arg) { + if !matches!(nodes[stck].kind, Kind::Stck | Kind::Arg) { debug_assert_matches!( - self.nodes[stck].kind, + nodes[stck].kind, Kind::Phi | Kind::Return | Kind::Load | Kind::Call { .. } | Kind::Stre ); continue; } - stack_size += tys.size_of(self.nodes[stck].ty); - self.nodes[stck].offset = stack_size; + stack_size += tys.size_of(nodes[stck].ty); + self.offsets[stck as usize] = stack_size; } for &stck in mems.iter() { - if !matches!(self.nodes[stck].kind, Kind::Stck | Kind::Arg) { + if !matches!(nodes[stck].kind, Kind::Stck | Kind::Arg) { continue; } - self.nodes[stck].offset = stack_size - self.nodes[stck].offset; + self.offsets[stck as usize] = stack_size - self.offsets[stck as usize]; } - self.nodes[MEM].outputs = mems; + nodes[MEM].outputs = mems; } - let saved = self.emit_body_code(sig, tys, files, &mut pool.ralloc); + 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.nodes[r].offset as usize != self.code.len()) + .map_or(true, |&(r, _)| self.offsets[r as usize] as usize != self.code.len()) { self.code.truncate(self.code.len() - 5); self.ret_relocs.pop(); @@ -575,7 +267,7 @@ impl ItemCtx { // FIXME: maybe do this incrementally for (nd, rel) in self.jump_relocs.drain(..) { - let offset = self.nodes[nd].offset; + let offset = self.offsets[nd as usize]; //debug_assert!(offset < self.code.len() as u32 - 1); rel.apply_jump(&mut self.code, offset, 0); } @@ -590,17 +282,20 @@ impl ItemCtx { let pushed = (saved as i64 + !tail as i64) * 8; let stack = stack_size as i64; + let add_len = instrs::addi64(0, 0, 0).0; + let st_len = instrs::st(0, 0, 0, 0).0; + match (pushed, stack) { (0, 0) => { - stripped_prelude_size = instrs::addi64(0, 0, 0).0 + instrs::st(0, 0, 0, 0).0; + stripped_prelude_size = add_len + st_len; self.code.drain(0..stripped_prelude_size); break '_close_function; } (0, stack) => { write_reloc(&mut self.code, 3, -stack, 8); - stripped_prelude_size = instrs::st(0, 0, 0, 0).0; - let end = instrs::addi64(0, 0, 0).0 + instrs::st(0, 0, 0, 0).0; - self.code.drain(instrs::addi64(0, 0, 0).0..end); + stripped_prelude_size = st_len; + let end = add_len + st_len; + self.code.drain(add_len..end); self.emit(instrs::addi64(reg::STACK_PTR, reg::STACK_PTR, stack as _)); break '_close_function; } @@ -623,616 +318,61 @@ impl ItemCtx { if sig.ret != ty::Id::NEVER { self.emit(instrs::jala(reg::ZERO, reg::RET_ADDR, 0)); } - } -} -#[derive(Debug)] -struct Block { - nid: Nid, - preds: Vec, - succs: Vec, - instrs: regalloc2::InstRange, - params: Vec, - branch_blockparams: Vec, -} - -#[derive(Debug)] -struct Instr { - nid: Nid, - ops: Vec, -} - -pub struct Function<'a> { - sig: Sig, - nodes: &'a mut Nodes, - tys: &'a Types, - blocks: Vec, - instrs: Vec, -} - -impl core::fmt::Debug for Function<'_> { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - for (i, block) in self.blocks.iter().enumerate() { - writeln!(f, "sb{i}{:?}-{:?}:", block.params, block.preds)?; - - for inst in block.instrs.iter() { - let instr = &self.instrs[inst.index()]; - writeln!(f, "{}: i{:?}:{:?}", inst.index(), self.nodes[instr.nid].kind, instr.ops)?; - } - - writeln!(f, "eb{i}{:?}-{:?}:", block.branch_blockparams, block.succs)?; + if self.funcs.get(id as usize).is_none() { + self.funcs.resize_with(id as usize + 1, Default::default); } - Ok(()) + self.funcs[id as usize].code = mem::take(&mut self.code); + self.funcs[id as usize].relocs = mem::take(&mut self.relocs); + + debug_assert_eq!(self.ret_relocs.len(), 0); + debug_assert_eq!(self.relocs.len(), 0); + debug_assert_eq!(self.jump_relocs.len(), 0); + debug_assert_eq!(self.code.len(), 0); } } -impl<'a> Function<'a> { - fn new(nodes: &'a mut Nodes, tys: &'a Types, sig: Sig) -> Self { - let mut s = - Self { nodes, tys, sig, blocks: Default::default(), instrs: Default::default() }; - s.nodes.visited.clear(s.nodes.values.len()); - s.emit_node(VOID, VOID); - s.add_block(0); - s.blocks.pop(); - s - } - - fn add_block(&mut self, nid: Nid) -> RallocBRef { - if let Some(prev) = self.blocks.last_mut() { - prev.instrs = regalloc2::InstRange::new( - prev.instrs.first(), - regalloc2::Inst::new(self.instrs.len()), - ); - } - - self.blocks.push(Block { - nid, - preds: Default::default(), - succs: Default::default(), - instrs: regalloc2::InstRange::new( - regalloc2::Inst::new(self.instrs.len()), - regalloc2::Inst::new(self.instrs.len() + 1), - ), - params: Default::default(), - branch_blockparams: Default::default(), - }); - self.blocks.len() as RallocBRef - 1 - } - - fn add_instr(&mut self, nid: Nid, ops: Vec) { - self.instrs.push(Instr { nid, ops }); - } - - fn urg(&mut self, nid: Nid) -> regalloc2::Operand { - regalloc2::Operand::reg_use(self.rg(nid)) - } - - fn drg(&mut self, nid: Nid) -> regalloc2::Operand { - regalloc2::Operand::reg_def(self.rg(nid)) - } - - fn rg(&self, nid: Nid) -> regalloc2::VReg { - debug_assert!( - !self.nodes.is_cfg(nid) || matches!(self.nodes[nid].kind, Kind::Call { .. }), - "{:?}", - self.nodes[nid] - ); - debug_assert_eq!(self.nodes[nid].lock_rc, 0, "{nid} {:?}", self.nodes[nid]); - debug_assert!(self.nodes[nid].kind != Kind::Phi || self.nodes[nid].ty != ty::Id::VOID); - regalloc2::VReg::new(nid as _, regalloc2::RegClass::Int) - } - - fn emit_node(&mut self, nid: Nid, prev: Nid) { - if matches!(self.nodes[nid].kind, Kind::Region | Kind::Loop) { - let prev_bref = self.nodes[prev].ralloc_backref; - let node = self.nodes[nid].clone(); - - let idx = 1 + node.inputs.iter().position(|&i| i == prev).unwrap(); - - for ph in node.outputs { - if self.nodes[ph].kind != Kind::Phi || self.nodes[ph].ty == ty::Id::VOID { - continue; - } - - let rg = self.rg(self.nodes[ph].inputs[idx]); - self.blocks[prev_bref as usize].branch_blockparams.push(rg); - } - - self.add_instr(nid, vec![]); - - match (self.nodes[nid].kind, self.nodes.visited.set(nid)) { - (Kind::Loop, false) => { - for i in node.inputs { - self.bridge(i, nid); - } - return; - } - (Kind::Region, true) => return, - _ => {} - } - } else if !self.nodes.visited.set(nid) { - return; - } - - let mut node = self.nodes[nid].clone(); +impl Nodes { + fn is_never_used(&self, nid: Nid, tys: &Types) -> bool { + let node = &self[nid]; match node.kind { - Kind::Start => { - debug_assert_matches!(self.nodes[node.outputs[0]].kind, Kind::Entry); - self.emit_node(node.outputs[0], VOID) + Kind::CInt { .. } => 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::Add | TokenKind::Sub } => { + self[node.inputs[1]].lock_rc != 0 + || (self.is_const(node.inputs[2]) + && node.outputs.iter().all(|&n| self[n].uses_direct_offset_of(nid, tys))) } - Kind::If => { - self.nodes[nid].ralloc_backref = self.nodes[prev].ralloc_backref; - - 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) - { - if swapped { - mem::swap(&mut then, &mut else_); - } - let &[_, lhs, rhs] = self.nodes[cond].inputs.as_slice() else { unreachable!() }; - let ops = vec![self.urg(lhs), self.urg(rhs)]; - self.add_instr(nid, ops); - } else { - mem::swap(&mut then, &mut else_); - let ops = vec![self.urg(cond)]; - self.add_instr(nid, ops); - } - - self.emit_node(then, nid); - self.emit_node(else_, nid); - } - Kind::Region | Kind::Loop => { - self.nodes[nid].ralloc_backref = self.add_block(nid); - if node.kind == Kind::Region { - for i in node.inputs { - self.bridge(i, nid); - } - } - let mut block = vec![]; - for ph in node.outputs.clone() { - if self.nodes[ph].kind != Kind::Phi || self.nodes[ph].ty == ty::Id::VOID { - continue; - } - block.push(self.rg(ph)); - } - self.blocks[self.nodes[nid].ralloc_backref as usize].params = block; - self.reschedule_block(nid, &mut node.outputs); - for o in node.outputs.into_iter().rev() { - self.emit_node(o, nid); - } - } - Kind::Return => { - let ops = match self.tys.parama(self.sig.ret).0 { - None => vec![], - Some(PLoc::Reg(..)) if self.sig.ret.loc(self.tys) == Loc::Stack => { - vec![self.urg(self.nodes[node.inputs[1]].inputs[1])] - } - Some(PLoc::Reg(r, ..)) => { - vec![regalloc2::Operand::reg_fixed_use( - self.rg(node.inputs[1]), - regalloc2::PReg::new(r as _, regalloc2::RegClass::Int), - )] - } - Some(PLoc::WideReg(..)) => { - vec![self.urg(self.nodes[node.inputs[1]].inputs[1])] - } - Some(PLoc::Ref(..)) => { - vec![self.urg(self.nodes[node.inputs[1]].inputs[1]), self.urg(MEM)] - } - }; - - self.add_instr(nid, ops); - self.emit_node(node.outputs[0], nid); - } - Kind::Die => { - self.add_instr(nid, vec![]); - self.emit_node(node.outputs[0], nid); - } - Kind::CInt { .. } - if node.outputs.iter().all(|&o| { - let ond = &self.nodes[o]; - matches!(ond.kind, Kind::BinOp { op } - if op.imm_binop(ond.ty).is_some() - && self.nodes.is_const(ond.inputs[2]) - && op.cond_op(ond.ty).is_none()) - }) => - { - self.nodes.lock(nid) - } - Kind::CInt { .. } => { - let ops = vec![self.drg(nid)]; - self.add_instr(nid, ops); - } - Kind::Entry => { - self.nodes[nid].ralloc_backref = self.add_block(nid); - - 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(PLoc::Reg(r, _) | PLoc::WideReg(r, _) | PLoc::Ref(r, _)) => { - self.add_instr(NEVER, vec![regalloc2::Operand::reg_fixed_def( - self.rg(arg), - regalloc2::PReg::new(r as _, regalloc2::RegClass::Int), - )]); - } - } - } - - if let Some(PLoc::Ref(r, ..)) = ret { - self.add_instr(NEVER, vec![regalloc2::Operand::reg_fixed_def( - self.rg(MEM), - regalloc2::PReg::new(r as _, regalloc2::RegClass::Int), - )]); - } - - self.reschedule_block(nid, &mut node.outputs); - for o in node.outputs.into_iter().rev() { - self.emit_node(o, nid); - } - } - Kind::Then | Kind::Else => { - self.nodes[nid].ralloc_backref = self.add_block(nid); - self.bridge(prev, nid); - self.reschedule_block(nid, &mut node.outputs); - for o in node.outputs.into_iter().rev() { - self.emit_node(o, nid); - } - } - Kind::BinOp { op: TokenKind::Add } if self.nodes[node.inputs[1]].lock_rc != 0 => self.nodes.lock(nid), - Kind::BinOp { op: TokenKind::Add } - if self.nodes.is_const(node.inputs[2]) - && node.outputs.iter().all(|&n| { - (matches!(self.nodes[n].kind, Kind::Stre if self.nodes[n].inputs[2] == nid) - || matches!(self.nodes[n].kind, Kind::Load if self.nodes[n].inputs[1] == nid)) - && self.nodes[n].ty.loc(self.tys) == Loc::Reg - }) => - { - self.nodes.lock(nid) - } - Kind::BinOp { op } - if op.cond_op(node.ty).is_some() - && node.outputs.iter().all(|&n| self.nodes[n].kind == Kind::If) => - { - self.nodes.lock(nid) - } - Kind::BinOp { .. } => { - let &[_, lhs, rhs] = node.inputs.as_slice() else { unreachable!() }; - - let ops = if let Kind::CInt { .. } = self.nodes[rhs].kind - && self.nodes[rhs].lock_rc != 0 - { - vec![self.drg(nid), self.urg(lhs)] - } else { - vec![self.drg(nid), self.urg(lhs), self.urg(rhs)] - }; - self.add_instr(nid, ops); - } - Kind::UnOp { .. } => { - let ops = vec![self.drg(nid), self.urg(node.inputs[1])]; - self.add_instr(nid, ops); - } - Kind::Call { args, .. } => { - self.nodes[nid].ralloc_backref = self.nodes[prev].ralloc_backref; - let mut ops = vec![]; - - let (ret, mut parama) = self.tys.parama(node.ty); - if ret.is_some() { - ops.push(regalloc2::Operand::reg_fixed_def( - self.rg(nid), - regalloc2::PReg::new(1, regalloc2::RegClass::Int), - )); - } - - let mut tys = args.args(); - let mut args = node.inputs[1..].iter(); - while let Some(ty) = tys.next_value(self.tys) { - let mut i = *args.next().unwrap(); - let Some(loc) = parama.next(ty, self.tys) else { continue }; - - match loc { - PLoc::Reg(r, _) if ty.loc(self.tys) == Loc::Reg => { - ops.push(regalloc2::Operand::reg_fixed_use( - self.rg(i), - regalloc2::PReg::new(r as _, regalloc2::RegClass::Int), - )); - } - PLoc::WideReg(..) | PLoc::Reg(..) => { - loop { - match self.nodes[i].kind { - Kind::Stre { .. } => i = self.nodes[i].inputs[2], - Kind::Load { .. } => i = self.nodes[i].inputs[1], - _ => break, - } - debug_assert_ne!(i, 0); - } - debug_assert!(i != 0); - ops.push(self.urg(i)); - } - PLoc::Ref(r, _) => { - loop { - match self.nodes[i].kind { - Kind::Stre { .. } => i = self.nodes[i].inputs[2], - Kind::Load { .. } => i = self.nodes[i].inputs[1], - _ => break, - } - debug_assert_ne!(i, 0); - } - debug_assert!(i != 0); - ops.push(regalloc2::Operand::reg_fixed_use( - self.rg(i), - regalloc2::PReg::new(r as _, regalloc2::RegClass::Int), - )); - } - } - } - - if let Some(PLoc::Ref(r, _)) = ret { - ops.push(regalloc2::Operand::reg_fixed_use( - self.rg(*node.inputs.last().unwrap()), - regalloc2::PReg::new(r as _, regalloc2::RegClass::Int), - )); - } - - self.add_instr(nid, ops); - - 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, nid); - } - } - } - Kind::Global { .. } => { - let ops = vec![self.drg(nid)]; - self.add_instr(nid, ops); - } - Kind::Stck | Kind::Arg - if node.outputs.iter().all(|&n| { - matches!(self.nodes[n].kind, Kind::Load - if self.nodes[n].ty.loc(self.tys) == Loc::Reg) - || matches!(self.nodes[n].kind, Kind::Stre - if self.nodes[n].ty.loc(self.tys) == Loc::Reg - && self.nodes[n].inputs[1] != nid) - || matches!(self.nodes[n].kind, Kind::BinOp { op: TokenKind::Add } - if self.nodes.is_const(self.nodes[n].inputs[2]) - && self.nodes[n] - .outputs - .iter() - .all(|&n| matches!(self.nodes[n].kind, Kind::Load - if self.nodes[n].ty.loc(self.tys) == Loc::Reg) - || matches!(self.nodes[n].kind, Kind::Stre - if self.nodes[n].ty.loc(self.tys) == Loc::Reg - && self.nodes[n].inputs[1] != nid))) - }) => self.nodes.lock(nid), - Kind::Stck if self.tys.size_of(node.ty) == 0 => self.nodes.lock(nid), - Kind::Stck => { - let ops = vec![self.drg(nid)]; - self.add_instr(nid, ops); - } - Kind::End | - Kind::Phi | Kind::Arg | Kind::Mem | Kind::Loops => {} - Kind::Load { .. } if node.ty.loc(self.tys) == Loc::Stack => { - self.nodes.lock(nid) - } - Kind::Load { .. } => { - let mut region = node.inputs[1]; - if self.nodes[region].kind == (Kind::BinOp { op: TokenKind::Add }) - && self.nodes.is_const(self.nodes[region].inputs[2]) - && node.ty.loc(self.tys) == Loc::Reg - { - region = self.nodes[region].inputs[1] - } - let ops = match self.nodes[region].kind { - Kind::Stck => vec![self.drg(nid)], - _ => vec![self.drg(nid), self.urg(region)], - }; - self.add_instr(nid, ops); - } - Kind::Stre if node.inputs[1] == VOID => self.nodes.lock(nid), - Kind::Stre => { - let mut region = node.inputs[2]; - if self.nodes[region].kind == (Kind::BinOp { op: TokenKind::Add }) - && self.nodes.is_const(self.nodes[region].inputs[2]) - && node.ty.loc(self.tys) == Loc::Reg - { - region = self.nodes[region].inputs[1] - } - let ops = match self.nodes[region].kind { - _ if node.ty.loc(self.tys) == Loc::Stack => { - if self.nodes[node.inputs[1]].kind == Kind::Arg { - vec![self.urg(region), self.urg(node.inputs[1])] - } else { - vec![self.urg(region), self.urg(self.nodes[node.inputs[1]].inputs[1])] - } - } - Kind::Stck => vec![self.urg(node.inputs[1])], - _ => vec![self.urg(region), self.urg(node.inputs[1])], - }; - self.add_instr(nid, ops); + Kind::BinOp { op } => { + op.cond_op(node.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) + || (matches!(self[n].kind, Kind::BinOp { op: TokenKind::Add }) + && self.is_never_used(n, tys)) + }), + Kind::Load { .. } => node.ty.loc(tys) == Loc::Stack, + _ => false, } } - - fn bridge(&mut self, pred: u16, succ: u16) { - if self.nodes[pred].ralloc_backref == u16::MAX - || self.nodes[succ].ralloc_backref == u16::MAX - { - return; - } - self.blocks[self.nodes[pred].ralloc_backref as usize] - .succs - .push(regalloc2::Block::new(self.nodes[succ].ralloc_backref as usize)); - self.blocks[self.nodes[succ].ralloc_backref as usize] - .preds - .push(regalloc2::Block::new(self.nodes[pred].ralloc_backref as usize)); - } - - 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); - } } -impl regalloc2::Function for Function<'_> { - fn num_insts(&self) -> usize { - self.instrs.len() - } - - fn num_blocks(&self) -> usize { - self.blocks.len() - } - - fn entry_block(&self) -> regalloc2::Block { - regalloc2::Block(0) - } - - fn block_insns(&self, block: regalloc2::Block) -> regalloc2::InstRange { - self.blocks[block.index()].instrs - } - - fn block_succs(&self, block: regalloc2::Block) -> &[regalloc2::Block] { - &self.blocks[block.index()].succs - } - - fn block_preds(&self, block: regalloc2::Block) -> &[regalloc2::Block] { - &self.blocks[block.index()].preds - } - - fn block_params(&self, block: regalloc2::Block) -> &[regalloc2::VReg] { - &self.blocks[block.index()].params - } - - fn is_ret(&self, insn: regalloc2::Inst) -> bool { - matches!(self.nodes[self.instrs[insn.index()].nid].kind, Kind::Return | Kind::Die) - } - - fn is_branch(&self, insn: regalloc2::Inst) -> bool { - matches!( - self.nodes[self.instrs[insn.index()].nid].kind, - Kind::If | Kind::Then | Kind::Else | Kind::Entry | Kind::Loop | Kind::Region - ) - } - - fn branch_blockparams( - &self, - block: regalloc2::Block, - _insn: regalloc2::Inst, - _succ_idx: usize, - ) -> &[regalloc2::VReg] { - debug_assert!( - self.blocks[block.index()].succs.len() == 1 - || self.blocks[block.index()].branch_blockparams.is_empty() - ); - - &self.blocks[block.index()].branch_blockparams - } - - fn inst_operands(&self, insn: regalloc2::Inst) -> &[regalloc2::Operand] { - &self.instrs[insn.index()].ops - } - - fn inst_clobbers(&self, insn: regalloc2::Inst) -> regalloc2::PRegSet { - let node = &self.nodes[self.instrs[insn.index()].nid]; - if matches!(node.kind, Kind::Call { .. }) { - let mut set = regalloc2::PRegSet::default(); - let returns = self.tys.parama(node.ty).0.is_some(); - for i in 1 + returns as usize..13 { - set.add(regalloc2::PReg::new(i, regalloc2::RegClass::Int)); - } - set - } else { - regalloc2::PRegSet::default() - } - } - - fn num_vregs(&self) -> usize { - self.nodes.values.len() - } - - fn spillslot_size(&self, regclass: regalloc2::RegClass) -> usize { - match regclass { - regalloc2::RegClass::Int => 1, - regalloc2::RegClass::Float => unreachable!(), - regalloc2::RegClass::Vector => unreachable!(), - } +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 } } impl TokenKind { - pub fn cmp_against(self) -> Option { + fn cmp_against(self) -> Option { Some(match self { TokenKind::Le | TokenKind::Gt => 1, TokenKind::Ne | TokenKind::Eq => 0, @@ -1241,7 +381,7 @@ impl TokenKind { }) } - pub fn float_cmp(self, ty: ty::Id) -> Option EncodedInstr> { + fn float_cmp(self, ty: ty::Id) -> Option EncodedInstr> { if !ty.is_float() { return None; } @@ -1356,11 +496,12 @@ impl TokenKind { Some(ops[size.ilog2() as usize]) } - pub fn unop(&self, dst: ty::Id, src: ty::Id) -> Option EncodedInstr> { + fn unop(&self, dst: ty::Id, src: ty::Id) -> Option EncodedInstr> { let src_idx = src.simple_size().unwrap().ilog2() as usize - 2; Some(match self { Self::Sub => instrs::neg, Self::Float if dst.is_float() && src.is_integer() => { + debug_assert_eq!(dst.simple_size(), src.simple_size()); [instrs::itf32, instrs::itf64][src_idx] } Self::Number if src.is_float() && dst.is_integer() => { @@ -1379,7 +520,7 @@ fn emit(out: &mut Vec, (len, instr): EncodedInstr) { out.extend_from_slice(&instr[..len]); } -pub fn binary_prelude(to: &mut Vec) { +fn binary_prelude(to: &mut Vec) { emit(to, instrs::jal(reg::RET_ADDR, reg::ZERO, 0)); emit(to, instrs::tx()); } @@ -1488,6 +629,7 @@ pub struct Comptime { pub vm: hbvm::Vm, stack: Box<[u8; VM_STACK_SIZE]>, pub code: Vec, + depth: usize, } impl Comptime { @@ -1531,6 +673,19 @@ impl Comptime { pub fn clear(&mut self) { self.code.clear(); } + + #[must_use] + pub fn active(&self) -> bool { + self.depth != 0 + } + + pub fn activate(&mut self) { + self.depth += 1; + } + + pub fn deactivate(&mut self) { + self.depth -= 1; + } } impl Default for Comptime { @@ -1539,7 +694,7 @@ impl Default for Comptime { let mut vm = hbvm::Vm::default(); let ptr = unsafe { stack.as_mut_ptr().cast::().add(VM_STACK_SIZE) as u64 }; vm.write_reg(reg::STACK_PTR, ptr); - Self { vm, stack: unsafe { stack.assume_init() }, code: Default::default() } + Self { vm, stack: unsafe { stack.assume_init() }, code: Default::default(), depth: 0 } } } @@ -1595,7 +750,8 @@ pub fn test_run_vm(out: &[u8], output: &mut String) { unsafe { alloc::alloc::dealloc(ptr as *mut u8, layout) }; } 3 => vm.write_reg(1, 42), - unknown => unreachable!("unknown ecall: {unknown:?}"), + 8 => {} + unknown => writeln!(output, "unknown ecall: {unknown:?}").unwrap(), }, Ok(hbvm::VmRunOk::Timer) => { writeln!(output, "timed out").unwrap(); diff --git a/lang/src/son/hbvm/my_regalloc.rs b/lang/src/son/hbvm/my_regalloc.rs new file mode 100644 index 0000000..6b70ae3 --- /dev/null +++ b/lang/src/son/hbvm/my_regalloc.rs @@ -0,0 +1,903 @@ +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, + 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) { + 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) { + 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 { + 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) { + 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, + pub node_to_reg: Vec, + use_buf: Vec, + phi_input_buf: Vec, + dfs_buf: Vec, + dfs_seem: BitSet, +} + +pub struct Bundle { + taken: Vec, +} + +impl Bundle { + fn new(size: usize) -> Self { + Self { taken: vec![false; size] } + } + + fn add(&mut self, range: Range) { + 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, + pub instrs: Vec, +} + +pub struct Block { + pub range: Range, + pub entry: Nid, +} diff --git a/lang/src/son/hbvm/their_regalloc.rs b/lang/src/son/hbvm/their_regalloc.rs new file mode 100644 index 0000000..1fee4e6 --- /dev/null +++ b/lang/src/son/hbvm/their_regalloc.rs @@ -0,0 +1,1007 @@ +use { + super::{HbvmBackend, Nid, Nodes}, + crate::{ + lexer::TokenKind, + parser, reg, + son::{debug_assert_matches, Kind, ARG_START, MEM, NEVER, VOID}, + ty::{self, Arg, Loc}, + utils::{BitSet, Vc}, + HashMap, Offset, PLoc, Reloc, Sig, TypedReloc, Types, + }, + alloc::{borrow::ToOwned, vec::Vec}, + core::mem, + hbbytecode::{self as instrs}, +}; + +pub struct Regalloc { + env: regalloc2::MachineEnv, + ctx: regalloc2::Ctx, +} + +impl Default for Regalloc { + fn default() -> Self { + Self { + env: regalloc2::MachineEnv { + preferred_regs_by_class: [ + (1..13).map(|i| regalloc2::PReg::new(i, regalloc2::RegClass::Int)).collect(), + vec![], + vec![], + ], + non_preferred_regs_by_class: [ + (13..64).map(|i| regalloc2::PReg::new(i, regalloc2::RegClass::Int)).collect(), + vec![], + vec![], + ], + scratch_by_class: Default::default(), + fixed_stack_slots: Default::default(), + }, + ctx: Default::default(), + } + } +} + +impl HbvmBackend { + pub fn emit_body_code( + &mut self, + nodes: &mut Nodes, + sig: Sig, + tys: &Types, + files: &[parser::Ast], + ) -> (usize, bool) { + let mut ralloc = mem::take(&mut self.ralloc); + + let fuc = Function::new(nodes, tys, sig); + log::info!("{:?}", fuc); + if !fuc.tail { + mem::swap( + &mut ralloc.env.preferred_regs_by_class, + &mut ralloc.env.non_preferred_regs_by_class, + ); + }; + + let options = regalloc2::RegallocOptions { + verbose_log: false, + validate_ssa: cfg!(debug_assertions), + algorithm: regalloc2::Algorithm::Ion, + }; + regalloc2::run_with_ctx(&fuc, &ralloc.env, &options, &mut ralloc.ctx).unwrap_or_else( + |err| { + if let regalloc2::RegAllocError::SSA(vreg, inst) = err { + fuc.nodes[vreg.vreg() as Nid].lock_rc = Nid::MAX; + fuc.nodes[fuc.instrs[inst.index()].nid].lock_rc = Nid::MAX - 1; + } + fuc.nodes.graphviz_in_browser(ty::Display::new(tys, files, ty::Id::VOID)); + panic!("{err}") + }, + ); + + if !fuc.tail { + mem::swap( + &mut ralloc.env.preferred_regs_by_class, + &mut ralloc.env.non_preferred_regs_by_class, + ); + }; + + let mut saved_regs = HashMap::::default(); + let mut atr = |allc: regalloc2::Allocation| { + debug_assert!(allc.is_reg()); + let hvenc = regalloc2::PReg::from_index(allc.index()).hw_enc() as u8; + if hvenc <= 12 { + return hvenc; + } + let would_insert = saved_regs.len() as u8 + reg::RET_ADDR + 1; + *saved_regs.entry(hvenc).or_insert(would_insert) + }; + + '_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 (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(..) | PLoc::Ref(..) => 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 _)); + } + } + + for (i, block) in fuc.blocks.iter().enumerate() { + let blk = regalloc2::Block(i as _); + self.offsets[block.nid as usize] = self.code.len() as _; + for instr_or_edit in ralloc.ctx.output.block_insts_and_edits(&fuc, blk) { + let inst = match instr_or_edit { + regalloc2::InstOrEdit::Inst(inst) => inst, + regalloc2::InstOrEdit::Edit(®alloc2::Edit::Move { from, to }) => { + self.emit(instrs::cp(atr(to), atr(from))); + continue; + } + }; + + let nid = fuc.instrs[inst.index()].nid; + if nid == NEVER { + continue; + }; + let allocs = ralloc.ctx.output.inst_allocs(inst); + let node = &fuc.nodes[nid]; + let backref = fuc.backrefs[nid as usize]; + + let mut extend = |base: ty::Id, dest: ty::Id, from: usize, to: usize| { + 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(allocs[to]), atr(allocs[from])) + } + _ => { + let mask = (1u64 << (bsize * 8)) - 1; + instrs::andi(atr(allocs[to]), atr(allocs[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] = allocs else { unreachable!() }; + let &[_, lh, rh] = fuc.nodes[cnd].inputs.as_slice() else { + unreachable!() + }; + + self.emit(extend(fuc.nodes[lh].ty, fuc.nodes[lh].ty.extend(), 0, 0)); + self.emit(extend(fuc.nodes[rh].ty, fuc.nodes[rh].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(allocs[0]), reg::ZERO, 0)); + } + } + Kind::Loop | Kind::Region => { + if backref as usize != i + 1 { + let rel = Reloc::new(self.code.len(), 1, 4); + self.jump_relocs.push((nid, rel)); + self.emit(instrs::jmp(0)); + } + } + Kind::Return => { + match retl { + Some(PLoc::Reg(r, size)) if sig.ret.loc(tys) == Loc::Stack => { + self.emit(instrs::ld(r, atr(allocs[0]), 0, size)) + } + None | Some(PLoc::Reg(..)) => {} + Some(PLoc::WideReg(r, size)) => { + self.emit(instrs::ld(r, atr(allocs[0]), 0, size)) + } + Some(PLoc::Ref(_, size)) => { + let [src, dst] = [atr(allocs[0]), atr(allocs[1])]; + 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.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(allocs[0]), + (f64::from_bits(value as _) as f32).to_bits(), + ), + ty::Id::F64 => instrs::li64(atr(allocs[0]), value as _), + _ => unreachable!(), + }); + } + Kind::CInt { value } => self.emit(match tys.size_of(node.ty) { + 1 => instrs::li8(atr(allocs[0]), value as _), + 2 => instrs::li16(atr(allocs[0]), value as _), + 4 => instrs::li32(atr(allocs[0]), value as _), + _ => instrs::li64(atr(allocs[0]), value as _), + }), + Kind::UnOp { op } => { + let op = op + .unop(node.ty, fuc.nodes[node.inputs[1]].ty) + .expect("TODO: unary operator not supported"); + let &[dst, oper] = allocs else { unreachable!() }; + self.emit(op(atr(dst), atr(oper))); + } + Kind::BinOp { .. } if node.lock_rc != 0 => {} + Kind::BinOp { op } => { + let &[.., lh, rh] = node.inputs.as_slice() else { unreachable!() }; + + if let Kind::CInt { value } = fuc.nodes[rh].kind + && fuc.nodes[rh].lock_rc != 0 + && let Some(op) = op.imm_binop(node.ty) + { + let &[dst, lhs] = allocs else { unreachable!() }; + self.emit(op(atr(dst), atr(lhs), value as _)); + } else if let Some(op) = + op.binop(node.ty).or(op.float_cmp(fuc.nodes[lh].ty)) + { + let &[dst, lhs, rhs] = allocs else { unreachable!() }; + self.emit(op(atr(dst), atr(lhs), atr(rhs))); + } else if let Some(against) = op.cmp_against() { + let op_ty = fuc.nodes[rh].ty; + + self.emit(extend(fuc.nodes[lh].ty, fuc.nodes[lh].ty.extend(), 0, 0)); + self.emit(extend(fuc.nodes[rh].ty, fuc.nodes[rh].ty.extend(), 1, 1)); + let &[dst, lhs, rhs] = allocs else { unreachable!() }; + + 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(dst), atr(lhs), atr(rhs))); + self.emit(instrs::not(atr(dst), atr(dst))); + } else { + let op_fn = + if op_ty.is_signed() { instrs::cmps } else { instrs::cmpu }; + self.emit(op_fn(atr(dst), atr(lhs), atr(rhs))); + self.emit(instrs::cmpui(atr(dst), atr(dst), against)); + if matches!(op, TokenKind::Eq | TokenKind::Lt | TokenKind::Gt) { + self.emit(instrs::not(atr(dst), atr(dst))); + } + } + } else { + todo!("unhandled operator: {op}"); + } + } + Kind::Call { args, func } => { + let (ret, mut parama) = tys.parama(node.ty); + let has_ret = ret.is_some() as usize; + let mut args = args.args(); + let mut allocs = allocs[has_ret..].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 &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(..) | PLoc::Reg(..) => continue, + }; + if size > 8 { + allocs.next().unwrap(); + } + 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)); + } + + if let Some(PLoc::WideReg(r, size)) = ret { + 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)); + } + if let Some(PLoc::Reg(r, size)) = ret + && 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)); + } + } + 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(allocs[0]), 0, 0)); + } + Kind::Stck => { + let base = reg::STACK_PTR; + let offset = self.offsets[nid as usize]; + self.emit(instrs::addi64(atr(allocs[0]), 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(allocs[1]), offset), + }; + self.emit(instrs::ld(atr(allocs[0]), 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 (base, offset, src) = match nd.kind { + Kind::Stck if node.ty.loc(tys) == Loc::Reg => { + (reg::STACK_PTR, self.offsets[region as usize] + offset, allocs[0]) + } + _ => (atr(allocs[0]), offset, allocs[1]), + }; + + 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::Start + | Kind::Assert { .. } + | Kind::Entry + | Kind::Mem + | Kind::End + | Kind::Loops + | Kind::Then + | Kind::Else + | Kind::Phi + | Kind::Arg => unreachable!(), + } + } + } + + self.ralloc = ralloc; + + (saved_regs.len(), fuc.tail) + } +} + +#[derive(Debug)] +struct Block { + nid: Nid, + preds: Vec, + succs: Vec, + instrs: regalloc2::InstRange, + params: Vec, + branch_blockparams: Vec, +} + +#[derive(Debug)] +struct Instr { + nid: Nid, + ops: Vec, +} + +pub struct Function<'a> { + sig: Sig, + nodes: &'a mut Nodes, + tys: &'a Types, + tail: bool, + visited: BitSet, + backrefs: Vec, + blocks: Vec, + instrs: Vec, +} + +impl core::fmt::Debug for Function<'_> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + for (i, block) in self.blocks.iter().enumerate() { + writeln!(f, "sb{i}{:?}-{:?}:", block.params, block.preds)?; + + for inst in block.instrs.iter() { + let instr = &self.instrs[inst.index()]; + writeln!(f, "{}: i{:?}:{:?}", inst.index(), self.nodes[instr.nid].kind, instr.ops)?; + } + + writeln!(f, "eb{i}{:?}-{:?}:", block.branch_blockparams, block.succs)?; + } + Ok(()) + } +} + +impl<'a> Function<'a> { + fn new(nodes: &'a mut Nodes, tys: &'a Types, sig: Sig) -> Self { + let mut s = Self { + tys, + sig, + tail: true, + visited: Default::default(), + backrefs: vec![u16::MAX; nodes.values.len()], + blocks: Default::default(), + instrs: Default::default(), + nodes, + }; + s.visited.clear(s.nodes.values.len()); + s.emit_node(VOID, VOID); + s.add_block(0); + s.blocks.pop(); + s + } + + fn add_block(&mut self, nid: Nid) -> u16 { + if let Some(prev) = self.blocks.last_mut() { + prev.instrs = regalloc2::InstRange::new( + prev.instrs.first(), + regalloc2::Inst::new(self.instrs.len()), + ); + } + + self.blocks.push(Block { + nid, + preds: Default::default(), + succs: Default::default(), + instrs: regalloc2::InstRange::new( + regalloc2::Inst::new(self.instrs.len()), + regalloc2::Inst::new(self.instrs.len() + 1), + ), + params: Default::default(), + branch_blockparams: Default::default(), + }); + self.blocks.len() as u16 - 1 + } + + fn add_instr(&mut self, nid: Nid, ops: Vec) { + self.instrs.push(Instr { nid, ops }); + } + + fn urg(&mut self, nid: Nid) -> regalloc2::Operand { + regalloc2::Operand::reg_use(self.rg(nid)) + } + + fn drg(&mut self, nid: Nid) -> regalloc2::Operand { + regalloc2::Operand::reg_def(self.rg(nid)) + } + + fn rg(&self, nid: Nid) -> regalloc2::VReg { + debug_assert!( + !self.nodes.is_cfg(nid) || matches!(self.nodes[nid].kind, Kind::Call { .. }), + "{:?}", + self.nodes[nid] + ); + debug_assert_eq!(self.nodes[nid].lock_rc, 0, "{nid} {:?}", self.nodes[nid]); + debug_assert!(self.nodes[nid].kind != Kind::Phi || self.nodes[nid].ty != ty::Id::VOID); + regalloc2::VReg::new(nid as _, regalloc2::RegClass::Int) + } + + fn emit_node(&mut self, nid: Nid, prev: Nid) { + if matches!(self.nodes[nid].kind, Kind::Region | Kind::Loop) { + let prev_bref = self.backrefs[prev as usize]; + let node = self.nodes[nid].clone(); + + let idx = 1 + node.inputs.iter().position(|&i| i == prev).unwrap(); + + for ph in node.outputs { + if self.nodes[ph].kind != Kind::Phi || self.nodes[ph].ty == ty::Id::VOID { + continue; + } + + let rg = self.rg(self.nodes[ph].inputs[idx]); + self.blocks[prev_bref as usize].branch_blockparams.push(rg); + } + + self.add_instr(nid, vec![]); + + match (self.nodes[nid].kind, self.visited.set(nid)) { + (Kind::Loop, false) => { + for i in node.inputs { + self.bridge(i, nid); + } + return; + } + (Kind::Region, true) => 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.emit_node(node.outputs[0], VOID) + } + Kind::If => { + self.backrefs[nid as usize] = self.backrefs[prev as usize]; + + 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) + { + if swapped { + mem::swap(&mut then, &mut else_); + } + let &[_, lhs, rhs] = self.nodes[cond].inputs.as_slice() else { unreachable!() }; + let ops = vec![self.urg(lhs), self.urg(rhs)]; + self.add_instr(nid, ops); + } else { + mem::swap(&mut then, &mut else_); + let ops = vec![self.urg(cond)]; + self.add_instr(nid, ops); + } + + self.emit_node(then, nid); + self.emit_node(else_, nid); + } + Kind::Region | Kind::Loop => { + self.backrefs[nid as usize] = self.add_block(nid); + if node.kind == Kind::Region { + for i in node.inputs { + self.bridge(i, nid); + } + } + let mut block = vec![]; + for ph in node.outputs.clone() { + if self.nodes[ph].kind != Kind::Phi || self.nodes[ph].ty == ty::Id::VOID { + continue; + } + block.push(self.rg(ph)); + } + self.blocks[self.backrefs[nid as usize] as usize].params = block; + self.reschedule_block(nid, &mut node.outputs); + for o in node.outputs.into_iter().rev() { + self.emit_node(o, nid); + } + } + Kind::Return => { + let ops = match self.tys.parama(self.sig.ret).0 { + None => vec![], + Some(PLoc::Reg(..)) if self.sig.ret.loc(self.tys) == Loc::Stack => { + vec![self.urg(self.nodes[node.inputs[1]].inputs[1])] + } + Some(PLoc::Reg(r, ..)) => { + vec![regalloc2::Operand::reg_fixed_use( + self.rg(node.inputs[1]), + regalloc2::PReg::new(r as _, regalloc2::RegClass::Int), + )] + } + Some(PLoc::WideReg(..)) => { + vec![self.urg(self.nodes[node.inputs[1]].inputs[1])] + } + Some(PLoc::Ref(..)) => { + vec![self.urg(self.nodes[node.inputs[1]].inputs[1]), self.urg(MEM)] + } + }; + + self.add_instr(nid, ops); + self.emit_node(node.outputs[0], nid); + } + Kind::Die => { + self.add_instr(nid, vec![]); + self.emit_node(node.outputs[0], nid); + } + Kind::CInt { .. } => { + let ops = vec![self.drg(nid)]; + self.add_instr(nid, ops); + } + Kind::Entry => { + self.backrefs[nid as usize] = self.add_block(nid); + + 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(PLoc::Reg(r, _) | PLoc::WideReg(r, _) | PLoc::Ref(r, _)) => { + self.add_instr(NEVER, vec![regalloc2::Operand::reg_fixed_def( + self.rg(arg), + regalloc2::PReg::new(r as _, regalloc2::RegClass::Int), + )]); + } + } + } + + if let Some(PLoc::Ref(r, ..)) = ret { + self.add_instr(NEVER, vec![regalloc2::Operand::reg_fixed_def( + self.rg(MEM), + regalloc2::PReg::new(r as _, regalloc2::RegClass::Int), + )]); + } + + self.reschedule_block(nid, &mut node.outputs); + for o in node.outputs.into_iter().rev() { + self.emit_node(o, nid); + } + } + Kind::Then | Kind::Else => { + self.backrefs[nid as usize] = self.add_block(nid); + self.bridge(prev, nid); + self.reschedule_block(nid, &mut node.outputs); + for o in node.outputs.into_iter().rev() { + self.emit_node(o, nid); + } + } + Kind::BinOp { .. } => { + let &[_, lhs, rhs] = node.inputs.as_slice() else { unreachable!() }; + + let ops = if let Kind::CInt { .. } = self.nodes[rhs].kind + && self.nodes[rhs].lock_rc != 0 + { + vec![self.drg(nid), self.urg(lhs)] + } else { + vec![self.drg(nid), self.urg(lhs), self.urg(rhs)] + }; + self.add_instr(nid, ops); + } + Kind::UnOp { .. } => { + let ops = vec![self.drg(nid), self.urg(node.inputs[1])]; + self.add_instr(nid, ops); + } + Kind::Call { args, func } => { + self.tail &= func == ty::ECA; + self.backrefs[nid as usize] = self.backrefs[prev as usize]; + let mut ops = vec![]; + + let (ret, mut parama) = self.tys.parama(node.ty); + if ret.is_some() { + ops.push(regalloc2::Operand::reg_fixed_def( + self.rg(nid), + regalloc2::PReg::new(1, regalloc2::RegClass::Int), + )); + } + + let mut tys = args.args(); + let mut args = node.inputs[1..].iter(); + while let Some(ty) = tys.next_value(self.tys) { + let mut i = *args.next().unwrap(); + let Some(loc) = parama.next(ty, self.tys) else { continue }; + + match loc { + PLoc::Reg(r, _) if ty.loc(self.tys) == Loc::Reg => { + ops.push(regalloc2::Operand::reg_fixed_use( + self.rg(i), + regalloc2::PReg::new(r as _, regalloc2::RegClass::Int), + )); + } + PLoc::WideReg(r, size) | PLoc::Reg(r, size) => { + loop { + match self.nodes[i].kind { + Kind::Stre { .. } => i = self.nodes[i].inputs[2], + Kind::Load { .. } => i = self.nodes[i].inputs[1], + _ => break, + } + debug_assert_ne!(i, 0); + } + debug_assert!(i != 0); + ops.push(regalloc2::Operand::reg_fixed_use( + self.rg(i), + regalloc2::PReg::new(r as _, regalloc2::RegClass::Int), + )); + if size > 8 { + ops.push(regalloc2::Operand::reg_fixed_use( + self.rg(i), + regalloc2::PReg::new((r + 1) as _, regalloc2::RegClass::Int), + )); + } + } + PLoc::Ref(r, _) => { + loop { + match self.nodes[i].kind { + Kind::Stre { .. } => i = self.nodes[i].inputs[2], + Kind::Load { .. } => i = self.nodes[i].inputs[1], + _ => break, + } + debug_assert_ne!(i, 0); + } + debug_assert!(i != 0); + ops.push(regalloc2::Operand::reg_fixed_use( + self.rg(i), + regalloc2::PReg::new(r as _, regalloc2::RegClass::Int), + )); + } + } + } + + if let Some(PLoc::Ref(r, _)) = ret { + ops.push(regalloc2::Operand::reg_fixed_use( + self.rg(*node.inputs.last().unwrap()), + regalloc2::PReg::new(r as _, regalloc2::RegClass::Int), + )); + } + + self.add_instr(nid, ops); + + 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, nid); + } + } + } + Kind::Global { .. } => { + let ops = vec![self.drg(nid)]; + self.add_instr(nid, ops); + } + Kind::Stck => { + let ops = vec![self.drg(nid)]; + self.add_instr(nid, ops); + } + Kind::Assert { .. } => unreachable!(), + Kind::End | Kind::Phi | Kind::Arg | Kind::Mem | Kind::Loops => {} + Kind::Load { .. } => { + let mut region = node.inputs[1]; + if self.nodes[region].kind == (Kind::BinOp { op: TokenKind::Add }) + && self.nodes.is_const(self.nodes[region].inputs[2]) + && node.ty.loc(self.tys) == Loc::Reg + { + region = self.nodes[region].inputs[1] + } + let ops = match self.nodes[region].kind { + Kind::Stck => vec![self.drg(nid)], + _ => vec![self.drg(nid), self.urg(region)], + }; + self.add_instr(nid, ops); + } + Kind::Stre => { + debug_assert_ne!(self.tys.size_of(node.ty), 0); + let mut region = node.inputs[2]; + if self.nodes[region].kind == (Kind::BinOp { op: TokenKind::Add }) + && self.nodes.is_const(self.nodes[region].inputs[2]) + && node.ty.loc(self.tys) == Loc::Reg + { + region = self.nodes[region].inputs[1] + } + let ops = match self.nodes[region].kind { + _ if node.ty.loc(self.tys) == Loc::Stack => { + if self.nodes[node.inputs[1]].kind == Kind::Arg { + vec![self.urg(region), self.urg(node.inputs[1])] + } else { + vec![self.urg(region), self.urg(self.nodes[node.inputs[1]].inputs[1])] + } + } + Kind::Stck => vec![self.urg(node.inputs[1])], + _ => vec![self.urg(region), self.urg(node.inputs[1])], + }; + self.add_instr(nid, ops); + } + } + } + + fn bridge(&mut self, pred: u16, succ: u16) { + if self.backrefs[pred as usize] == u16::MAX || self.backrefs[succ as usize] == u16::MAX { + return; + } + self.blocks[self.backrefs[pred as usize] as usize] + .succs + .push(regalloc2::Block::new(self.backrefs[succ as usize] as usize)); + self.blocks[self.backrefs[succ as usize] as usize] + .preds + .push(regalloc2::Block::new(self.backrefs[pred as usize] as usize)); + } + + 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); + } +} + +impl regalloc2::Function for Function<'_> { + fn num_insts(&self) -> usize { + self.instrs.len() + } + + fn num_blocks(&self) -> usize { + self.blocks.len() + } + + fn entry_block(&self) -> regalloc2::Block { + regalloc2::Block(0) + } + + fn block_insns(&self, block: regalloc2::Block) -> regalloc2::InstRange { + self.blocks[block.index()].instrs + } + + fn block_succs(&self, block: regalloc2::Block) -> &[regalloc2::Block] { + &self.blocks[block.index()].succs + } + + fn block_preds(&self, block: regalloc2::Block) -> &[regalloc2::Block] { + &self.blocks[block.index()].preds + } + + fn block_params(&self, block: regalloc2::Block) -> &[regalloc2::VReg] { + &self.blocks[block.index()].params + } + + fn is_ret(&self, insn: regalloc2::Inst) -> bool { + matches!(self.nodes[self.instrs[insn.index()].nid].kind, Kind::Return | Kind::Die) + } + + fn is_branch(&self, insn: regalloc2::Inst) -> bool { + matches!( + self.nodes[self.instrs[insn.index()].nid].kind, + Kind::If | Kind::Then | Kind::Else | Kind::Entry | Kind::Loop | Kind::Region + ) + } + + fn branch_blockparams( + &self, + block: regalloc2::Block, + _insn: regalloc2::Inst, + _succ_idx: usize, + ) -> &[regalloc2::VReg] { + debug_assert!( + self.blocks[block.index()].succs.len() == 1 + || self.blocks[block.index()].branch_blockparams.is_empty() + ); + + &self.blocks[block.index()].branch_blockparams + } + + fn inst_operands(&self, insn: regalloc2::Inst) -> &[regalloc2::Operand] { + &self.instrs[insn.index()].ops + } + + fn inst_clobbers(&self, insn: regalloc2::Inst) -> regalloc2::PRegSet { + let node = &self.nodes[self.instrs[insn.index()].nid]; + if matches!(node.kind, Kind::Call { .. }) { + let mut set = regalloc2::PRegSet::default(); + let returns = self.tys.parama(node.ty).0.is_some(); + for i in 1 + returns as usize..13 { + set.add(regalloc2::PReg::new(i, regalloc2::RegClass::Int)); + } + set + } else { + regalloc2::PRegSet::default() + } + } + + fn num_vregs(&self) -> usize { + self.nodes.values.len() + } + + fn spillslot_size(&self, regclass: regalloc2::RegClass) -> usize { + match regclass { + regalloc2::RegClass::Int => 1, + regalloc2::RegClass::Float => unreachable!(), + regalloc2::RegClass::Vector => unreachable!(), + } + } +} diff --git a/lang/src/utils.rs b/lang/src/utils.rs index ca781bf..b0d3477 100644 --- a/lang/src/utils.rs +++ b/lang/src/utils.rs @@ -62,6 +62,12 @@ impl BitSet { const INLINE_ELEMS: usize = Self::UNIT - 1; const UNIT: usize = core::mem::size_of::() * 8; + pub fn with_capacity(len: usize) -> Self { + let mut s = Self::default(); + s.reserve(len); + s + } + fn is_inline(&self) -> bool { unsafe { self.inline & Self::FLAG != 0 } } diff --git a/lang/tests/son_tests_advanced_floating_point_arithmetic.txt b/lang/tests/son_tests_advanced_floating_point_arithmetic.txt new file mode 100644 index 0000000..6f57016 --- /dev/null +++ b/lang/tests/son_tests_advanced_floating_point_arithmetic.txt @@ -0,0 +1,44 @@ +main: + ADDI64 r254, r254, -24d + ST r31, r254, 0a, 24h + LI32 r32, 1148846080w + CP r2, r32 + JAL r31, r0, :sin + FMUL32 r33, r1, r32 + FTI32 r1, r33, 1b + 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 + JALA r0, r31, 0a +code size: 1303 +ret: 826 +status: Ok(()) diff --git a/lang/tests/son_tests_arrays.txt b/lang/tests/son_tests_arrays.txt index eaad767..b8c50e3 100644 --- a/lang/tests/son_tests_arrays.txt +++ b/lang/tests/son_tests_arrays.txt @@ -1,17 +1,17 @@ main: - ADDI64 r254, r254, -40d - ST r31, r254, 24a, 16h + ADDI64 r254, r254, -56d + ST r31, r254, 24a, 32h LI64 r32, 1d ADDI64 r2, r254, 0d ST r32, r254, 0a, 8h - LI64 r5, 2d - ST r5, r254, 8a, 8h - LI64 r8, 4d - ST r8, r254, 16a, 8h + LI64 r33, 2d + ST r33, r254, 8a, 8h + LI64 r34, 4d + ST r34, r254, 16a, 8h JAL r31, r0, :pass ADD64 r1, r1, r32 - LD r31, r254, 24a, 16h - ADDI64 r254, r254, 40d + LD r31, r254, 24a, 32h + ADDI64 r254, r254, 56d JALA r0, r31, 0a pass: LD r4, r2, 8a, 8h diff --git a/lang/tests/son_tests_c_strings.txt b/lang/tests/son_tests_c_strings.txt index 782ffb0..0aa31af 100644 --- a/lang/tests/son_tests_c_strings.txt +++ b/lang/tests/son_tests_c_strings.txt @@ -6,8 +6,7 @@ main: CP r32, r1 LRA r2, r0, :"fff\0" JAL r31, r0, :str_len - CP r10, r32 - ADD64 r1, r1, r10 + ADD64 r1, r1, r32 LD r31, r254, 0a, 16h ADDI64 r254, r254, 16d JALA r0, r31, 0a @@ -23,6 +22,6 @@ str_len: ADDI64 r1, r1, 1d JMP :2 1: JALA r0, r31, 0a -code size: 219 +code size: 216 ret: 16 status: Ok(()) diff --git a/lang/tests/son_tests_conditional_stores.txt b/lang/tests/son_tests_conditional_stores.txt index b7fc3d2..a9a2494 100644 --- a/lang/tests/son_tests_conditional_stores.txt +++ b/lang/tests/son_tests_conditional_stores.txt @@ -2,18 +2,18 @@ cond: LI64 r1, 0d JALA r0, r31, 0a main: - ADDI64 r254, r254, -8d - ST r31, r254, 0a, 8h + ADDI64 r254, r254, -24d + ST r31, r254, 0a, 24h JAL r31, r0, :cond - LI64 r5, 0d - CP r7, r5 - JNE r1, r7, :0 - CP r5, r7 - CP r1, r5 + LI64 r32, 0d + CP r33, r32 + JNE r1, r33, :0 + CP r32, r33 + CP r1, r32 JMP :1 0: LI64 r1, 2d - 1: LD r31, r254, 0a, 8h - ADDI64 r254, r254, 8d + 1: LD r31, r254, 0a, 24h + ADDI64 r254, r254, 24d JALA r0, r31, 0a code size: 134 ret: 0 diff --git a/lang/tests/son_tests_directives.txt b/lang/tests/son_tests_directives.txt index be46acd..9c25941 100644 --- a/lang/tests/son_tests_directives.txt +++ b/lang/tests/son_tests_directives.txt @@ -8,12 +8,13 @@ main: LI64 r6, 6d LI64 r5, 5d LI64 r2, 1d - LD r3, r4, 0a, 16h + CP r3, r4 + LD r3, r3, 0a, 16h ECA LI64 r1, 0d ADDI64 r254, r254, 16d JALA r0, r31, 0a ev: Ecall -code size: 152 +code size: 155 ret: 0 status: Ok(()) diff --git a/lang/tests/son_tests_exhaustive_loop_testing.txt b/lang/tests/son_tests_exhaustive_loop_testing.txt index e113d00..eaf456d 100644 --- a/lang/tests/son_tests_exhaustive_loop_testing.txt +++ b/lang/tests/son_tests_exhaustive_loop_testing.txt @@ -30,50 +30,50 @@ infinite_loop: ADDI64 r254, r254, 24d JALA r0, r31, 0a main: - ADDI64 r254, r254, -56d - ST r31, r254, 0a, 56h + ADDI64 r254, r254, -64d + ST r31, r254, 0a, 64h LI64 r32, 0d CP r2, r32 JAL r31, r0, :multiple_breaks - CP r2, r1 + CP r33, r1 LI64 r1, 3d - JEQ r2, r1, :0 + JEQ r33, r1, :0 LI64 r1, 1d JMP :1 - 0: CP r33, r1 - LI64 r34, 4d - CP r2, r34 + 0: CP r34, r1 + LI64 r35, 4d + CP r2, r35 JAL r31, r0, :multiple_breaks - CP r35, r34 - LI64 r36, 10d - JEQ r1, r36, :2 + CP r36, r35 + LI64 r37, 10d + JEQ r1, r37, :2 LI64 r1, 2d JMP :1 2: CP r2, r32 JAL r31, r0, :state_change_in_break JEQ r1, r32, :3 - CP r1, r33 + CP r1, r34 JMP :1 - 3: CP r2, r35 + 3: CP r2, r36 JAL r31, r0, :state_change_in_break - JEQ r1, r36, :4 - CP r1, r35 + JEQ r1, r37, :4 + CP r1, r36 JMP :1 - 4: CP r2, r36 + 4: CP r2, r37 JAL r31, r0, :continue_and_state_change - JEQ r1, r36, :5 + JEQ r1, r37, :5 LI64 r1, 5d JMP :1 - 5: CP r2, r33 + 5: CP r2, r34 JAL r31, r0, :continue_and_state_change JEQ r1, r32, :6 LI64 r1, 6d JMP :1 - 6: CP r37, r32 + 6: CP r38, r32 JAL r31, r0, :infinite_loop - CP r1, r37 - 1: LD r31, r254, 0a, 56h - ADDI64 r254, r254, 56d + CP r1, r38 + 1: LD r31, r254, 0a, 64h + ADDI64 r254, r254, 64d JALA r0, r31, 0a multiple_breaks: LI64 r6, 3d diff --git a/lang/tests/son_tests_fb_driver.txt b/lang/tests/son_tests_fb_driver.txt index a4bf731..ae33add 100644 --- a/lang/tests/son_tests_fb_driver.txt +++ b/lang/tests/son_tests_fb_driver.txt @@ -6,42 +6,40 @@ check_platform: ADDI64 r254, r254, 8d JALA r0, r31, 0a main: - ADDI64 r254, r254, -48d - ST r31, r254, 0a, 48h + ADDI64 r254, r254, -64d + ST r31, r254, 0a, 64h JAL r31, r0, :check_platform LI64 r32, 0d LI64 r33, 30d LI64 r34, 100d - CP r10, r32 CP r35, r32 CP r36, r32 - 5: JLTU r10, r33, :0 - ADDI64 r35, r35, 1d + CP r37, r32 + 5: JLTU r35, r33, :0 + ADDI64 r36, r36, 1d CP r2, r32 - CP r3, r35 + CP r3, r36 CP r4, r33 JAL r31, r0, :set_pixel - CP r2, r36 - JEQ r1, r2, :1 + JEQ r1, r37, :1 CP r1, r32 JMP :2 - 1: CP r5, r32 - CP r36, r2 - JNE r35, r34, :3 - CP r1, r36 + 1: CP r38, r32 + JNE r36, r34, :3 + CP r1, r37 JMP :2 - 3: CP r1, r36 - CP r10, r5 + 3: CP r1, r37 + CP r35, r38 JMP :4 - 0: CP r1, r36 - CP r5, r32 + 0: CP r1, r37 + CP r38, r32 ADDI64 r1, r1, 1d - ADDI64 r10, r10, 1d - 4: CP r32, r5 - CP r36, r1 + ADDI64 r35, r35, 1d + 4: CP r32, r38 + CP r37, r1 JMP :5 - 2: LD r31, r254, 0a, 48h - ADDI64 r254, r254, 48d + 2: LD r31, r254, 0a, 64h + ADDI64 r254, r254, 64d JALA r0, r31, 0a set_pixel: MUL64 r7, r3, r4 @@ -50,6 +48,6 @@ set_pixel: x86_fb_ptr: LI64 r1, 100d JALA r0, r31, 0a -code size: 336 +code size: 330 ret: 3000 status: Ok(()) diff --git a/lang/tests/son_tests_functions.txt b/lang/tests/son_tests_functions.txt index f5291f3..686e680 100644 --- a/lang/tests/son_tests_functions.txt +++ b/lang/tests/son_tests_functions.txt @@ -12,11 +12,10 @@ main: CP r32, r1 LI64 r2, 20d JAL r31, r0, :add_two - CP r10, r32 - ADD64 r1, r1, r10 + ADD64 r1, r1, r32 LD r31, r254, 0a, 16h ADDI64 r254, r254, 16d JALA r0, r31, 0a -code size: 155 +code size: 152 ret: 33 status: Ok(()) diff --git a/lang/tests/son_tests_generic_functions.txt b/lang/tests/son_tests_generic_functions.txt index eb53bd7..94db254 100644 --- a/lang/tests/son_tests_generic_functions.txt +++ b/lang/tests/son_tests_generic_functions.txt @@ -5,8 +5,8 @@ add: ADD32 r1, r2, r3 JALA r0, r31, 0a main: - ADDI64 r254, r254, -16d - ST r31, r254, 0a, 16h + ADDI64 r254, r254, -24d + ST r31, r254, 0a, 24h LI32 r3, 2w CP r2, r3 JAL r31, r0, :add @@ -14,12 +14,11 @@ main: LI64 r3, 3d LI64 r2, 1d JAL r31, r0, :add - CP r2, r32 - ANDI r11, r2, 4294967295d - SUB64 r1, r11, r1 - LD r31, r254, 0a, 16h - ADDI64 r254, r254, 16d + ANDI r33, r32, 4294967295d + SUB64 r1, r33, r1 + LD r31, r254, 0a, 24h + ADDI64 r254, r254, 24d JALA r0, r31, 0a -code size: 161 +code size: 158 ret: 0 status: Ok(()) diff --git a/lang/tests/son_tests_generic_types.txt b/lang/tests/son_tests_generic_types.txt index 755dcb1..211b4a6 100644 --- a/lang/tests/son_tests_generic_types.txt +++ b/lang/tests/son_tests_generic_types.txt @@ -1,17 +1,17 @@ deinit: - ADDI64 r254, r254, -16d - ST r31, r254, 0a, 16h + ADDI64 r254, r254, -32d + ST r31, r254, 0a, 32h CP r32, r2 - LD r5, r2, 16a, 8h + LD r33, r2, 16a, 8h LI64 r4, 8d - MUL64 r3, r5, r4 - CP r5, r32 - LD r2, r5, 0a, 8h + MUL64 r3, r33, r4 + CP r34, r32 + LD r2, r34, 0a, 8h JAL r31, r0, :free CP r1, r32 JAL r31, r0, :new - LD r31, r254, 0a, 16h - ADDI64 r254, r254, 16d + LD r31, r254, 0a, 32h + ADDI64 r254, r254, 32d JALA r0, r31, 0a free: CP r10, r2 @@ -23,21 +23,21 @@ free: ECA JALA r0, r31, 0a main: - ADDI64 r254, r254, -48d - ST r31, r254, 24a, 24h + ADDI64 r254, r254, -56d + ST r31, r254, 24a, 32h ADDI64 r32, r254, 0d CP r1, r32 JAL r31, r0, :new LI64 r3, 69d CP r2, r32 JAL r31, r0, :push - LD r9, r254, 0a, 8h - LD r33, r9, 0a, 8h + LD r33, r254, 0a, 8h + LD r34, r33, 0a, 8h CP r2, r32 JAL r31, r0, :deinit - CP r1, r33 - LD r31, r254, 24a, 24h - ADDI64 r254, r254, 48d + CP r1, r34 + LD r31, r254, 24a, 32h + ADDI64 r254, r254, 56d JALA r0, r31, 0a malloc: CP r9, r2 @@ -58,69 +58,69 @@ new: ADDI64 r254, r254, 24d JALA r0, r31, 0a push: - ADDI64 r254, r254, -72d - ST r31, r254, 0a, 72h + ADDI64 r254, r254, -192d + ST r31, r254, 0a, 192h CP r32, r3 LI64 r33, 1d - LD r6, r2, 8a, 8h - LD r8, r2, 16a, 8h - CP r34, r2 - JNE r8, r6, :0 - LI64 r35, 0d - JNE r8, r35, :1 - CP r36, r33 + LD r34, r2, 8a, 8h + LD r35, r2, 16a, 8h + CP r36, r2 + JNE r35, r34, :0 + LI64 r37, 0d + JNE r35, r37, :1 + CP r38, r33 JMP :2 - 1: MULI64 r36, r8, 2d - 2: LI64 r37, 8d - MUL64 r2, r36, r37 - CP r3, r37 + 1: MULI64 r38, r35, 2d + 2: LI64 r39, 8d + MUL64 r2, r38, r39 + CP r3, r39 JAL r31, r0, :malloc - CP r38, r1 - CP r39, r34 - ST r36, r39, 16a, 8h + CP r40, r1 LI64 r1, 0d - CP r7, r38 - JNE r7, r1, :3 + CP r41, r40 + JNE r41, r1, :3 JMP :4 - 3: CP r38, r7 - LD r8, r39, 8a, 8h - MULI64 r10, r8, 8d - LD r3, r39, 0a, 8h - ADD64 r7, r3, r10 - CP r5, r38 - 9: LD r2, r39, 0a, 8h - LD r10, r39, 8a, 8h - JNE r7, r3, :5 - JEQ r10, r35, :6 - CP r4, r37 - MUL64 r3, r10, r4 + 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 JAL r31, r0, :free - CP r6, r38 + CP r1, r40 JMP :7 - 6: CP r6, r38 - 7: ST r6, r39, 0a, 8h + 6: CP r1, r40 + 7: ST r1, r42, 0a, 8h JMP :8 - 5: CP r4, r37 - CP r6, r38 - ADDI64 r8, r5, 8d - ADDI64 r9, r3, 8d - LD r10, r3, 0a, 8h - ST r10, r5, 0a, 8h - CP r3, r9 - CP r5, r8 + 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 r39, r34 - 8: LD r5, r39, 8a, 8h - MULI64 r7, r5, 8d - LD r6, r39, 0a, 8h - ADD64 r1, r6, r7 + 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 r2, r39, 8a, 8h - ADD64 r3, r2, r33 - ST r3, r39, 8a, 8h - 4: LD r31, r254, 0a, 72h - ADDI64 r254, r254, 72d + LD r53, r42, 8a, 8h + ADD64 r54, r53, r33 + ST r54, r42, 8a, 8h + 4: LD r31, r254, 0a, 192h + ADDI64 r254, r254, 192d JALA r0, r31, 0a code size: 955 ret: 69 diff --git a/lang/tests/son_tests_global_aliasing_overptimization.txt b/lang/tests/son_tests_global_aliasing_overptimization.txt index 5d1a573..9fe9282 100644 --- a/lang/tests/son_tests_global_aliasing_overptimization.txt +++ b/lang/tests/son_tests_global_aliasing_overptimization.txt @@ -4,15 +4,15 @@ clobber: ST r3, r1, 0a, 8h JALA r0, r31, 0a main: - ADDI64 r254, r254, -16d - ST r31, r254, 0a, 16h + ADDI64 r254, r254, -24d + ST r31, r254, 0a, 24h LRA r32, r0, :var - LI64 r3, 2d - ST r3, r32, 0a, 8h + LI64 r33, 2d + ST r33, r32, 0a, 8h JAL r31, r0, :clobber LD r1, r32, 0a, 8h - LD r31, r254, 0a, 16h - ADDI64 r254, r254, 16d + LD r31, r254, 0a, 24h + ADDI64 r254, r254, 24d JALA r0, r31, 0a code size: 166 ret: 0 diff --git a/lang/tests/son_tests_if_statements.txt b/lang/tests/son_tests_if_statements.txt index 65a7815..852e400 100644 --- a/lang/tests/son_tests_if_statements.txt +++ b/lang/tests/son_tests_if_statements.txt @@ -1,22 +1,21 @@ fib: - ADDI64 r254, r254, -32d - ST r31, r254, 0a, 32h + ADDI64 r254, r254, -40d + ST r31, r254, 0a, 40h LI64 r1, 1d LI64 r32, 2d JGTU r2, r32, :0 JMP :1 - 0: CP r6, r2 - SUB64 r2, r6, r1 - CP r33, r6 + 0: CP r33, r2 + SUB64 r2, r33, r1 + CP r34, r33 JAL r31, r0, :fib - CP r2, r33 - CP r34, r1 + CP r2, r34 + CP r35, r1 SUB64 r2, r2, r32 JAL r31, r0, :fib - CP r8, r34 - ADD64 r1, r1, r8 - 1: LD r31, r254, 0a, 32h - ADDI64 r254, r254, 32d + ADD64 r1, r1, r35 + 1: LD r31, r254, 0a, 40h + ADDI64 r254, r254, 40d JALA r0, r31, 0a main: ADDI64 r254, r254, -8d @@ -26,6 +25,6 @@ main: LD r31, r254, 0a, 8h ADDI64 r254, r254, 8d JALA r0, r31, 0a -code size: 214 +code size: 211 ret: 55 status: Ok(()) diff --git a/lang/tests/son_tests_inline_test.txt b/lang/tests/son_tests_inline_test.txt index a427d1b..035bb3f 100644 --- a/lang/tests/son_tests_inline_test.txt +++ b/lang/tests/son_tests_inline_test.txt @@ -1,22 +1,21 @@ main: - ADDI64 r254, r254, -16d - ST r31, r254, 0a, 16h + ADDI64 r254, r254, -32d + ST r31, r254, 0a, 32h JAL r31, r0, :scalar_values - LI64 r3, 0d - CP r8, r3 - JEQ r1, r8, :0 + LI64 r32, 0d + CP r33, r32 + JEQ r1, r33, :0 LI64 r1, 1d JMP :1 - 0: CP r32, r8 - JAL r31, r0, :structs - CP r3, r32 - JEQ r1, r3, :2 + 0: JAL r31, r0, :structs + CP r34, r33 + JEQ r1, r34, :2 JAL r31, r0, :structs JMP :1 - 2: CP r1, r3 - CP r32, r3 - 1: LD r31, r254, 0a, 16h - ADDI64 r254, r254, 16d + 2: CP r1, r34 + CP r33, r34 + 1: LD r31, r254, 0a, 32h + ADDI64 r254, r254, 32d JALA r0, r31, 0a scalar_values: LI64 r1, 0d @@ -24,17 +23,17 @@ scalar_values: structs: ADDI64 r254, r254, -32d LI64 r1, 5d - ST r1, r254, 0a, 8h - ST r1, r254, 8a, 8h - LD r5, r254, 0a, 8h + ST r1, r254, 16a, 8h + ST r1, r254, 24a, 8h + LD r5, r254, 16a, 8h ADDI64 r7, r5, 15d - ST r7, r254, 16a, 8h + ST r7, r254, 0a, 8h LI64 r10, 20d - ST r10, r254, 24a, 8h - LD r1, r254, 16a, 8h + ST r10, r254, 8a, 8h + LD r1, r254, 0a, 8h SUB64 r1, r1, r10 ADDI64 r254, r254, 32d JALA r0, r31, 0a -code size: 310 +code size: 307 ret: 0 status: Ok(()) diff --git a/lang/tests/son_tests_inlining_issues.txt b/lang/tests/son_tests_inlining_issues.txt new file mode 100644 index 0000000..c4608ce --- /dev/null +++ b/lang/tests/son_tests_inlining_issues.txt @@ -0,0 +1,109 @@ +main: + ADDI64 r254, r254, -106d + ST r31, r254, 58a, 48h + ADDI64 r32, r254, 33d + ADDI64 r2, r254, 34d + ADDI64 r6, r254, 1d + LI64 r33, 0d + ADDI64 r4, r254, 17d + 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 r34, r254, 42a, 8h + LI8 r36, 0b + ST r35, r254, 0a, 1h + ST r33, r254, 9a, 8h + ST r33, 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 + JAL r31, r0, :put_filled_rect + LD r31, r254, 58a, 48h + ADDI64 r254, r254, 106d + JALA r0, r31, 0a +put_filled_rect: + ADDI64 r254, r254, -212d + ST r32, r254, 108a, 104h + ST r3, r254, 92a, 16h + ADDI64 r3, r254, 92d + ST r5, r254, 76a, 16h + ADDI64 r5, r254, 76d + 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 + 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 + ECA + ST r41, r254, 42a, 8h + CP r2, r43 + CP r3, r32 + CP r4, r33 + CP r5, r44 + ECA + ADD64 r42, r40, r42 + SUB64 r41, r41, r40 + SUB64 r39, r39, r32 + CP r6, r43 + CP r8, r44 + JMP :3 + 2: LD r32, r254, 108a, 104h + ADDI64 r254, r254, 212d + JALA r0, r31, 0a +code size: 917 +ret: 0 +status: Ok(()) diff --git a/lang/tests/son_tests_intcast_store.txt b/lang/tests/son_tests_intcast_store.txt index 05870f6..8e8874c 100644 --- a/lang/tests/son_tests_intcast_store.txt +++ b/lang/tests/son_tests_intcast_store.txt @@ -1,16 +1,16 @@ main: - ADDI64 r254, r254, -24d - ST r31, r254, 16a, 8h + ADDI64 r254, r254, -32d + ST r31, r254, 16a, 16h ADDI64 r3, r254, 0d ADDI64 r2, r254, 8d - LI64 r4, 0d - ST r4, r254, 0a, 8h - ST r4, r254, 8a, 8h + LI64 r32, 0d + ST r32, r254, 0a, 8h + ST r32, r254, 8a, 8h LI64 r4, 1024d JAL r31, r0, :set ANDI r1, r1, 4294967295d - LD r31, r254, 16a, 8h - ADDI64 r254, r254, 24d + LD r31, r254, 16a, 16h + ADDI64 r254, r254, 32d JALA r0, r31, 0a set: CP r1, r4 diff --git a/lang/tests/son_tests_more_if_opts.txt b/lang/tests/son_tests_more_if_opts.txt new file mode 100644 index 0000000..378e925 --- /dev/null +++ b/lang/tests/son_tests_more_if_opts.txt @@ -0,0 +1,27 @@ +main: + ADDI64 r254, r254, -32d + ST r31, r254, 0a, 32h + JAL r31, r0, :opaque + CP r32, r1 + JAL r31, r0, :opaque + LI64 r33, 0d + CP r1, r32 + JNE r1, r33, :0 + CP r32, r1 + LI64 r1, 0d + CP r34, r32 + JMP :1 + 0: CP r34, r1 + LD r1, r34, 0a, 8h + 1: JEQ r34, r33, :2 + LD r1, r34, 0a, 8h + JMP :2 + 2: LD r31, r254, 0a, 32h + ADDI64 r254, r254, 32d + JALA r0, r31, 0a +opaque: + LI64 r1, 0d + JALA r0, r31, 0a +code size: 183 +ret: 0 +status: Ok(()) diff --git a/lang/tests/son_tests_needless_unwrap.txt b/lang/tests/son_tests_needless_unwrap.txt new file mode 100644 index 0000000..73b8726 --- /dev/null +++ b/lang/tests/son_tests_needless_unwrap.txt @@ -0,0 +1,6 @@ +test.hb:4:17: unwrap is not needed since the value is (provably) never null, remove it, or replace with '@as(, )' + 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) + ^ diff --git a/lang/tests/son_tests_null_check_test.txt b/lang/tests/son_tests_null_check_test.txt index c9727f8..dc24071 100644 --- a/lang/tests/son_tests_null_check_test.txt +++ b/lang/tests/son_tests_null_check_test.txt @@ -1,6 +1,26 @@ -test.hb:15:4: can't prove the value is not 'null', use '@unwrap()' if you believe compiler is stupid, or explicitly check for null and handle it ('if == null { /* handle */ } else { /* use opt */ }') - *ptr += 1 - ^ -test.hb:15:4: can't prove the value is not 'null', use '@unwrap()' if you believe compiler is stupid, or explicitly check for null and handle it ('if == null { /* handle */ } else { /* use opt */ }') - *ptr += 1 - ^ +get_ptr: + ADDI64 r254, r254, -8d + ADDI64 r1, r254, 0d + ADDI64 r254, r254, 8d + JALA r0, r31, 0a +main: + ADDI64 r254, r254, -40d + ST r31, r254, 0a, 40h + JAL r31, r0, :get_ptr + LI64 r32, 0d + JNE r1, r32, :0 + LI64 r1, 0d + 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 + JALA r0, r31, 0a +code size: 208 +ret: 10 +status: Ok(()) diff --git a/lang/tests/son_tests_nullable_structure.txt b/lang/tests/son_tests_nullable_structure.txt new file mode 100644 index 0000000..cb6c81f --- /dev/null +++ b/lang/tests/son_tests_nullable_structure.txt @@ -0,0 +1,65 @@ +main: + ADDI64 r254, r254, -122d + ST r31, r254, 26a, 96h + JAL r31, r0, :returner_fn + CP r32, r1 + ADDI64 r1, r254, 2d + JAL r31, r0, :returner_bn + ADDI64 r33, 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 + JMP :1 + 0: LI64 r1, 1d + 1: LD r31, r254, 26a, 96h + ADDI64 r254, r254, 122d + 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 + 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 + ADDI64 r254, r254, 2d + JALA r0, r31, 0a +returner_fn: + LD r1, r254, 0a, 0h + ORI r1, r1, 128d + JALA r0, r31, 0a +code size: 546 +ret: 1 +status: Ok(()) diff --git a/lang/tests/son_tests_nullable_types.txt b/lang/tests/son_tests_nullable_types.txt index 273bcc1..c504425 100644 --- a/lang/tests/son_tests_nullable_types.txt +++ b/lang/tests/son_tests_nullable_types.txt @@ -2,86 +2,134 @@ decide: LI8 r1, 1b JALA r0, r31, 0a main: - ADDI64 r254, r254, -136d - ST r31, r254, 80a, 56h + ADDI64 r254, r254, -224d + ST r31, r254, 80a, 144h JAL r31, r0, :decide - LI64 r4, 0d - ADDI64 r32, r254, 72d + LI64 r32, 0d + ADDI64 r2, r254, 72d + CP r33, r2 ANDI r1, r1, 255d JNE r1, r0, :0 - CP r33, r4 + CP r34, r32 JMP :1 - 0: CP r33, r32 - 1: JNE r33, r4, :2 + 0: CP r34, r33 + 1: JNE r34, r32, :2 LI64 r1, 9001d JMP :3 2: JAL r31, r0, :decide - LI8 r34, 0b - LI8 r35, 1b + LI8 r35, 0b ANDI r1, r1, 255d JNE r1, r0, :4 - ST r35, r254, 56a, 1h - LD r9, r33, 0a, 8h - ST r9, r254, 64a, 8h + LI8 r36, 1b + ST r36, r254, 56a, 1h + LD r36, r34, 0a, 8h + ST r36, r254, 64a, 8h JMP :5 - 4: ST r34, r254, 56a, 1h - 5: LD r6, r254, 56a, 1h - ANDI r6, r6, 255d - ANDI r34, r34, 255d - JEQ r6, r34, :6 + 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 JMP :3 6: JAL r31, r0, :decide - LI32 r2, 0w + LI32 r38, 0w ANDI r1, r1, 255d JNE r1, r0, :7 - CP r36, r2 + CP r39, r38 JMP :8 - 7: LI32 r36, 8388609w - 8: ANDI r36, r36, 4294967295d - ANDI r2, r2, 4294967295d - JNE r36, r2, :9 + 7: LI32 r39, 2147483649w + 8: ANDI r39, r39, 4294967295d + ANDI r38, r38, 4294967295d + JNE r39, r38, :9 LI64 r1, 69d JMP :3 - 9: JAL r31, r0, :decide - LI64 r3, 0d - LI64 r37, 1d + 9: ADDI64 r3, r254, 40d + CP r40, r3 + 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 + JMP :3 +10: LRA r4, r0, :"foo\0" + CP r3, r40 + CP r2, r3 + LD r2, r2, 0a, 16h + JAL r31, r0, :use_foo + ADDI64 r42, r254, 0d + JAL r31, r0, :no_foo + ST r1, r254, 0a, 16h + JAL r31, r0, :decide ANDI r1, r1, 255d - JNE r1, r0, :10 - ST r3, r254, 16a, 8h - JMP :11 -10: ST r32, r254, 16a, 8h - ST r37, r254, 24a, 8h - ST r37, r254, 72a, 8h -11: LD r2, r254, 16a, 8h - JNE r2, r3, :12 + JNE r1, r0, :11 + CP r2, r33 + 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 JMP :3 -12: JAL r31, r0, :decide - ADDI64 r10, r254, 32d +13: ADDI64 r1, r254, 16d + JAL r31, r0, :new_bar + JAL r31, r0, :decide ANDI r1, r1, 255d - JNE r1, r0, :13 - ADDI64 r11, r254, 0d - ST r32, r254, 0a, 8h - ST r37, r254, 8a, 8h - ST r35, r254, 32a, 1h - ADDI64 r12, r10, 8d - BMC r11, r12, 16h - JMP :14 -13: ST r34, r254, 32a, 1h -14: LD r11, r254, 32a, 1h - ANDI r11, r11, 255d - ANDI r34, r34, 255d - JEQ r11, r34, :15 + JNE r1, 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 JMP :3 -15: LD r5, r254, 16a, 8h - LD r7, r5, 0a, 8h - ANDI r9, r36, 65535d - SUB64 r1, r9, r7 - 3: LD r31, r254, 80a, 56h - ADDI64 r254, r254, 136d +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 JALA r0, r31, 0a -code size: 729 +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 + 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 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 + 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 + JALA r0, r31, 0a +code size: 1143 ret: 0 status: Ok(()) diff --git a/lang/tests/son_tests_only_break_loop.txt b/lang/tests/son_tests_only_break_loop.txt index 93b55da..b353a8c 100644 --- a/lang/tests/son_tests_only_break_loop.txt +++ b/lang/tests/son_tests_only_break_loop.txt @@ -2,14 +2,14 @@ inb: CP r1, r2 JALA r0, r31, 0a main: - ADDI64 r254, r254, -24d - ST r31, r254, 0a, 24h + ADDI64 r254, r254, -32d + ST r31, r254, 0a, 32h LI64 r32, 0d LI64 r33, 100d 4: CP r2, r33 JAL r31, r0, :inb - ANDI r7, r1, 2d - JNE r7, r32, :0 + ANDI r34, r1, 2d + JNE r34, r32, :0 LI64 r2, 96d CP r3, r32 JAL r31, r0, :outb @@ -20,8 +20,8 @@ main: JMP :2 1: JMP :3 0: JMP :4 - 2: LD r31, r254, 0a, 24h - ADDI64 r254, r254, 24d + 2: LD r31, r254, 0a, 32h + ADDI64 r254, r254, 32d JALA r0, r31, 0a outb: JALA r0, r31, 0a diff --git a/lang/tests/son_tests_optional_from_eca.txt b/lang/tests/son_tests_optional_from_eca.txt new file mode 100644 index 0000000..fe479eb --- /dev/null +++ b/lang/tests/son_tests_optional_from_eca.txt @@ -0,0 +1,22 @@ +main: + ADDI64 r254, r254, -16d + ADDI64 r3, r254, 0d + LI64 r6, 0d + CP r3, r6 + CP r4, r6 + CP r5, r6 + 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 + UN + 0: LD r1, r254, 8a, 8h + ADDI64 r254, r254, 16d + JALA r0, r31, 0a +unknown ecall: 0 +code size: 142 +ret: 0 +status: Err(Unreachable) diff --git a/lang/tests/son_tests_overwrite_aliasing_overoptimization.txt b/lang/tests/son_tests_overwrite_aliasing_overoptimization.txt index 72aae6b..ec02dec 100644 --- a/lang/tests/son_tests_overwrite_aliasing_overoptimization.txt +++ b/lang/tests/son_tests_overwrite_aliasing_overoptimization.txt @@ -1,23 +1,23 @@ main: - ADDI64 r254, r254, -48d - ST r31, r254, 40a, 8h - LI64 r4, 4d - ADDI64 r3, r254, 24d - ADDI64 r6, r254, 0d - ST r4, r254, 24a, 8h - LI64 r5, 1d - ST r5, r254, 32a, 8h - ST r5, r254, 16a, 8h - BMC r3, r6, 16h + 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 JAL r31, r0, :opaque ST r1, r254, 0a, 16h - LD r4, r254, 8a, 8h - LD r6, r254, 16a, 8h - ADD64 r8, r6, r4 - LD r6, r254, 0a, 8h - SUB64 r1, r6, r8 - LD r31, r254, 40a, 8h - ADDI64 r254, r254, 48d + 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 JALA r0, r31, 0a opaque: ADDI64 r254, r254, -16d diff --git a/lang/tests/son_tests_pointer_opts.txt b/lang/tests/son_tests_pointer_opts.txt index bbf3ccb..f8db122 100644 --- a/lang/tests/son_tests_pointer_opts.txt +++ b/lang/tests/son_tests_pointer_opts.txt @@ -3,17 +3,17 @@ clobber: ST r3, r2, 0a, 8h JALA r0, r31, 0a main: - ADDI64 r254, r254, -16d - ST r31, r254, 8a, 8h + ADDI64 r254, r254, -40d + ST r31, r254, 8a, 32h ADDI64 r2, r254, 0d - LI64 r3, 2d - ST r3, r254, 0a, 8h + LI64 r32, 2d + ST r32, r254, 0a, 8h JAL r31, r0, :clobber - LD r8, r254, 0a, 8h - LI64 r9, 4d - SUB64 r1, r9, r8 - LD r31, r254, 8a, 8h - ADDI64 r254, r254, 16d + LD r33, r254, 0a, 8h + LI64 r34, 4d + SUB64 r1, r34, r33 + LD r31, r254, 8a, 32h + ADDI64 r254, r254, 40d JALA r0, r31, 0a code size: 169 ret: 0 diff --git a/lang/tests/son_tests_pointers.txt b/lang/tests/son_tests_pointers.txt index ec81e27..3376e76 100644 --- a/lang/tests/son_tests_pointers.txt +++ b/lang/tests/son_tests_pointers.txt @@ -1,18 +1,18 @@ drop: JALA r0, r31, 0a main: - ADDI64 r254, r254, -24d - ST r31, r254, 8a, 16h + ADDI64 r254, r254, -32d + ST r31, r254, 8a, 24h ADDI64 r2, r254, 0d LI64 r32, 1d ST r32, r254, 0a, 8h JAL r31, r0, :modify CP r2, r32 JAL r31, r0, :drop - LD r8, r254, 0a, 8h - ADDI64 r1, r8, -2d - LD r31, r254, 8a, 16h - ADDI64 r254, r254, 24d + LD r33, r254, 0a, 8h + ADDI64 r1, r33, -2d + LD r31, r254, 8a, 24h + ADDI64 r254, r254, 32d JALA r0, r31, 0a modify: LI64 r3, 2d diff --git a/lang/tests/son_tests_reading_idk.txt b/lang/tests/son_tests_reading_idk.txt index 5604ab6..e10b421 100644 --- a/lang/tests/son_tests_reading_idk.txt +++ b/lang/tests/son_tests_reading_idk.txt @@ -1,3 +1,6 @@ -test.hb:3:16: type 'int' cannot be uninitialized, use a zero value instead ('null' in case of pointers) - a := @as(int, idk) - ^ +main: + LI64 r1, 0d + JALA r0, r31, 0a +code size: 29 +ret: 0 +status: Ok(()) diff --git a/lang/tests/son_tests_request_page.txt b/lang/tests/son_tests_request_page.txt index 03c37f1..e249c8f 100644 --- a/lang/tests/son_tests_request_page.txt +++ b/lang/tests/son_tests_request_page.txt @@ -1,6 +1,6 @@ create_back_buffer: - ADDI64 r254, r254, -48d - ST r31, r254, 0a, 48h + ADDI64 r254, r254, -56d + ST r31, r254, 0a, 56h LI64 r32, 255d JGTS r2, r32, :0 JAL r31, r0, :request_page @@ -9,23 +9,23 @@ create_back_buffer: LI8 r34, 255b CP r2, r34 JAL r31, r0, :request_page - CP r35, r1 - LI64 r36, 0d + LI64 r35, 0d CP r2, r33 - SUB64 r33, r2, r32 - 5: JGTS r33, r36, :2 - CP r1, r35 + SUB64 r36, r2, r32 + 5: JGTS r36, r35, :2 JMP :1 - 2: JLTS r33, r32, :3 + 2: CP r37, r1 + JLTS r36, r32, :3 CP r2, r34 JAL r31, r0, :request_page JMP :4 - 3: CP r2, r33 + 3: CP r2, r36 JAL r31, r0, :request_page - 4: SUB64 r33, r33, r32 + 4: SUB64 r36, r36, r32 + CP r1, r37 JMP :5 - 1: LD r31, r254, 0a, 48h - ADDI64 r254, r254, 48d + 1: LD r31, r254, 0a, 56h + ADDI64 r254, r254, 56d JALA r0, r31, 0a main: ADDI64 r254, r254, -8d diff --git a/lang/tests/son_tests_returning_global_struct.txt b/lang/tests/son_tests_returning_global_struct.txt index 6f3bca0..ea20c83 100644 --- a/lang/tests/son_tests_returning_global_struct.txt +++ b/lang/tests/son_tests_returning_global_struct.txt @@ -1,22 +1,22 @@ main: - ADDI64 r254, r254, -12d - ST r31, r254, 4a, 8h - ADDI64 r2, r254, 0d + ADDI64 r254, r254, -100d + ST r31, r254, 4a, 96h + ADDI64 r32, r254, 0d JAL r31, r0, :random_color ST r1, r254, 0a, 4h - LD r5, r254, 0a, 1h - LD r8, r254, 1a, 1h - LD r12, r254, 2a, 1h - ANDI r9, r5, 255d - ANDI r1, r8, 255d - LD r6, r254, 3a, 1h - ANDI r5, r12, 255d - ADD64 r4, r1, r9 - ANDI r10, r6, 255d - ADD64 r9, r4, r5 - ADD64 r1, r9, r10 - LD r31, r254, 4a, 8h - ADDI64 r254, r254, 12d + LD r33, r254, 0a, 1h + LD r34, r254, 1a, 1h + LD r35, r254, 2a, 1h + ANDI r36, r33, 255d + ANDI r37, r34, 255d + LD r38, r254, 3a, 1h + ANDI r39, r35, 255d + ADD64 r40, r37, r36 + ANDI r41, r38, 255d + ADD64 r42, r40, r39 + ADD64 r1, r42, r41 + LD r31, r254, 4a, 96h + ADDI64 r254, r254, 100d JALA r0, r31, 0a random_color: LRA r1, r0, :white diff --git a/lang/tests/son_tests_returning_optional_issues.txt b/lang/tests/son_tests_returning_optional_issues.txt new file mode 100644 index 0000000..a38591c --- /dev/null +++ b/lang/tests/son_tests_returning_optional_issues.txt @@ -0,0 +1,31 @@ +get_format: + ADDI64 r254, r254, -16d + LI8 r5, 1b + ADDI64 r4, r254, 0d + LRA r3, r0, :BMP + ST r5, r254, 0a, 1h + LD r6, r3, 0a, 8h + ST r6, r254, 8a, 8h + LD r1, r4, 0a, 16h + ADDI64 r254, r254, 16d + JALA r0, r31, 0a +main: + ADDI64 r254, r254, -48d + ST r31, r254, 16a, 32h + ADDI64 r32, r254, 0d + JAL r31, r0, :get_format + ST r1, r254, 0a, 16h + LI8 r33, 0b + LD r34, r254, 0a, 1h + ANDI r34, r34, 255d + ANDI r33, r33, 255d + JNE r34, r33, :0 + LI64 r1, 1d + JMP :1 + 0: LD r1, r254, 8a, 8h + 1: LD r31, r254, 16a, 32h + ADDI64 r254, r254, 48d + JALA r0, r31, 0a +code size: 283 +ret: 0 +status: Ok(()) diff --git a/lang/tests/son_tests_small_struct_bitcast.txt b/lang/tests/son_tests_small_struct_bitcast.txt index 7ecaf98..0e299b4 100644 --- a/lang/tests/son_tests_small_struct_bitcast.txt +++ b/lang/tests/son_tests_small_struct_bitcast.txt @@ -1,25 +1,25 @@ main: - ADDI64 r254, r254, -12d - ST r31, r254, 4a, 8h - LRA r1, r0, :white - ADDI64 r4, r254, 0d - LD r2, r1, 0a, 4h + ADDI64 r254, r254, -36d + ST r31, r254, 4a, 32h + LRA r32, r0, :white + ADDI64 r33, r254, 0d + LD r2, r32, 0a, 4h JAL r31, r0, :u32_to_color ST r1, r254, 0a, 4h - LD r9, r254, 0a, 1h - ANDI r1, r9, 255d - LD r31, r254, 4a, 8h - ADDI64 r254, r254, 12d + LD r34, r254, 0a, 1h + ANDI r1, r34, 255d + LD r31, r254, 4a, 32h + ADDI64 r254, r254, 36d JALA r0, r31, 0a u32_to_color: - ADDI64 r254, r254, -12d - ST r31, r254, 4a, 8h + ADDI64 r254, r254, -20d + ST r31, r254, 4a, 16h JAL r31, r0, :u32_to_u32 - ADDI64 r5, r254, 0d + ADDI64 r32, r254, 0d ST r1, r254, 0a, 4h - LD r1, r5, 0a, 4h - LD r31, r254, 4a, 8h - ADDI64 r254, r254, 12d + LD r1, r32, 0a, 4h + LD r31, r254, 4a, 16h + ADDI64 r254, r254, 20d JALA r0, r31, 0a u32_to_u32: CP r1, r2 diff --git a/lang/tests/son_tests_struct_patterns.txt b/lang/tests/son_tests_struct_patterns.txt index 46b7218..28b22bb 100644 --- a/lang/tests/son_tests_struct_patterns.txt +++ b/lang/tests/son_tests_struct_patterns.txt @@ -4,15 +4,14 @@ fib: CP r32, r2 LI64 r33, 2d JLTU r2, r33, :0 - CP r6, r32 - ADDI64 r2, r6, -1d + CP r34, r32 + ADDI64 r2, r34, -1d JAL r31, r0, :fib CP r2, r32 CP r34, r1 SUB64 r2, r2, r33 JAL r31, r0, :fib - CP r6, r34 - ADD64 r1, r1, r6 + ADD64 r1, r1, r34 JMP :1 0: CP r1, r32 1: LD r31, r254, 0a, 32h @@ -41,11 +40,10 @@ main: CP r2, r32 CP r33, r1 JAL r31, r0, :fib_iter - CP r9, r33 - SUB64 r1, r9, r1 + SUB64 r1, r33, r1 LD r31, r254, 0a, 24h ADDI64 r254, r254, 24d JALA r0, r31, 0a -code size: 306 +code size: 300 ret: 0 status: Ok(()) diff --git a/lang/tests/son_tests_struct_return_from_module_function.txt b/lang/tests/son_tests_struct_return_from_module_function.txt index 3d0e7cc..c17dbb3 100644 --- a/lang/tests/son_tests_struct_return_from_module_function.txt +++ b/lang/tests/son_tests_struct_return_from_module_function.txt @@ -10,28 +10,28 @@ foo: ADDI64 r254, r254, 16d JALA r0, r31, 0a main: - ADDI64 r254, r254, -56d - ST r31, r254, 48a, 8h - ADDI64 r2, r254, 32d + ADDI64 r254, r254, -128d + ST r31, r254, 48a, 80h + ADDI64 r32, r254, 32d JAL r31, r0, :foo ST r1, r254, 32a, 16h - ADDI64 r7, r254, 16d + ADDI64 r33, r254, 16d JAL r31, r0, :foo ST r1, r254, 16a, 16h - ADDI64 r2, r254, 0d + ADDI64 r34, r254, 0d JAL r31, r0, :foo ST r1, r254, 0a, 16h - LD r1, r254, 24a, 4h - LD r7, r254, 12a, 4h - ANDI r4, r1, 4294967295d - LD r1, r254, 32a, 8h - ANDI r11, r7, 4294967295d - ADD64 r8, r1, r4 - ADD64 r2, r8, r11 - LI64 r3, 7d - SUB64 r1, r3, r2 - LD r31, r254, 48a, 8h - ADDI64 r254, r254, 56d + LD r35, r254, 24a, 4h + LD r36, r254, 12a, 4h + ANDI r37, r35, 4294967295d + LD r35, r254, 32a, 8h + ANDI r38, r36, 4294967295d + ADD64 r39, r35, r37 + ADD64 r40, r39, r38 + LI64 r34, 7d + SUB64 r1, r34, r40 + LD r31, r254, 48a, 80h + ADDI64 r254, r254, 128d JALA r0, r31, 0a code size: 355 ret: 0 diff --git a/lang/tests/son_tests_structs.txt b/lang/tests/son_tests_structs.txt index baa5202..7a590a3 100644 --- a/lang/tests/son_tests_structs.txt +++ b/lang/tests/son_tests_structs.txt @@ -1,23 +1,24 @@ main: - ADDI64 r254, r254, -56d - ST r31, r254, 32a, 24h - LI64 r3, 4d - ADDI64 r2, r254, 16d - ST r3, r254, 16a, 8h - LI64 r32, 3d - ST r32, r254, 24a, 8h - ADDI64 r33, r254, 0d - LD r3, r2, 0a, 16h + ADDI64 r254, r254, -72d + ST r31, r254, 32a, 40h + LI64 r32, 4d + ADDI64 r4, r254, 16d + ST r32, r254, 16a, 8h + LI64 r33, 3d + ST r33, r254, 24a, 8h + ADDI64 r34, r254, 0d + CP r3, r4 + LD r3, r3, 0a, 16h JAL r31, r0, :odher_pass ST r1, r254, 0a, 16h - LD r2, r254, 8a, 8h - JNE r2, r32, :0 - CP r2, r33 + LD r35, r254, 8a, 8h + JNE r35, r33, :0 + CP r2, r34 JAL r31, r0, :pass JMP :1 0: LI64 r1, 0d - 1: LD r31, r254, 32a, 24h - ADDI64 r254, r254, 56d + 1: LD r31, r254, 32a, 40h + ADDI64 r254, r254, 72d JALA r0, r31, 0a odher_pass: ADDI64 r254, r254, -16d @@ -29,6 +30,6 @@ odher_pass: pass: LD r1, r2, 0a, 8h JALA r0, r31, 0a -code size: 305 +code size: 308 ret: 4 status: Ok(()) diff --git a/lang/tests/son_tests_wide_ret.txt b/lang/tests/son_tests_wide_ret.txt index baac64a..2d01e04 100644 --- a/lang/tests/son_tests_wide_ret.txt +++ b/lang/tests/son_tests_wide_ret.txt @@ -1,47 +1,47 @@ main: - ADDI64 r254, r254, -24d - ST r31, r254, 16a, 8h - ADDI64 r3, r254, 0d + ADDI64 r254, r254, -56d + ST r31, r254, 16a, 40h + ADDI64 r32, r254, 0d LI64 r4, 0d CP r3, r4 JAL r31, r0, :maina ST r1, r254, 0a, 16h - LD r8, r254, 12a, 1h - LD r9, r254, 3a, 1h - SUB8 r11, r9, r8 - ANDI r1, r11, 255d - LD r31, r254, 16a, 8h - ADDI64 r254, r254, 24d + LD r33, r254, 12a, 1h + LD r34, r254, 3a, 1h + SUB8 r35, r34, r33 + ANDI r1, r35, 255d + LD r31, r254, 16a, 40h + ADDI64 r254, r254, 56d JALA r0, r31, 0a maina: - ADDI64 r254, r254, -28d - ST r31, r254, 20a, 8h - ADDI64 r5, r254, 16d + ADDI64 r254, r254, -68d + ST r31, r254, 20a, 48h + ADDI64 r32, r254, 16d JAL r31, r0, :small_struct ST r1, r254, 16a, 4h - LI8 r9, 0b - ADDI64 r1, r254, 0d - ST r9, r254, 0a, 1h - ST r9, r254, 1a, 1h - ST r9, r254, 2a, 1h - LI8 r3, 3b - ST r3, r254, 3a, 1h - LI8 r6, 1b - ST r6, r254, 4a, 1h - ST r9, r254, 5a, 1h - ST r9, r254, 6a, 1h - ST r9, r254, 7a, 1h - ST r9, r254, 8a, 1h - ST r9, r254, 9a, 1h - ST r9, r254, 10a, 1h - ST r3, r254, 11a, 1h - ST r6, r254, 12a, 1h - ST r9, r254, 13a, 1h - ST r9, r254, 14a, 1h - ST r9, r254, 15a, 1h - LD r1, r1, 0a, 16h - LD r31, r254, 20a, 8h - ADDI64 r254, r254, 28d + LI8 r33, 0b + ADDI64 r34, r254, 0d + ST r33, r254, 0a, 1h + ST r33, r254, 1a, 1h + ST r33, r254, 2a, 1h + LI8 r35, 3b + ST r35, r254, 3a, 1h + LI8 r36, 1b + ST r36, r254, 4a, 1h + ST r33, r254, 5a, 1h + ST r33, r254, 6a, 1h + ST r33, r254, 7a, 1h + ST r33, r254, 8a, 1h + ST r33, r254, 9a, 1h + ST r33, r254, 10a, 1h + ST r35, r254, 11a, 1h + ST r36, r254, 12a, 1h + ST r33, r254, 13a, 1h + ST r33, r254, 14a, 1h + ST r33, r254, 15a, 1h + LD r1, r34, 0a, 16h + LD r31, r254, 20a, 48h + ADDI64 r254, r254, 68d JALA r0, r31, 0a small_struct: ADDI64 r254, r254, -4d