├── .github └── workflows │ └── rust.yml ├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md ├── fuzz ├── .gitignore ├── Cargo.toml ├── fuzz_targets │ ├── basic.rs │ ├── eval_gen.rs │ └── eval_str.rs └── src │ ├── lib.rs │ └── script_generator.rs ├── rust-toolchain.toml └── src ├── datastructures ├── arena.rs ├── id.rs ├── id_set.rs ├── id_vec.rs ├── mod.rs └── random.rs ├── lib.rs ├── main.rs └── sea_of_nodes ├── global_code_motion.rs ├── graph_visualizer.rs ├── ir_printer.rs ├── location.rs ├── mod.rs ├── nodes.rs ├── nodes ├── cfg.rs ├── gvn.rs ├── idealize.rs ├── iter_peeps.rs ├── node.rs ├── peephole.rs ├── print.rs └── scope.rs ├── parser.rs ├── tests.rs ├── tests ├── brainfuck.rs ├── chapter01.rs ├── chapter02.rs ├── chapter03.rs ├── chapter04.rs ├── chapter05.rs ├── chapter06.rs ├── chapter07.rs ├── chapter08.rs ├── chapter09.rs ├── chapter10.rs ├── chapter11.rs ├── chapter12.rs ├── chapter13.rs ├── chapter14.rs ├── chapter15.rs ├── chapter16.rs ├── chapter17.rs ├── evaluator.rs ├── rust.rs ├── scheduler.rs ├── scheduler_test.rs └── type_test.rs ├── types.rs └── types ├── field.rs ├── ty.rs ├── type.rs ├── type_float.rs ├── type_integer.rs ├── type_mem.rs ├── type_mem_ptr.rs ├── type_struct.rs └── type_tuple.rs /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Cargo Build & Test 2 | 3 | on: 4 | push: 5 | pull_request: 6 | 7 | env: 8 | CARGO_TERM_COLOR: always 9 | 10 | jobs: 11 | build_and_test: 12 | strategy: 13 | matrix: 14 | platform: [ ubuntu-latest ] #, macos-latest, windows-latest ] 15 | toolchain: 16 | - nightly 17 | runs-on: ${{ matrix.platform }} 18 | 19 | steps: 20 | - uses: actions/checkout@v3 21 | - name: Rustup update 22 | run: rustup update ${{ matrix.toolchain }} && rustup default ${{ matrix.toolchain }} 23 | - name: Build 24 | run: cargo build --verbose 25 | - name: Run tests 26 | run: cargo test --verbose 27 | 28 | # if we ever need a c compiler under windows 29 | # - name: Run tests (windows) 30 | # if: matrix.platform == 'windows-latest' 31 | # run: | 32 | # call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat" 33 | # cargo test --verbose 34 | # shell: cmd 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | .idea/ 4 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "simple-rust" 3 | version = "0.1.0" 4 | authors = ["Robert Obkircher "] 5 | edition = "2021" 6 | 7 | [features] 8 | fuzzing = [] -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Sea of Nodes - Simple 2 | 3 | Rust implementation of https://github.com/SeaOfNodes/Simple 4 | 5 | ## Status 6 | 7 | This is still a **work in progress** and the code is not very readable. 8 | 9 | The code is currently equivalent to `chapter17` of the Java implementation at commit `8d9cd9a0`. 10 | -------------------------------------------------------------------------------- /fuzz/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | corpus 3 | artifacts 4 | coverage 5 | -------------------------------------------------------------------------------- /fuzz/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "simple-rust-fuzz" 3 | version = "0.0.0" 4 | publish = false 5 | edition = "2021" 6 | 7 | [package.metadata] 8 | cargo-fuzz = true 9 | 10 | [dependencies] 11 | libfuzzer-sys = "0.4" 12 | arbitrary = { version = "1", features = ["derive"] } 13 | 14 | [dependencies.simple-rust] 15 | path = ".." 16 | features = ["fuzzing"] 17 | 18 | [[bin]] 19 | name = "basic" 20 | path = "fuzz_targets/basic.rs" 21 | test = false 22 | doc = false 23 | bench = false 24 | 25 | [[bin]] 26 | name = "eval_str" 27 | path = "fuzz_targets/eval_str.rs" 28 | test = false 29 | doc = false 30 | bench = false 31 | 32 | [[bin]] 33 | name = "eval_gen" 34 | path = "fuzz_targets/eval_gen.rs" 35 | test = false 36 | doc = false 37 | bench = false 38 | -------------------------------------------------------------------------------- /fuzz/fuzz_targets/basic.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | use libfuzzer_sys::fuzz_target; 4 | use simple_rust::datastructures::arena ::DroplessArena; 5 | use simple_rust::sea_of_nodes::parser::Parser; 6 | use simple_rust::sea_of_nodes::types::Types; 7 | 8 | fuzz_target!(|source: &str| { 9 | let arena = DroplessArena::new(); 10 | let mut types = Types::new(&arena); 11 | let mut parser = Parser::new(source, &mut types); 12 | parser.disable_show_graph_println = true; 13 | let _ = parser.parse(); 14 | }); 15 | -------------------------------------------------------------------------------- /fuzz/fuzz_targets/eval_gen.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | use libfuzzer_sys::fuzz_target; 4 | use simple_rust_fuzz::script_generator::GeneratedScript; 5 | 6 | fuzz_target!(|script: GeneratedScript| { 7 | simple_rust_fuzz::run_and_compare_eval(&script.source, !script.maybe_invalid); 8 | }); 9 | -------------------------------------------------------------------------------- /fuzz/fuzz_targets/eval_str.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | use libfuzzer_sys::fuzz_target; 4 | 5 | fuzz_target!(|input: &str| { 6 | simple_rust_fuzz::run_and_compare_eval(input, false); 7 | }); 8 | -------------------------------------------------------------------------------- /fuzz/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod script_generator; 2 | 3 | use simple_rust::datastructures::arena::DroplessArena; 4 | use simple_rust::sea_of_nodes::parser::Parser; 5 | use simple_rust::sea_of_nodes::tests::evaluator; 6 | use simple_rust::sea_of_nodes::tests::evaluator::{EResult, HeapObject}; 7 | use simple_rust::sea_of_nodes::types::Types; 8 | 9 | pub fn run_and_compare_eval(source: &str, definitely_valid: bool) { 10 | let arena1 = DroplessArena::new(); 11 | let mut types1 = Types::new(&arena1); 12 | let mut parser1 = Parser::new(source, &mut types1); 13 | parser1.disable_show_graph_println = true; 14 | parser1.nodes.disable_peephole = true; 15 | let result1 = parser1.parse(); 16 | 17 | let arena2 = DroplessArena::new(); 18 | let mut types2 = Types::new(&arena2); 19 | let mut parser2 = Parser::new(source, &mut types2); 20 | parser2.disable_show_graph_println = true; 21 | parser2.nodes.disable_peephole = false; 22 | let result2 = parser2.parse(); 23 | 24 | let (stop1, stop2) = match (result1, result2) { 25 | (Ok(stop1), Ok(stop2)) => (stop1, stop2), 26 | (r1, r2) => { 27 | assert_eq!(r1, r2); 28 | assert!(!definitely_valid); 29 | return; 30 | } 31 | }; 32 | 33 | let timeout = 1000; 34 | for arg in [0, 1, 10] { 35 | let er1 = evaluator::evaluate_with_result(&parser1.nodes, stop1.to_node(), arg, timeout); 36 | let er2 = evaluator::evaluate_with_result(&parser2.nodes, stop2.to_node(), arg, timeout); 37 | let EResult::Value(o1) = er1.1 else { 38 | continue; 39 | }; 40 | let EResult::Value(o2) = er2.1 else { 41 | continue; 42 | }; 43 | assert_eq!( 44 | HeapObject { 45 | heap: &er1.0, 46 | object: o1, 47 | }, 48 | HeapObject { 49 | heap: &er2.0, 50 | object: o2, 51 | } 52 | ); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "nightly" -------------------------------------------------------------------------------- /src/datastructures/arena.rs: -------------------------------------------------------------------------------- 1 | use std::alloc::Layout; 2 | use std::cell::{Cell, RefCell}; 3 | use std::mem::MaybeUninit; 4 | use std::{ptr, slice}; 5 | 6 | const CHUNK_SIZE: usize = 4000; 7 | 8 | /// This is an arena allocator. 9 | /// 10 | /// Its main purpose is to create references that share the same lifetime 11 | /// and can thus be passed around easily. 12 | /// 13 | /// It only works with types that do not need to be dropped 14 | /// which is enforced by requiring them to implement `Copy`. 15 | /// 16 | /// This is a combination of rustc_arena::DroplessArena and bumpalo. 17 | /// It does seem to pass all tests with miri, but I wouldn't be surprised if it is unsound. 18 | pub struct DroplessArena { 19 | start: Cell<*mut u8>, 20 | end: Cell<*mut u8>, 21 | allocations: RefCell>>, 22 | } 23 | 24 | impl DroplessArena { 25 | pub const fn new() -> Self { 26 | Self { 27 | start: Cell::new(ptr::null_mut()), 28 | end: Cell::new(ptr::null_mut()), 29 | allocations: RefCell::new(vec![]), 30 | } 31 | } 32 | 33 | fn alloc_layout(&self, layout: Layout) -> *mut u8 { 34 | assert!(layout.size() < CHUNK_SIZE, "intended for small values"); 35 | assert_ne!(layout.size(), 0, "would result in identical references"); 36 | debug_assert_ne!(layout.align(), 0, "checked by Layout"); 37 | debug_assert!(layout.align().is_power_of_two(), "checked by Layout"); 38 | 39 | loop { 40 | let end = self.end.get(); 41 | // if end is not null (first time) and no underflow possible, and result greater than null 42 | if (end as usize) > (layout.size() + layout.align()) { 43 | let ptr = end.wrapping_sub(layout.size()); 44 | let result = ptr.wrapping_sub(ptr as usize & (layout.align() - 1)); 45 | debug_assert_eq!(result as usize % layout.align(), 0); 46 | if result >= self.start.get() { 47 | self.end.set(result); 48 | return result; 49 | } 50 | } 51 | self.grow(); 52 | } 53 | } 54 | 55 | #[cold] 56 | fn grow(&self) { 57 | let chunk = Box::leak(Box::new_uninit_slice(CHUNK_SIZE)).as_mut_ptr(); 58 | self.start.set(chunk.cast()); 59 | self.end.set(chunk.wrapping_add(CHUNK_SIZE).cast()); // could this overflow to 0? 60 | self.allocations.borrow_mut().push(chunk); 61 | } 62 | 63 | pub fn alloc(&self, value: T) -> &mut T { 64 | let layout = Layout::for_value(&value); 65 | let result = self.alloc_layout(layout).cast::(); 66 | unsafe { 67 | // SAFETY: We reserved enough aligned memory, initialize it here, and the 68 | // memory won't be freed because self outlives the return value. 69 | ptr::write(result, value); 70 | &mut *result 71 | } 72 | } 73 | 74 | pub fn alloc_slice_copy(&self, slice: &[T]) -> &mut [T] { 75 | if slice.is_empty() { 76 | return &mut []; // we can't allocate a unique pointer anyway 77 | } 78 | let layout = Layout::for_value(slice); 79 | let result = self.alloc_layout(layout).cast::(); 80 | unsafe { 81 | // SAFETY: We reserved enough aligned memory, initialize it here, and the 82 | // memory won't be freed because self outlives the return value. 83 | ptr::copy_nonoverlapping(slice.as_ptr(), result, slice.len()); 84 | slice::from_raw_parts_mut(result, slice.len()) 85 | } 86 | } 87 | 88 | pub fn alloc_str(&self, s: &str) -> &mut str { 89 | let bytes = self.alloc_slice_copy(s.as_bytes()); 90 | debug_assert_eq!(s.as_bytes(), bytes); 91 | // SAFETY: the copied bytes are still valid utf8 92 | unsafe { std::str::from_utf8_unchecked_mut(bytes) } 93 | } 94 | } 95 | 96 | impl Drop for DroplessArena { 97 | fn drop(&mut self) { 98 | for &chunk in self.allocations.borrow_mut().iter() { 99 | // SAFETY: the pointer comes from a boxed slice of that size, and none of the 100 | // returned references can outlive self. 101 | let _ = unsafe { Box::from_raw(slice::from_raw_parts_mut(chunk, CHUNK_SIZE)) }; 102 | } 103 | } 104 | } 105 | 106 | #[cfg(test)] 107 | mod tests { 108 | use crate::datastructures::arena::{DroplessArena, CHUNK_SIZE}; 109 | 110 | #[test] 111 | fn ptr_values() { 112 | #[derive(Copy, Clone)] 113 | struct Test { 114 | x: u32, 115 | } 116 | 117 | let arena = DroplessArena::new(); 118 | 119 | let a = arena.alloc(Test { x: 0 }); 120 | let b = arena.alloc(Test { x: 1 }); 121 | let c = arena.alloc(Test { x: 2 }); 122 | 123 | assert_eq!(a.x, 0); 124 | assert_eq!(b.x, 1); 125 | assert_eq!(c.x, 2); 126 | 127 | assert_eq!( 128 | a as *const Test as usize, 129 | b as *const Test as usize + size_of::() 130 | ); 131 | assert_eq!( 132 | b as *const Test as usize, 133 | c as *const Test as usize + size_of::() 134 | ); 135 | } 136 | 137 | #[test] 138 | fn arena() { 139 | #[derive(Copy, Clone)] 140 | struct Test { 141 | x: u32, 142 | } 143 | 144 | let arena = DroplessArena::new(); 145 | 146 | let mut tests: Vec<&mut Test> = Vec::new(); 147 | for i in 0..(2 * CHUNK_SIZE / size_of::() + 10) { 148 | let t = arena.alloc(Test { x: i as u32 }); 149 | 150 | tests.push(t); 151 | } 152 | 153 | for (expected, t) in tests.iter_mut().enumerate() { 154 | assert_eq!(expected as u32, t.x); 155 | t.x = expected as u32 + 100; 156 | } 157 | 158 | for (i, t) in tests.iter().enumerate() { 159 | assert_eq!(i as u32 + 100, t.x); 160 | } 161 | assert_eq!(arena.allocations.borrow().len(), 3); 162 | } 163 | 164 | #[test] 165 | fn slices() { 166 | let arena = DroplessArena::new(); 167 | let a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; 168 | let b = [11, 12, 13, 14, 15, 16, 17]; 169 | 170 | let aa = arena.alloc_slice_copy(&a); 171 | let bb = arena.alloc_slice_copy(&b); 172 | 173 | assert_eq!(aa, a); 174 | assert_eq!(bb, b); 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /src/datastructures/id.rs: -------------------------------------------------------------------------------- 1 | pub trait Id { 2 | fn index(&self) -> usize; 3 | } 4 | 5 | impl Id for usize { 6 | fn index(&self) -> usize { 7 | *self 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/datastructures/id_set.rs: -------------------------------------------------------------------------------- 1 | use std::marker::PhantomData; 2 | 3 | use crate::datastructures::id::Id; 4 | 5 | pub struct IdSet { 6 | words: Vec, 7 | phantom_data: PhantomData, 8 | } 9 | 10 | impl IdSet { 11 | pub fn zeros(capacity: usize) -> Self { 12 | Self { 13 | words: vec![ 14 | 0; 15 | if capacity == 0 { 16 | 0 17 | } else { 18 | capacity / usize::BITS as usize + 1 19 | } 20 | ], 21 | phantom_data: Default::default(), 22 | } 23 | } 24 | 25 | fn ensure_size(&mut self, index: usize) { 26 | let size = index + 1; 27 | if size > self.words.len() { 28 | self.words.resize(size, 0) 29 | } 30 | } 31 | 32 | fn index(element: &E) -> (usize, u32) { 33 | let i = element.index(); 34 | let word = i / usize::BITS as usize; 35 | let bit = (i % usize::BITS as usize) as u32; 36 | (word, bit) 37 | } 38 | 39 | pub fn add(&mut self, element: E) { 40 | let (word, bit) = IdSet::index(&element); 41 | self.ensure_size(word); 42 | self.words[word] |= 1 << bit; 43 | debug_assert!(self.get(element)); 44 | } 45 | 46 | pub fn remove(&mut self, element: E) { 47 | let (word, bit) = IdSet::index(&element); 48 | if let Some(word) = self.words.get_mut(word) { 49 | *word &= !(1 << bit); 50 | } 51 | debug_assert!(!self.get(element)); 52 | } 53 | 54 | pub fn get(&self, element: E) -> bool { 55 | let (word, bit) = IdSet::index(&element); 56 | self.words.get(word).is_some_and(|w| (w & (1 << bit)) != 0) 57 | } 58 | 59 | pub fn clear(&mut self) { 60 | self.words.iter_mut().for_each(|w| *w = 0); 61 | } 62 | 63 | pub fn is_empty(&self) -> bool { 64 | self.words.iter().all(|w| *w == 0) 65 | } 66 | } 67 | 68 | #[cfg(test)] 69 | mod tests { 70 | use super::IdSet; 71 | 72 | #[test] 73 | fn basic() { 74 | let mut set = IdSet::zeros(0); 75 | set.add(42); 76 | assert!(set.get(42)); 77 | assert!(!set.is_empty()); 78 | set.add(43); 79 | set.remove(42); 80 | assert!(!set.get(42)); 81 | assert!(set.get(43)); 82 | set.remove(43); 83 | 84 | for i in 0..100 { 85 | assert!(!set.get(i)); 86 | } 87 | 88 | assert_eq!(set.words.iter().sum::(), 0); 89 | assert!(set.is_empty()); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/datastructures/id_vec.rs: -------------------------------------------------------------------------------- 1 | use std::marker::PhantomData; 2 | use std::ops::{Index, IndexMut}; 3 | 4 | use crate::datastructures::id::Id; 5 | 6 | pub struct IdVec(Vec, PhantomData); 7 | 8 | impl IdVec { 9 | pub fn new(inner: Vec) -> Self { 10 | Self(inner, PhantomData) 11 | } 12 | 13 | pub fn len(&self) -> usize { 14 | self.0.len() 15 | } 16 | 17 | pub fn push(&mut self, value: T) { 18 | self.0.push(value) 19 | } 20 | } 21 | 22 | impl Index for IdVec { 23 | type Output = T; 24 | 25 | fn index(&self, index: I) -> &Self::Output { 26 | &self.0[index.index()] 27 | } 28 | } 29 | impl IndexMut for IdVec { 30 | fn index_mut(&mut self, index: I) -> &mut Self::Output { 31 | &mut self.0[index.index()] 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/datastructures/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod arena; 2 | pub mod id; 3 | pub mod id_set; 4 | pub mod id_vec; 5 | pub mod random; 6 | -------------------------------------------------------------------------------- /src/datastructures/random.rs: -------------------------------------------------------------------------------- 1 | use std::num::Wrapping; 2 | 3 | /// a simple rng for the worklist that behaves like java 4 | pub struct Random { 5 | seed: Wrapping, 6 | } 7 | 8 | const MULTIPLIER: Wrapping = Wrapping(0x5DEECE66D); 9 | const ADDEND: Wrapping = Wrapping(0xB); 10 | const MASK: Wrapping = Wrapping((1 << 48) - 1); 11 | 12 | impl Random { 13 | pub fn with_seed(seed: u64) -> Self { 14 | Self { 15 | seed: (Wrapping(seed) ^ MULTIPLIER) & MASK, 16 | } 17 | } 18 | 19 | fn next(&mut self, bits: u8) -> Wrapping { 20 | self.seed = ((self.seed * MULTIPLIER) + ADDEND) & MASK; 21 | self.seed >> (48 - bits) as usize 22 | } 23 | 24 | pub fn next_int(&mut self, exclusive_bound: i32) -> usize { 25 | assert!(exclusive_bound > 0); 26 | let bound = Wrapping(exclusive_bound as u64); 27 | 28 | let mut r = self.next(31); 29 | let m = bound - Wrapping(1); 30 | 31 | if (bound & m).0 == 0 { 32 | r = (bound * r) >> 31 // if power of two 33 | } else { 34 | let mut u = r; 35 | loop { 36 | r = u % bound; 37 | if u + m >= r { 38 | break; 39 | } 40 | u = self.next(31) 41 | } 42 | } 43 | r.0 as usize 44 | } 45 | } 46 | 47 | #[cfg(test)] 48 | mod tests { 49 | use crate::datastructures::random::Random; 50 | 51 | #[test] 52 | fn test1() { 53 | let mut r = Random::with_seed(123); 54 | assert_eq!(r.next_int(2), 1); 55 | assert_eq!(r.next_int(2), 0); 56 | assert_eq!(r.next_int(4), 3); 57 | assert_eq!(r.next_int(321), 227); 58 | assert_eq!(r.next_int(64), 16); 59 | assert_eq!(r.next_int(93), 14); 60 | assert_eq!(r.next_int(64), 38); 61 | assert_eq!(r.next_int(64), 16); 62 | assert_eq!(r.next_int(11), 8); 63 | } 64 | 65 | #[test] 66 | fn test2() { 67 | // var r = new Random(123); 68 | // IntStream.range(1, 100).map(r::nextInt).toArray() 69 | let mut r = Random::with_seed(123); 70 | let array = (1..100).map(|i| r.next_int(i)).collect::>(); 71 | assert_eq!( 72 | array, 73 | &[ 74 | 0, 0, 2, 1, 0, 5, 3, 2, 8, 3, 0, 6, 7, 5, 7, 10, 10, 5, 18, 16, 14, 3, 4, 2, 3, 3, 75 | 10, 20, 1, 4, 28, 10, 24, 33, 7, 15, 8, 2, 32, 26, 3, 2, 5, 14, 14, 10, 18, 18, 35, 76 | 40, 48, 38, 27, 44, 38, 13, 10, 52, 47, 37, 35, 55, 14, 25, 32, 8, 61, 39, 47, 50, 77 | 64, 37, 3, 13, 51, 73, 44, 16, 9, 30, 48, 23, 59, 81, 14, 73, 5, 82, 51, 64, 42, 78 | 20, 67, 66, 78, 45, 8, 55, 12 79 | ] 80 | ); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(hash_raw_entry)] 2 | #![feature(hash_set_entry)] 3 | 4 | pub mod datastructures; 5 | pub mod sea_of_nodes; 6 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | todo!() 3 | } 4 | -------------------------------------------------------------------------------- /src/sea_of_nodes/graph_visualizer.rs: -------------------------------------------------------------------------------- 1 | use crate::datastructures::id::Id; 2 | use crate::sea_of_nodes::nodes::node::{Scope, Stop}; 3 | use crate::sea_of_nodes::nodes::{Node, Nodes, Op}; 4 | use crate::sea_of_nodes::types::Ty; 5 | use fmt::Write; 6 | use std::collections::HashSet; 7 | use std::fmt; 8 | use std::process::{Command, Stdio}; 9 | use std::sync::Mutex; 10 | use std::time::Duration; 11 | 12 | pub fn run_graphviz_and_chromium(input: String) { 13 | let child = Command::new(&"bash") 14 | .args(["-c", "dot -Tsvg | base64"]) 15 | .stdin(Stdio::piped()) 16 | .stdout(Stdio::piped()) 17 | .stderr(Stdio::piped()) 18 | .spawn() 19 | .unwrap(); 20 | 21 | use std::io::Write; 22 | child 23 | .stdin 24 | .as_ref() 25 | .unwrap() 26 | .write_all(input.as_ref()) 27 | .unwrap(); 28 | 29 | let output = child.wait_with_output().unwrap(); 30 | 31 | // assert_eq!(String::from_utf8(output.stderr).unwrap(), ""); 32 | println!( 33 | "GRAPHVIZ BEGIN:\n{}GRAPHVIZ END", 34 | std::str::from_utf8(&output.stderr).unwrap() 35 | ); 36 | assert_eq!(output.status.code(), Some(0)); 37 | 38 | let url = format!( 39 | "data:image/svg+xml;base64,{}", 40 | std::str::from_utf8(&output.stdout).unwrap() 41 | ); 42 | 43 | static LOCK: Mutex<()> = Mutex::new(()); 44 | let _guard = LOCK.lock().unwrap(); 45 | 46 | // using chromium becasue firefox only displays it after manually selecting the address hitting enter 47 | Command::new(&"chromium") 48 | .arg(url) 49 | .stdin(Stdio::null()) 50 | .stdout(Stdio::null()) 51 | .stderr(Stdio::null()) 52 | .spawn() 53 | .unwrap(); 54 | 55 | std::thread::sleep(Duration::from_millis(1000)); // give it some time... 56 | } 57 | 58 | pub fn generate_dot_output( 59 | sea: &Nodes, 60 | stop: Stop, 61 | scope: Option, 62 | x_scopes: &[Scope], 63 | source: &str, 64 | separate_control_cluster: bool, 65 | ) -> Result { 66 | let all = find_all(sea, &[Some(**stop), scope.map(|s| s.to_node())]); 67 | let mut all = Vec::from_iter(all); 68 | all.sort_by_key(Node::index); 69 | 70 | let mut sb = String::new(); 71 | writeln!(sb, "digraph \"{}\" {{", source.replace("\"", "\\\""))?; 72 | 73 | writeln!(sb, "\trankdir=BT;")?; 74 | // writeln!(sb, "\tordering=\"in\";")?; 75 | writeln!(sb, "\tconcentrate=\"true\";")?; 76 | writeln!(sb, "\tcompound=\"true\";")?; 77 | 78 | do_nodes(&mut sb, &all, separate_control_cluster, sea)?; 79 | for &scope in x_scopes { 80 | if !scope.is_dead(sea) { 81 | do_scope(&mut sb, sea, scope)?; 82 | } 83 | } 84 | node_edges(&mut sb, sea, &all)?; 85 | for &scope in x_scopes { 86 | if !scope.is_dead(sea) { 87 | scope_edges(&mut sb, sea, scope)?; 88 | } 89 | } 90 | writeln!(sb, "}}")?; 91 | Ok(sb) 92 | } 93 | 94 | fn nodes_by_cluster( 95 | sb: &mut String, 96 | do_ctrl: bool, 97 | all: &[Node], 98 | separate_control_cluster: bool, 99 | sea: &Nodes, 100 | ) -> fmt::Result { 101 | if !separate_control_cluster && do_ctrl { 102 | return Ok(()); 103 | } 104 | if do_ctrl { 105 | writeln!(sb, "\tsubgraph cluster_Controls {{")?; 106 | } else { 107 | writeln!(sb, "\tsubgraph cluster_Nodes {{")?; // Magic "cluster_" in the subgraph name 108 | } 109 | 110 | for &n in all.iter() { 111 | if n.is_proj(sea) || n.is_cproj(sea) || n.is_scope_min(sea) || n == **sea.xctrl { 112 | continue; 113 | } 114 | if separate_control_cluster && do_ctrl && !n.is_cfg(sea) { 115 | continue; 116 | } 117 | if separate_control_cluster && !do_ctrl && n.is_cfg(sea) { 118 | continue; 119 | } 120 | 121 | write!(sb, "\t\t{} [ ", n.unique_name(sea))?; 122 | let lab = sea[n].glabel(); 123 | if n.is_multi_node(sea) { 124 | writeln!(sb, "shape=plaintext label=<")?; 125 | writeln!( 126 | sb, 127 | "\t\t\t" 128 | )?; 129 | writeln!(sb, "\t\t\t")?; 130 | write!(sb, "\t\t\t")?; 131 | 132 | let mut do_proj_table = false; 133 | 134 | // differs from java: we cannot mutate the outputs, so we copy 135 | let mut os = sea.outputs[n].clone(); 136 | os.sort_by(|x, y| { 137 | if let (Some(xp), Some(yp)) = (x.to_proj(sea), y.to_proj(sea)) { 138 | sea[xp].index.cmp(&sea[yp].index) 139 | } else { 140 | x.index().cmp(&y.index()) 141 | } 142 | }); 143 | 144 | // n._outputs.sort((x,y) -> x instanceof ProjNode xp && y instanceof ProjNode yp ? (xp._idx - yp._idx) : (x._nid - y._nid)); 145 | for use_ in os { 146 | if let proj @ Op::Proj(p) = &sea[use_] { 147 | if !do_proj_table { 148 | do_proj_table = true; 149 | writeln!(sb, "
{lab}
")?; 150 | writeln!( 151 | sb, 152 | "\t\t\t\t" 153 | )?; 154 | write!(sb, "\t\t\t\t")?; 155 | } 156 | write!(sb, "", p.index, proj.glabel())?; 157 | } 158 | if let proj @ Op::CProj(p) = &sea[use_] { 159 | if !do_proj_table { 160 | do_proj_table = true; 161 | writeln!(sb, "")?; 180 | } 181 | writeln!(sb, "")?; 182 | write!(sb, "\t\t\t
{}")?; 162 | writeln!( 163 | sb, 164 | "\t\t\t\t" 165 | )?; 166 | write!(sb, "\t\t\t\t")?; 167 | } 168 | write!( 169 | sb, 170 | "", 171 | p.index, 172 | proj.glabel() 173 | )?; 174 | } 175 | } 176 | if do_proj_table { 177 | writeln!(sb, "")?; 178 | writeln!(sb, "\t\t\t\t
{}
")?; 179 | write!(sb, "\t\t\t
>\n\t\t")?; 183 | } else { 184 | if n.is_cfg(sea) { 185 | write!(sb, "shape=box style=filled fillcolor=yellow ")?; 186 | } else if n.is_phi(sea) { 187 | write!(sb, "style=filled fillcolor=lightyellow ")?; 188 | } 189 | write!(sb, "label=\"{lab}\" ")?; 190 | } 191 | writeln!(sb, "];")?; 192 | } 193 | 194 | if !separate_control_cluster { 195 | // Force Region & Phis to line up 196 | for &n in all { 197 | if n.is_region(sea) { 198 | write!(sb, "\t\t{{ rank=same; {};", n.print(sea))?; 199 | for &phi in &sea.outputs[n] { 200 | if let Op::Phi(_) = &sea[phi] { 201 | write!(sb, "{};", phi.unique_name(sea))?; 202 | } 203 | } 204 | writeln!(sb, "}}")?; 205 | } 206 | } 207 | } 208 | 209 | writeln!(sb, "\t}}") 210 | } 211 | 212 | fn do_nodes( 213 | sb: &mut String, 214 | all: &[Node], 215 | separate_control_cluster: bool, 216 | sea: &Nodes, 217 | ) -> fmt::Result { 218 | nodes_by_cluster(sb, true, all, separate_control_cluster, sea)?; 219 | nodes_by_cluster(sb, false, all, separate_control_cluster, sea) 220 | } 221 | 222 | fn do_scope(sb: &mut String, sea: &Nodes, scope: Scope) -> fmt::Result { 223 | writeln!(sb, "\tnode [shape=plaintext];")?; 224 | 225 | let last = scope.inputs(sea).len(); 226 | let max = sea[scope].lex_size.len(); 227 | for i in 0..max { 228 | let level = max - i - 1; 229 | let scope_name = make_scope_name(sea, scope, level); 230 | 231 | writeln!(sb, "\tsubgraph cluster_{scope_name} {{")?; 232 | if level == 0 { 233 | let n = scope.mem(sea); 234 | writeln!( 235 | sb, 236 | "\t\t{} [label=\"{}\"];", 237 | n.unique_name(sea), 238 | sea[n].glabel() 239 | )?; 240 | } 241 | writeln!(sb, "\t\t{scope_name} [label=<")?; 242 | writeln!( 243 | sb, 244 | "\t\t\t" 245 | )?; 246 | write!(sb, "\t\t\t")?; 247 | let lex_start = sea[scope].lex_size[level]; 248 | for j in lex_start..last { 249 | let name = sea[scope].vars[j].name; 250 | let port_name = make_port_name(&scope_name, name); 251 | write!(sb, "")?; 252 | } 253 | writeln!(sb, "")?; 254 | writeln!(sb, "\t\t\t
{level}{name}
>];")?; 255 | } 256 | 257 | for _ in 0..max { 258 | writeln!(sb, "\t}}")?; 259 | } 260 | 261 | Ok(()) 262 | } 263 | 264 | fn make_scope_name(sea: &Nodes, scope: Scope, level: usize) -> String { 265 | format!("{}_{level}", scope.unique_name(sea)) 266 | } 267 | 268 | fn make_port_name(scope_name: &str, var_name: &str) -> String { 269 | format!("{scope_name}_{var_name}") 270 | } 271 | 272 | impl Node { 273 | fn is_mem(self, sea: &Nodes) -> bool { 274 | match &sea[self] { 275 | Op::Phi(p) => p.ty.is_mem(), 276 | Op::Proj(_) => self.ty(sea).is_some_and(Ty::is_mem), 277 | Op::Store(_) => true, 278 | _ => false, 279 | } 280 | } 281 | } 282 | 283 | fn node_edges(sb: &mut String, sea: &Nodes, all: &[Node]) -> fmt::Result { 284 | writeln!(sb, "\tedge [ fontname=Helvetica, fontsize=8 ];")?; 285 | for &n in all.iter() { 286 | if n.is_constant(sea) 287 | || n.is_proj(sea) 288 | || n.is_cproj(sea) 289 | || n.is_scope(sea) 290 | || n == **sea.xctrl 291 | { 292 | continue; 293 | } 294 | for (i, def) in sea.inputs[n].iter().enumerate() { 295 | let Some(def) = *def else { continue }; 296 | 297 | if n.is_phi(sea) && def.is_region(sea) { 298 | writeln!( 299 | sb, 300 | "\t{} -> {} [style=dotted taillabel={i}];", 301 | n.unique_name(sea), 302 | def.unique_name(sea) 303 | )?; 304 | continue; 305 | } 306 | 307 | write!(sb, "\t{} -> {}", n.unique_name(sea), def_name(sea, def))?; 308 | 309 | write!(sb, "[taillabel={i}")?; 310 | 311 | if n.is_new(sea) { 312 | write!(sb, " color=green")?; 313 | } else if def.is_cfg(sea) { 314 | write!(sb, " color=red")?; 315 | } else if def.is_mem(sea) { 316 | write!(sb, " color=blue")?; 317 | } 318 | 319 | if i == 2 && (n.is_phi(sea) || n.is_loop(sea)) { 320 | write!(sb, " constraint=false")?; 321 | } 322 | 323 | writeln!(sb, "];")?; 324 | } 325 | } 326 | Ok(()) 327 | } 328 | 329 | fn scope_edges(sb: &mut String, sea: &Nodes, scope: Scope) -> fmt::Result { 330 | writeln!(sb, "\tedge [style=dashed color=cornflowerblue];")?; 331 | let mut level = 0; 332 | for v in &sea[scope].vars { 333 | let mut def = scope.inputs(sea)[v.index]; 334 | while let Some(lazy) = def.and_then(|d| d.to_scope(sea)) { 335 | def = lazy.inputs(sea)[v.index]; 336 | } 337 | let Some(def) = def else { continue }; 338 | while level < sea[scope].lex_size.len() && v.index >= sea[scope].lex_size[level] { 339 | level += 1; 340 | } 341 | let scope_name = make_scope_name(sea, scope, level - 1); 342 | let port_name = make_port_name(&scope_name, v.name); 343 | let def_name = def_name(sea, def); 344 | writeln!(sb, "\t{scope_name}:\"{port_name}\" -> {def_name};")?; 345 | } 346 | Ok(()) 347 | } 348 | 349 | fn def_name(sea: &Nodes, def: Node) -> String { 350 | match &sea[def] { 351 | Op::CProj(p) | Op::Proj(p) => { 352 | let mname = sea.inputs[def][0].unwrap().unique_name(sea); 353 | format!("{mname}:p{}", p.index) 354 | } 355 | _ => def.unique_name(sea), 356 | } 357 | } 358 | 359 | fn find_all(nodes: &Nodes, leaves: &[Option]) -> HashSet { 360 | let mut all = HashSet::new(); 361 | for &node in leaves.iter().flatten() { 362 | walk(nodes, &mut all, Some(node)); 363 | } 364 | all 365 | } 366 | 367 | fn walk(nodes: &Nodes, all: &mut HashSet, node: Option) { 368 | let Some(node) = node else { 369 | return; 370 | }; 371 | if node == Node::DUMMY { 372 | return; 373 | } 374 | 375 | if all.contains(&node) { 376 | return; 377 | } 378 | 379 | all.insert(node); 380 | 381 | for n in &nodes.inputs[node] { 382 | walk(nodes, all, *n); 383 | } 384 | 385 | for n in &nodes.outputs[node] { 386 | walk(nodes, all, Some(*n)); 387 | } 388 | } 389 | -------------------------------------------------------------------------------- /src/sea_of_nodes/location.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::{Display, Formatter}; 2 | 3 | use crate::sea_of_nodes::types::Ty; 4 | 5 | pub struct ParserLocation { 6 | pub line: u32, 7 | pub column: u32, 8 | } 9 | 10 | #[derive(Eq, PartialEq, Copy, Clone, Hash, Debug)] 11 | pub struct Location<'t> { 12 | pub module: Ty<'t>, 13 | pub line: u32, 14 | pub column: u32, 15 | } 16 | 17 | impl Display for Location<'_> { 18 | fn fmt(&self, _f: &mut Formatter<'_>) -> std::fmt::Result { 19 | // let module = self.module.unwrap_module(); 20 | // let file = module.file.to_string_lossy(); 21 | // write!( 22 | // f, 23 | // "{} {:?}:{}:{}", 24 | // module.ast.name, file, self.line, self.column 25 | // ) 26 | todo!() 27 | } 28 | } 29 | 30 | impl<'t> Location<'t> { 31 | pub fn new(location: ParserLocation, module: Ty<'t>) -> Self { 32 | // assert!(matches!(*module, Type::Module(_))); 33 | Self { 34 | module, 35 | line: location.line, 36 | column: location.column, 37 | } 38 | } 39 | } 40 | 41 | impl ParserLocation { 42 | pub fn with_module(self, module: Ty) -> Location { 43 | Location::new(self, module) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/sea_of_nodes/mod.rs: -------------------------------------------------------------------------------- 1 | mod global_code_motion; 2 | pub mod graph_visualizer; 3 | pub mod ir_printer; 4 | pub mod location; 5 | pub mod nodes; 6 | pub mod parser; 7 | #[cfg(any(test, feature = "fuzzing"))] 8 | pub mod tests; 9 | pub mod types; 10 | -------------------------------------------------------------------------------- /src/sea_of_nodes/nodes/cfg.rs: -------------------------------------------------------------------------------- 1 | use crate::datastructures::id::Id; 2 | use crate::datastructures::id_vec::IdVec; 3 | use crate::sea_of_nodes::nodes::node::{CProj, If, Load, Loop, Return, Stop}; 4 | use crate::sea_of_nodes::nodes::{Cfg, Node, Nodes, Op, Start}; 5 | use std::num::NonZeroU32; 6 | 7 | /// Control Flow Graph Nodes 8 | /// 9 | /// CFG nodes have a immediate dominator depth (idepth) and a loop nesting 10 | /// depth(loop_depth). 11 | pub struct CfgData { 12 | /// Immediate dominator tree depth, used to approximate a real IDOM during 13 | /// parsing where we do not have the whole program, and also peepholes 14 | /// change the CFG incrementally. 15 | /// 16 | /// See Wikipedia: Dominator 17 | idepth: u32, 18 | /// Anti-dependence field support 19 | pub anti: Option, 20 | ltree: Option, 21 | pre: u32, 22 | } 23 | impl CfgData { 24 | pub fn new() -> Self { 25 | Self { 26 | idepth: 0, 27 | anti: None, 28 | ltree: None, 29 | pre: 0, 30 | } 31 | } 32 | } 33 | 34 | #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] 35 | pub struct LoopTree(NonZeroU32); 36 | impl Id for LoopTree { 37 | fn index(&self) -> usize { 38 | self.0.get() as usize 39 | } 40 | } 41 | 42 | pub struct LoopTreeData { 43 | par: Option, 44 | head: Loop, 45 | depth: u32, 46 | } 47 | impl LoopTreeData { 48 | pub const DUMMY: Self = Self { 49 | par: None, 50 | head: Loop::DUMMY, 51 | depth: 0, 52 | }; 53 | } 54 | 55 | impl LoopTree { 56 | fn depth(self, data: &mut IdVec) -> u32 { 57 | if data[self].depth == 0 { 58 | if let Some(par) = data[self].par { 59 | data[self].depth = par.depth(data) + 1; 60 | } 61 | } 62 | data[self].depth 63 | } 64 | } 65 | 66 | impl Cfg { 67 | pub fn cfg(self, idx: usize, sea: &Nodes) -> Option { 68 | self.inputs(sea)[idx].map(|n| n.to_cfg(sea).unwrap()) 69 | } 70 | 71 | pub fn idepth(self, sea: &mut Nodes) -> u32 { 72 | if sea.cfg[self].idepth != 0 { 73 | return sea.cfg[self].idepth; 74 | } 75 | let d = match sea[*self] { 76 | Op::Start(_) => return 0, // uncached 77 | Op::Stop | Op::Region => (0..self.inputs(sea).len()) 78 | .filter_map(|i| self.cfg(i, sea).map(|d| d.idepth(sea))) 79 | .max() 80 | .unwrap(), 81 | _ => self.idom(sea).unwrap().idepth(sea), 82 | } + 1; 83 | sea.cfg[self].idepth = d; 84 | d 85 | } 86 | 87 | pub fn idom(self, sea: &mut Nodes) -> Option { 88 | self.idom_dep(None, sea) 89 | } 90 | 91 | /// Return the immediate dominator of this Node and compute dom tree depth. 92 | pub fn idom_dep(self, dep: Option, sea: &mut Nodes) -> Option { 93 | match sea[*self] { 94 | Op::Loop => self.cfg(1, sea), 95 | Op::Region => { 96 | let mut lca = None; 97 | // Walk the LHS & RHS idom trees in parallel until they match, or either fails. 98 | // Because this does not cache, it can be linear in the size of the program. 99 | for i in 1..self.inputs(sea).len() { 100 | lca = Some(self.cfg(i, sea).unwrap().idom_2(lca, dep, sea)); 101 | } 102 | lca 103 | } 104 | Op::Start(_) | Op::Stop => None, 105 | _ => self.cfg(0, sea), 106 | } 107 | } 108 | 109 | /// Return the LCA of two idoms 110 | pub fn idom_2(self, rhs: Option, dep: Option, sea: &mut Nodes) -> Cfg { 111 | let Some(mut rhs) = rhs else { 112 | return self; 113 | }; 114 | let mut lhs = self; 115 | while lhs != rhs { 116 | let comp = lhs.idepth(sea) as i64 - rhs.idepth(sea) as i64; 117 | if comp >= 0 { 118 | if let Some(dep) = dep { 119 | lhs.add_dep(dep, sea); 120 | } 121 | lhs = lhs.idom(sea).unwrap() 122 | } 123 | if comp <= 0 { 124 | if let Some(dep) = dep { 125 | rhs.add_dep(dep, sea); 126 | } 127 | rhs = rhs.idom(sea).unwrap() 128 | } 129 | } 130 | lhs 131 | } 132 | 133 | pub fn loop_depth(self, sea: &mut Nodes) -> u32 { 134 | sea.cfg[self] 135 | .ltree 136 | .map(|l| l.depth(&mut sea.loop_tree)) 137 | .unwrap_or(0) 138 | } 139 | } 140 | 141 | impl Start { 142 | /// ------------------------------------------------------------------------ 143 | /// Tag all CFG Nodes with their containing LoopNode; LoopNodes themselves 144 | /// also refer to *their* containing LoopNode, as well as have their depth. 145 | /// Start is a LoopNode which contains all at depth 1. 146 | pub fn build_loop_tree(self, stop: Stop, sea: &mut Nodes) { 147 | let l = LoopTree(NonZeroU32::try_from(sea.loop_tree.len() as u32).unwrap()); 148 | sea.loop_tree.push(LoopTreeData { 149 | par: None, 150 | head: *self, 151 | depth: 0, 152 | }); 153 | 154 | sea.cfg[***self].ltree = Some(l); 155 | sea.cfg[*stop].ltree = Some(l); 156 | sea.cfg[*sea.xctrl].ltree = Some(l); 157 | 158 | let mut post = IdVec::new(vec![false; sea.len()]); 159 | self.blt_walk(2, stop, &mut post, sea); 160 | } 161 | } 162 | impl Cfg { 163 | fn blt_walk( 164 | self, 165 | mut pre: u32, 166 | stop: Stop, 167 | post: &mut IdVec, 168 | sea: &mut Nodes, 169 | ) -> u32 { 170 | // Pre-walked? 171 | if sea.cfg[self].pre != 0 { 172 | return pre; 173 | } 174 | sea.cfg[self].pre = pre; 175 | pre += 1; 176 | 177 | // Pre-walk 178 | for use_ in 0..sea.outputs[self].len() { 179 | let use_ = sea.outputs[self][use_]; 180 | if use_ != Node::DUMMY { 181 | if let Some(usecfg) = use_.to_cfg(sea) { 182 | pre = usecfg.blt_walk(pre, stop, post, sea); 183 | } 184 | } 185 | } 186 | 187 | // Post-order work: find innermost loop 188 | let mut innner = None; 189 | 190 | for use_ in 0..sea.outputs[self].len() { 191 | let use_ = sea.outputs[self][use_]; 192 | if use_ == Node::DUMMY { 193 | continue; 194 | } 195 | let Some(usecfg) = use_.to_cfg(sea) else { 196 | continue; 197 | }; 198 | // Child visited but not post-visited? 199 | let mut ltree: LoopTree; 200 | if !post[usecfg] { 201 | // Must be a backedge to a LoopNode then 202 | ltree = LoopTree(NonZeroU32::try_from(sea.loop_tree.len() as u32).unwrap()); 203 | sea.loop_tree.push(LoopTreeData { 204 | par: None, 205 | head: usecfg.to_loop(sea).unwrap(), 206 | depth: 0, 207 | }); 208 | sea.cfg[usecfg].ltree = Some(ltree); 209 | } else { 210 | // Take child's loop choice, which must exist 211 | ltree = sea.cfg[usecfg].ltree.unwrap(); 212 | 213 | // If falling into a loop, use the target loop's parent instead 214 | if **sea.loop_tree[ltree].head == usecfg { 215 | if sea.loop_tree[ltree].par.is_none() { 216 | // This loop never had an If test choose to take its 217 | // exit, i.e. it is a no-exit infinite loop. 218 | sea.loop_tree[ltree].head.force_exit(stop, sea); 219 | sea.loop_tree[ltree].par = sea.cfg[*stop].ltree; 220 | } 221 | ltree = sea.loop_tree[ltree].par.unwrap(); 222 | } 223 | } 224 | // Sort inner loops. The decision point is some branch far removed 225 | // from either loop head OR either backedge so requires pre-order 226 | // numbers to figure out innermost. 227 | let Some(i) = innner else { 228 | innner = Some(ltree); 229 | continue; 230 | }; 231 | if i == ltree { 232 | continue; // No change 233 | } 234 | let (outer, i2) = if sea.cfg[**sea.loop_tree[ltree].head].pre 235 | > sea.cfg[**sea.loop_tree[i].head].pre 236 | { 237 | (i, ltree) 238 | } else { 239 | (ltree, i) 240 | }; 241 | innner = Some(i2); 242 | sea.loop_tree[i2].par = Some(outer); 243 | } 244 | 245 | // Set selected loop 246 | if innner.is_some() { 247 | sea.cfg[self].ltree = innner; 248 | } 249 | 250 | // Tag as post-walked 251 | post[self] = true; 252 | pre 253 | } 254 | } 255 | 256 | impl Loop { 257 | /// If this is an unreachable loop, it may not have an exit. If it does not 258 | /// (i.e., infinite loop), force an exit to make it reachable. 259 | fn force_exit(self, stop: Stop, sea: &mut Nodes) { 260 | // Walk the backedge, then immediate dominator tree util we hit this 261 | // Loop again. If we ever hit a CProj from an If (as opposed to 262 | // directly on the If) we found our exit. 263 | let mut x = self.back(sea); 264 | while x != self.to_cfg() { 265 | if let Some(exit) = x.to_cproj(sea) { 266 | if let Some(iff) = exit.inputs(sea)[0].unwrap().to_if(sea) { 267 | let other = iff.cproj(1 - sea[exit].index, sea).unwrap(); 268 | if other.loop_depth(sea) < self.loop_depth(sea) { 269 | return; 270 | } 271 | } 272 | } 273 | x = x.idom(sea).unwrap() 274 | } 275 | 276 | // Found a no-exit loop. Insert an exit 277 | let iff = If::new(*self.back(sea), None, sea); 278 | for i in 0..sea.outputs[self].len() { 279 | let use_ = sea.outputs[self][i]; 280 | if use_ != Node::DUMMY { 281 | if let Some(phi) = use_.to_phi(sea) { 282 | iff.add_def(*phi, sea); 283 | } 284 | } 285 | } 286 | 287 | let t = CProj::new(**iff, 0, "True", sea); 288 | let f = CProj::new(**iff, 1, "False", sea); 289 | self.set_def(2, f.to_node(), sea); 290 | stop.add_def(**Return::new(**t, *sea.zero, None, sea), sea); 291 | } 292 | } 293 | -------------------------------------------------------------------------------- /src/sea_of_nodes/nodes/gvn.rs: -------------------------------------------------------------------------------- 1 | use crate::datastructures::id_vec::IdVec; 2 | use crate::sea_of_nodes::nodes::{Node, Nodes, Op, OpVec}; 3 | use std::collections::hash_map::RawEntryMut; 4 | use std::hash::{BuildHasher, DefaultHasher, Hash, Hasher}; 5 | use std::num::NonZeroU32; 6 | 7 | /// NOTE: we derive Eq for re-hashing and removal, but for deduplication lookups we use the more relaxed `Self::equals` 8 | #[derive(Copy, Clone, Eq, PartialEq)] 9 | pub struct GvnEntry { 10 | hash: NonZeroU32, 11 | node: Node, 12 | } 13 | 14 | impl Hash for GvnEntry { 15 | fn hash(&self, state: &mut H) { 16 | self.hash.hash(state); 17 | } 18 | } 19 | 20 | impl Node { 21 | /// Returns a different semantically equal node, if one is present in the gvn table 22 | /// or inserts this one if not already present. 23 | pub fn global_value_numbering(self, sea: &mut Nodes) -> Option { 24 | if sea.hash[self].is_none() { 25 | let entry = GvnEntry { 26 | hash: self.hash_code(sea), 27 | node: self, 28 | }; 29 | let h = sea.gvn.hasher().hash_one(entry); 30 | match sea.gvn.raw_entry_mut().from_hash(h, |o| { 31 | entry.hash == o.hash && entry.node.equals(o.node, &sea.ops, &sea.inputs) 32 | }) { 33 | RawEntryMut::Vacant(v) => { 34 | v.insert(entry, ()); // Put in table now 35 | sea.hash[self] = Some(entry.hash); // hash is set iff in table 36 | } 37 | RawEntryMut::Occupied(o) => { 38 | return Some(o.key().node); 39 | } 40 | } 41 | } 42 | None 43 | } 44 | 45 | /// If the _hash is set, then the Node is in the GVN table; remove it. 46 | pub fn unlock(self, sea: &mut Nodes) { 47 | if let Some(hash) = sea.hash[self].take() { 48 | sea.gvn 49 | .remove(&GvnEntry { hash, node: self }) 50 | .expect("hash is set iff in table"); 51 | } 52 | } 53 | 54 | /// Two nodes are equal if they have the same inputs and the same "opcode" 55 | /// which means the same Java class, plus same internal parts. 56 | fn equals(self, that: Node, ops: &OpVec, inputs: &IdVec>>) -> bool { 57 | if self == that { 58 | return true; 59 | } 60 | 61 | if ops[self].operation() != ops[that].operation() { 62 | return false; 63 | } 64 | 65 | if inputs[self] != inputs[that] { 66 | return false; 67 | } 68 | 69 | match (&ops[self], &ops[that]) { 70 | (Op::Constant(c1), Op::Constant(c2)) => c1 == c2, 71 | (Op::Phi(_) | Op::Region { .. } | Op::Loop, _) => { 72 | !Nodes::in_progress(ops, inputs, self) 73 | } 74 | (Op::Proj(p1), Op::Proj(p2)) => p1.index == p2.index, 75 | (Op::CProj(p1), Op::CProj(p2)) => p1.index == p2.index, 76 | (Op::Load(m1), Op::Load(m2)) => m1.alias == m2.alias, 77 | (Op::Store(m1), Op::Store(m2)) => m1.alias == m2.alias, 78 | (Op::New(_), Op::New(_)) => false, 79 | _ => true, 80 | } 81 | } 82 | 83 | /// Hash of opcode and inputs 84 | fn hash_code(self, sea: &mut Nodes) -> NonZeroU32 { 85 | if let Some(hash) = sea.hash[self] { 86 | return hash; 87 | } 88 | 89 | let h = &mut DefaultHasher::new(); 90 | sea[self].operation().hash(h); 91 | match &sea[self] { 92 | Op::Constant(c) => c.hash(h), 93 | Op::Proj(p) => p.index.hash(h), 94 | Op::CProj(p) => p.index.hash(h), 95 | Op::Load(m) => m.alias.hash(h), 96 | Op::Store(m) => m.alias.hash(h), 97 | _ => {} 98 | }; 99 | self.inputs(sea).hash(h); 100 | 101 | let hash = h.finish(); 102 | let mut hash = (hash >> 32) as u32 ^ hash as u32; 103 | if hash == 0 { 104 | hash = 0xDEADBEEF; // Bad hash, so use some junky thing 105 | } 106 | 107 | NonZeroU32::new(hash).unwrap() 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/sea_of_nodes/nodes/iter_peeps.rs: -------------------------------------------------------------------------------- 1 | //! The IterPeeps runs after parsing. It iterates the peepholes to a fixed point 2 | //! so that no more peepholes apply. This should be linear because peepholes rarely 3 | //! (never?) increase code size. The graph should monotonically reduce in some 4 | //! dimension, which is usually size. It might also reduce in e.g. number of 5 | //! MulNodes or Load/Store nodes, swapping out more "expensive" Nodes for cheaper 6 | //! ones. 7 | //! 8 | //! The theoretical overall worklist is mindless just grabbing the next thing and 9 | //! doing it. If the graph changes, put the neighbors on the worklist. 10 | //! 11 | //! Lather, Rinse, Repeat until the worklist runs dry. 12 | //! 13 | //! The main issues we have to deal with: 14 | //! 15 | //!
    16 | //!
  • Nodes have uses; replacing some set of Nodes with another requires more graph 17 | //! reworking. Not rocket science, but it can be fiddly. Its helpful to have a 18 | //! small set of graph munging utilities, and the strong invariant that the graph 19 | //! is stable and correct between peepholes. In our case `Node.subsume` does 20 | //! most of the munging, building on our prior stable Node utilities.
  • 21 | //! 22 | //!
  • Changing a Node also changes the graph "neighborhood". The neighbors need to 23 | //! be checked to see if THEY can also peephole, and so on. After any peephole 24 | //! or graph update we put a Nodes uses and defs on the worklist.
  • 25 | //! 26 | //!
  • Our strong invariant is that for all Nodes, either they are on the worklist 27 | //! OR no peephole applies. This invariant is easy to check, although expensive. 28 | //! Basically the normal "iterate peepholes to a fixed point" is linear, and this 29 | //! check is linear at each peephole step... so quadratic overall. Its a useful 30 | //! assert, but one we can disable once the overall algorithm is stable - and 31 | //! then turn it back on again when some new set of peepholes is misbehaving. 32 | //! The code for this is turned on in `IterPeeps.iterate` as `assert 33 | //! progressOnList(stop);`
  • 34 | //!
35 | 36 | use crate::datastructures::id::Id; 37 | use crate::datastructures::id_set::IdSet; 38 | use crate::datastructures::random::Random; 39 | use crate::sea_of_nodes::nodes::node::Stop; 40 | use crate::sea_of_nodes::nodes::{Node, Nodes}; 41 | 42 | pub struct IterPeeps { 43 | work: WorkList, 44 | pub(crate) mid_assert: bool, 45 | } 46 | 47 | impl IterPeeps { 48 | pub fn new() -> Self { 49 | Self { 50 | work: WorkList::with_seed(123), 51 | mid_assert: false, 52 | } 53 | } 54 | pub fn add(&mut self, node: Node) { 55 | if node != Node::DUMMY { 56 | self.work.push(node); 57 | } 58 | } 59 | } 60 | 61 | impl Node { 62 | /// Add a node to the list of dependencies. Only add it if its not an input 63 | /// or output of this node, that is, it is at least one step away. The node 64 | /// being added must benefit from this node being peepholed. 65 | pub fn add_dep(self, dep: impl Into, sea: &mut Nodes) -> Node { 66 | let dep = dep.into(); 67 | // Running peepholes during the big assert cannot have side effects 68 | // like adding dependencies. 69 | if sea.iter_peeps.mid_assert { 70 | return self; 71 | } 72 | if !sea.deps[self].contains(&dep) 73 | && !self.inputs(sea).contains(&Some(dep)) 74 | && !sea.outputs[self].contains(&dep) 75 | { 76 | // Not on list and not an immediate neighbor 77 | sea.deps[self].push(dep); 78 | } 79 | self 80 | } 81 | 82 | /// Move the dependents onto a worklist, and clear for future dependents. 83 | pub fn move_deps_to_worklist(self, sea: &mut Nodes) { 84 | for dep in sea.deps[self].drain(..) { 85 | sea.iter_peeps.add(dep); 86 | } 87 | } 88 | } 89 | 90 | impl<'t> Nodes<'t> { 91 | /// Iterate peepholes to a fixed point 92 | pub fn iterate(&mut self, stop: Stop) { 93 | debug_assert!(self.progress_on_list(**stop)); 94 | while let Some(n) = self.iter_peeps.work.pop() { 95 | if n.is_dead(self) { 96 | continue; 97 | } 98 | 99 | if let Some(x) = n.peephole_opt(self) { 100 | if x.is_dead(self) { 101 | continue; 102 | } 103 | 104 | // peepholeOpt can return brand-new nodes, needing an initial type set 105 | if self.ty[x].is_none() { 106 | x.set_type(x.compute(self), self); 107 | } 108 | 109 | // Changes require neighbors onto the worklist 110 | if x != n || !x.is_constant(self) { 111 | // All outputs of n (changing node) not x (prior existing node). 112 | for &z in &self.outputs[n] { 113 | self.iter_peeps.work.push(z); 114 | } 115 | 116 | // Everybody gets a free "go again" in case they didn't get 117 | // made in their final form. 118 | self.iter_peeps.work.push(x); 119 | 120 | // If the result is not self, revisit all inputs (because 121 | // there's a new user), and replace in the graph. 122 | if x != n { 123 | for &z in self.inputs[n].iter().flatten() { 124 | self.iter_peeps.work.push(z); 125 | } 126 | n.subsume(x, self); 127 | } 128 | } 129 | 130 | // If there are distant neighbors, move to worklist 131 | n.move_deps_to_worklist(self); 132 | debug_assert!(self.progress_on_list(**stop)); // Very expensive assert 133 | } 134 | if n.is_unused(self) && !n.is_stop(self) { 135 | n.kill(self); // just plain dead 136 | } 137 | } 138 | } 139 | 140 | pub fn type_check(&mut self, stop: Stop) -> Result<(), String> { 141 | match self.walk_non_reentrant(**stop, |sea, n| n.err(sea)) { 142 | Some(err) => Err(err), 143 | None => Ok(()), 144 | } 145 | } 146 | 147 | /// Visit ALL nodes and confirm the invariant: 148 | /// Either you are on the WORK worklist OR running `iter()` makes no progress. 149 | /// 150 | /// This invariant ensures that no progress is missed, i.e., when the 151 | /// worklist is empty we have indeed done all that can be done. To help 152 | /// with debugging, the {@code assert} is broken out in a place where it is easy to 153 | /// stop if a change is found. 154 | /// 155 | /// Also, the normal usage of `iter()` may attempt peepholes with distance 156 | /// neighbors and these should fail, but will then try to add dependencies 157 | /// {@link #Node.addDep} which is a side effect in an assert. The {@link 158 | /// #midAssert} is used to stop this side effect. 159 | fn progress_on_list(&mut self, stop: Node) -> bool { 160 | self.iter_peeps.mid_assert = true; 161 | let (old_cnt, old_nop) = (self.iter_cnt, self.iter_nop_cnt); 162 | 163 | let changed = self.walk_non_reentrant(stop, |sea: &mut Self, n| { 164 | let mut m = n; 165 | // Types must be forwards, even if on worklist 166 | if n.compute(sea).isa(n.ty(sea).unwrap(), sea.types) 167 | && (!n.is_keep(sea) || n.index() <= 6) 168 | { 169 | if sea.iter_peeps.work.on(n) { 170 | return None; 171 | } 172 | m = n.peephole_opt(sea)?; 173 | } 174 | println!("BREAK HERE FOR BUG"); 175 | Some(m) 176 | }); 177 | 178 | self.iter_cnt = old_cnt; 179 | self.iter_nop_cnt = old_nop; 180 | 181 | self.iter_peeps.mid_assert = false; 182 | changed.is_none() 183 | } 184 | } 185 | 186 | /// Classic WorkList, with a fast add/remove, dup removal, random pull. 187 | /// The Node's nid is used to check membership in the worklist. 188 | pub struct WorkList { 189 | es: Vec, 190 | /// Bit set if node is on WorkList 191 | on: IdSet, 192 | /// For randomizing pull from the WorkList 193 | r: Random, 194 | /// Useful stat - how many nodes are processed in the post parse iterative opt 195 | total_work: usize, 196 | } 197 | 198 | impl WorkList { 199 | pub fn with_seed(seed: u64) -> Self { 200 | Self { 201 | es: vec![], 202 | on: IdSet::zeros(0), 203 | r: Random::with_seed(seed), 204 | total_work: 0, 205 | } 206 | } 207 | 208 | /// push node if not present 209 | pub fn push(&mut self, node: Node) { 210 | if !self.on.get(node) { 211 | self.on.add(node); 212 | self.es.push(node); 213 | self.total_work += 1; 214 | } 215 | } 216 | 217 | /// True if Node is on the WorkList 218 | pub fn on(&self, node: Node) -> bool { 219 | self.on.get(node) 220 | } 221 | 222 | /// Removes a random Node from the WorkList 223 | pub fn pop(&mut self) -> Option { 224 | if self.es.is_empty() { 225 | return None; 226 | } 227 | let idx = self.r.next_int(self.es.len() as i32); 228 | let node = self.es.swap_remove(idx); 229 | self.on.remove(node); 230 | Some(node) 231 | } 232 | } 233 | -------------------------------------------------------------------------------- /src/sea_of_nodes/nodes/print.rs: -------------------------------------------------------------------------------- 1 | use std::cell::RefCell; 2 | use std::fmt; 3 | use std::fmt::Display; 4 | 5 | use crate::datastructures::id_set::IdSet; 6 | use crate::sea_of_nodes::nodes::node::IfOp; 7 | use crate::sea_of_nodes::nodes::{Node, Nodes, Op}; 8 | 9 | pub struct PrintNodes<'a, 't> { 10 | node: Option, 11 | sea: &'a Nodes<'t>, 12 | visited: RefCell>, 13 | } 14 | 15 | impl Display for PrintNodes<'_, '_> { 16 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 17 | PrintNodes2 { 18 | node: self.node, 19 | sea: self.sea, 20 | visited: &self.visited, 21 | } 22 | .fmt(f) 23 | } 24 | } 25 | 26 | pub struct PrintNodes2<'a, 'b, 't> { 27 | node: Option, 28 | sea: &'a Nodes<'t>, 29 | visited: &'b RefCell>, 30 | } 31 | 32 | impl Node { 33 | pub(crate) fn print<'a, 't>(self, sea: &'a Nodes<'t>) -> PrintNodes<'a, 't> { 34 | PrintNodes { 35 | node: Some(self), 36 | sea, 37 | visited: RefCell::new(IdSet::zeros(sea.len())), 38 | } 39 | } 40 | 41 | // Unique label for graph visualization, e.g. "Add12" or "Region30" or "EQ99" 42 | pub fn unique_name(self, sea: &Nodes) -> String { 43 | match &sea[self] { 44 | Op::Constant(_) => format!("Con_{self}"), 45 | Op::Cast(_) => format!("Cast_{self}"), 46 | // Get rid of $ as graphviz doesn't like it 47 | _ => format!("{}{}", sea[self].label().replace('$', ""), self), 48 | } 49 | } 50 | } 51 | 52 | impl Display for PrintNodes2<'_, '_, '_> { 53 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 54 | let Some(node) = self.node else { 55 | return write!(f, ""); 56 | }; 57 | 58 | let sea = self.sea; 59 | let inputs = &sea.inputs[node]; 60 | 61 | if self.visited.borrow().get(node) && !node.is_constant(sea) { 62 | return write!(f, "{}", sea[node].label()); 63 | } 64 | self.visited.borrow_mut().add(node); 65 | 66 | let print = |node| Self { 67 | node, 68 | sea, 69 | visited: self.visited, 70 | }; 71 | let input = |index| print(inputs[index]); 72 | let mut binary = |op: &str| write!(f, "({}{}{})", input(1), op, input(2)); 73 | 74 | match &sea[node] { 75 | _ if node.is_dead(sea) => write!(f, "{}:DEAD", node.unique_name(sea)), 76 | Op::Add | Op::AddF => binary("+"), 77 | Op::Sub | Op::SubF => binary("-"), 78 | Op::Mul | Op::MulF => binary("*"), 79 | Op::Div | Op::DivF => binary("/"), 80 | Op::And => binary("&"), 81 | Op::Or => binary("|"), 82 | Op::Xor => binary("^"), 83 | Op::Sar => binary(">>"), 84 | Op::Shl => binary("<<"), 85 | Op::Shr => binary(">>>"), 86 | Op::Bool(op) => binary(op.str()), 87 | Op::Return => write!(f, "return {};", input(1)), 88 | Op::Constant(ty) => write!(f, "{}", ty), 89 | Op::XCtrl => write!(f, "Xctrl"), 90 | n @ Op::Start { .. } => write!(f, "{}", n.label()), 91 | Op::Minus | Op::MinusF => write!(f, "(-{})", input(1)), 92 | Op::RoundF32 => write!(f, "((f32){})", input(1)), 93 | Op::ToFloat => write!(f, "(flt){}", input(1)), 94 | Op::ReadOnly => write!(f, "(const){}", input(1)), 95 | Op::Scope(_) => { 96 | // sb.append("Scope[ "); 97 | // int j=1; 98 | // for( int i=0; i 0 { 107 | write!(f, ", ")?; 108 | } 109 | if Some(&i) == op.lex_size.get(j) { 110 | write!(f, "| ")?; 111 | j += 1; 112 | } 113 | let v = &op.vars[i]; 114 | // differs from java. We can't resolve the type here 115 | write!(f, "{} ", v.ty)?; 116 | if v.final_field { 117 | write!(f, "!")?; 118 | } 119 | write!(f, "{}=", v.name)?; 120 | let mut n = inputs[i]; 121 | while let Some(loop_) = n.and_then(|n| n.to_scope(sea)) { 122 | write!(f, "Lazy_")?; 123 | n = loop_.inputs(sea)[i]; 124 | } 125 | if n.is_none() { 126 | write!(f, "___")?; 127 | } else { 128 | write!(f, "{}", print(n))?; 129 | } 130 | } 131 | // sb.setLength(sb.length()-2); 132 | write!(f, "]") 133 | } 134 | Op::ScopeMin => { 135 | write!(f, "MEM[")?; 136 | for j in 2..inputs.len() { 137 | write!(f, " {j}:")?; 138 | let mut n = inputs[j]; 139 | while let Some(loop_) = n.and_then(|n| n.to_scope(sea)) { 140 | write!(f, "Lazy_")?; 141 | n = loop_.inputs(sea)[j]; 142 | } 143 | if n.is_none() { 144 | write!(f, "___")?; 145 | } else { 146 | write!(f, "{}", print(n))?; 147 | } 148 | } 149 | write!(f, "]") 150 | } 151 | Op::Struct(s) => { 152 | write!(f, "{}", s.name())?; 153 | for (i, &inp) in inputs.iter().enumerate() { 154 | if i == 0 { 155 | write!(f, " {{")?; 156 | } else { 157 | write!(f, "; ")?; 158 | } 159 | let t = inp.map(|i| i.ty(sea).unwrap()).unwrap_or(sea.tys.bot); 160 | write!(f, "{}:{t}", s.fields()[i].fname)?; 161 | } 162 | write!(f, "}}") 163 | } 164 | Op::Not => write!(f, "(!{})", input(1)), 165 | Op::Proj(proj) | Op::CProj(proj) => write!(f, "{}", proj.label), 166 | Op::If(op) => match op { 167 | IfOp::Cond => write!(f, "if( {} )", input(1)), 168 | IfOp::Never => write!(f, "Never"), 169 | }, 170 | Op::Phi(_) => { 171 | if !sea.instanceof_region(inputs[0]) 172 | || Nodes::in_progress(&sea.ops, &sea.inputs, sea.inputs[node][0].unwrap()) 173 | { 174 | write!(f, "Z")?; 175 | } 176 | write!(f, "Phi(")?; 177 | for (i, input) in inputs.iter().enumerate() { 178 | if i > 0 { 179 | write!(f, ",")?; 180 | } 181 | if input.is_none() { 182 | write!(f, "____")?; 183 | } else { 184 | write!(f, "{}", print(*input))?; 185 | } 186 | } 187 | write!(f, ")") 188 | } 189 | n @ Op::Region { .. } | n @ Op::Loop => write!(f, "{}", n.label()), 190 | Op::Stop => { 191 | if let ret @ Some(_) = node.unique_input(sea) { 192 | write!(f, "{}", print(ret)) 193 | } else { 194 | write!(f, "Stop[ ")?; 195 | for ret in inputs { 196 | write!(f, "{} ", print(*ret))?; 197 | } 198 | write!(f, "]") 199 | } 200 | } 201 | n @ Op::Cast(_) => { 202 | write!(f, "{}{}", n.label(), input(1)) 203 | } 204 | Op::New(t) => write!(f, "new {}", t.data().to.str()), 205 | Op::Load(l) => write!(f, ".{}", l.name), 206 | // Op::Store(s) => write!(f, ".{}={};", s.name, input(4)), 207 | Op::Store(s) => match inputs[4] { 208 | None => write!(f, ".{}=null;", s.name), 209 | Some(val) => write!(f, ".{}={};", s.name, val.print(sea)), 210 | }, 211 | Op::Cfg => unreachable!(), 212 | } 213 | } 214 | } 215 | -------------------------------------------------------------------------------- /src/sea_of_nodes/tests.rs: -------------------------------------------------------------------------------- 1 | use crate::datastructures::arena::DroplessArena; 2 | use crate::sea_of_nodes::nodes::node::Stop; 3 | use crate::sea_of_nodes::nodes::{Nodes, Op}; 4 | use crate::sea_of_nodes::parser::Parser; 5 | use crate::sea_of_nodes::types::Types; 6 | 7 | mod brainfuck; 8 | mod chapter01; 9 | mod chapter02; 10 | mod chapter03; 11 | mod chapter04; 12 | mod chapter05; 13 | mod chapter06; 14 | mod chapter07; 15 | mod chapter08; 16 | mod chapter09; 17 | mod chapter10; 18 | mod chapter11; 19 | mod chapter12; 20 | mod chapter13; 21 | mod chapter14; 22 | mod chapter15; 23 | mod chapter16; 24 | mod chapter17; 25 | pub mod evaluator; 26 | mod rust; 27 | mod scheduler; 28 | mod scheduler_test; 29 | mod type_test; 30 | 31 | fn test_error(source: &str, error: &str) { 32 | let arena = DroplessArena::new(); 33 | let types = Types::new(&arena); 34 | 35 | assert_eq!(Parser::new(source, &types).parse(), Err(error.to_string()),); 36 | } 37 | 38 | fn test_error_iterate(source: &str, error: &str) { 39 | let arena = DroplessArena::new(); 40 | let types = Types::new(&arena); 41 | 42 | let mut parser = Parser::new(source, &types); 43 | match parser.parse() { 44 | Err(err) => assert_eq!(err, error), 45 | Ok(stop) => { 46 | parser.iterate(stop); 47 | assert_eq!(parser.type_check(stop), Err(error.to_string())); 48 | } 49 | } 50 | } 51 | 52 | impl<'t> Nodes<'t> { 53 | pub fn ret_ctrl(&self, stop: Stop) -> &Op<'t> { 54 | let ret = stop.unique_input(self).unwrap(); 55 | assert!(matches!(&self[ret], Op::Return)); 56 | &self[self.inputs[ret][0].unwrap()] 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/sea_of_nodes/tests/brainfuck.rs: -------------------------------------------------------------------------------- 1 | use crate::datastructures::arena::DroplessArena; 2 | use crate::sea_of_nodes::parser::Parser; 3 | use crate::sea_of_nodes::tests::evaluator::evaluate; 4 | use crate::sea_of_nodes::types::Types; 5 | 6 | #[test] 7 | fn brainfuck() { 8 | let program = b"++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++."; 9 | 10 | use std::fmt::Write; 11 | let mut encoded = String::new(); 12 | write!(&mut encoded, "u8[] !program = new u8[{}];", program.len()).unwrap(); 13 | for (i, b) in program.iter().enumerate() { 14 | write!(&mut encoded, "program[{i}] = {b};").unwrap(); 15 | } 16 | encoded.push_str( 17 | "\ 18 | var d = 0; 19 | u8[] !output = new u8[0]; 20 | u8[] !data = new u8[100]; 21 | 22 | for( int pc = 0; pc < program#; pc++ ) { 23 | var command = program[pc]; 24 | if (command == 62) { 25 | d++; 26 | } else if (command == 60) { 27 | d--; 28 | } else if (command == 43) { 29 | data[d]++; 30 | } else if (command == 45) { 31 | data[d]--; 32 | } else if (command == 46) { 33 | // Output a byte; increase the output array size 34 | var old = output; 35 | output = new u8[output# + 1]; 36 | for( var i = 0; i < old#; i++ ) 37 | output[i] = old[i]; 38 | output[old#] = data[d]; // Add the extra byte on the end 39 | } else if (command == 44) { 40 | data[d] = 42; 41 | } else if (command == 91) { 42 | if (data[d] == 0) { 43 | for( var d = 1; d > 0; ) { 44 | command = program[++pc]; 45 | if (command == 91) d++; 46 | if (command == 93) d--; 47 | } 48 | } 49 | } else if (command == 93) { 50 | if (data[d]) { 51 | for( var d = 1; d > 0; ) { 52 | command = program[--pc]; 53 | if (command == 93) d++; 54 | if (command == 91) d--; 55 | } 56 | } 57 | } 58 | } 59 | return output; 60 | ", 61 | ); 62 | 63 | let arena = DroplessArena::new(); 64 | let types = Types::new(&arena); 65 | let mut parser = Parser::new(&encoded, &types); 66 | let stop = parser.parse().unwrap(); 67 | parser.iterate(stop); 68 | parser.type_check(stop).unwrap(); 69 | 70 | assert_eq!( 71 | "Hello World!\n", 72 | evaluate(&parser.nodes, stop, Some(0), Some(10000)).to_string() 73 | ); 74 | } 75 | -------------------------------------------------------------------------------- /src/sea_of_nodes/tests/chapter01.rs: -------------------------------------------------------------------------------- 1 | use crate::datastructures::arena::DroplessArena; 2 | use crate::sea_of_nodes::nodes::{Op, StartOp}; 3 | use crate::sea_of_nodes::parser::Parser; 4 | use crate::sea_of_nodes::tests::test_error; 5 | use crate::sea_of_nodes::types::{Int, Type, Types}; 6 | 7 | #[test] 8 | fn test_simple_program() { 9 | let arena = DroplessArena::new(); 10 | let types = Types::new(&arena); 11 | let mut parser = Parser::new("return 1;", &types); 12 | let stop = parser.parse().unwrap(); 13 | 14 | assert!(matches!(&parser.nodes[stop], Op::Stop)); 15 | let ret = stop.unique_input(&mut parser.nodes).unwrap(); 16 | assert!(matches!(&parser.nodes[ret], Op::Return)); 17 | 18 | let ctrl = parser.nodes.inputs[ret][0].expect("has ctrl"); 19 | assert!(matches!(parser.nodes[ctrl], Op::CProj(_))); 20 | 21 | let expr = parser.nodes.inputs[ret][1].expect("has expr"); 22 | let Op::Constant(constant) = &parser.nodes[expr] else { 23 | unreachable!("expect constant"); 24 | }; 25 | 26 | assert_eq!( 27 | Some(parser.nodes.start.to_node()), 28 | parser.nodes.inputs[expr][0] 29 | ); 30 | assert_eq!(1, constant.unwrap_int()); 31 | } 32 | 33 | #[test] 34 | fn test_zero() { 35 | let arena = DroplessArena::new(); 36 | let types = Types::new(&arena); 37 | let mut parser = Parser::new("return 0;", &types); 38 | let _stop = parser.parse().unwrap(); 39 | 40 | assert!(matches!(&parser.nodes[parser.nodes.start], StartOp { .. })); 41 | for output in &parser.nodes.outputs[parser.nodes.start] { 42 | if let Op::Constant(c) = &parser.nodes[*output] { 43 | if let Type::Int(value) = c.data() { 44 | assert_eq!(Int { min: 0, max: 0 }, *value); 45 | } 46 | } 47 | } 48 | } 49 | 50 | #[test] 51 | fn test_bad_1() { 52 | test_error("ret", "Undefined name 'ret'"); 53 | } 54 | 55 | #[test] 56 | fn test_bad_2() { 57 | test_error( 58 | "return 0123;", 59 | "Syntax error: integer values cannot start with '0'", 60 | ); 61 | } 62 | 63 | #[test] 64 | fn test_not_bad_3() { 65 | let arena = DroplessArena::new(); 66 | let types = Types::new(&arena); 67 | let mut parser = Parser::new("return - -12;", &types); 68 | let stop = parser.parse().unwrap(); 69 | assert_eq!(parser.print(stop), "return 12;"); 70 | } 71 | 72 | #[test] 73 | fn test_bad_4() { 74 | test_error("return 100", "Syntax error, expected ;: "); 75 | } 76 | 77 | #[test] 78 | fn test_not_bad_5() { 79 | let arena = DroplessArena::new(); 80 | let types = Types::new(&arena); 81 | let mut parser = Parser::new("return -100;", &types); 82 | let stop = parser.parse().unwrap(); 83 | assert_eq!(parser.print(stop), "return -100;"); 84 | } 85 | 86 | #[test] 87 | fn test_bad_6() { 88 | test_error("return100", "Undefined name 'return100'"); 89 | } 90 | 91 | #[test] 92 | fn test_bad_7() { 93 | test_error("return 1;}", "Syntax error, unexpected }"); 94 | } 95 | -------------------------------------------------------------------------------- /src/sea_of_nodes/tests/chapter02.rs: -------------------------------------------------------------------------------- 1 | use crate::datastructures::arena::DroplessArena; 2 | use crate::sea_of_nodes::parser::Parser; 3 | use crate::sea_of_nodes::types::Types; 4 | 5 | #[test] 6 | fn test_parse_grammar() { 7 | let arena = DroplessArena::new(); 8 | let types = Types::new(&arena); 9 | let mut parser = Parser::new("return 1+2*3+-5;", &types); 10 | parser.nodes.disable_peephole = true; 11 | let stop = parser.parse().unwrap(); 12 | assert_eq!(parser.print(stop), "return ((1+(2*3))+(-5));"); 13 | } 14 | 15 | #[test] 16 | fn test_add_peephole() { 17 | let arena = DroplessArena::new(); 18 | let types = Types::new(&arena); 19 | let mut parser = Parser::new("return 1+2;", &types); 20 | let stop = parser.parse().unwrap(); 21 | assert_eq!(parser.print(stop), "return 3;"); 22 | } 23 | 24 | #[test] 25 | fn test_sub_peephole() { 26 | let arena = DroplessArena::new(); 27 | let types = Types::new(&arena); 28 | let mut parser = Parser::new("return 1-2;", &types); 29 | let stop = parser.parse().unwrap(); 30 | assert_eq!(parser.print(stop), "return -1;"); 31 | } 32 | 33 | #[test] 34 | fn test_mul_peephole() { 35 | let arena = DroplessArena::new(); 36 | let types = Types::new(&arena); 37 | let mut parser = Parser::new("return 2*3;", &types); 38 | let stop = parser.parse().unwrap(); 39 | assert_eq!(parser.print(stop), "return 6;"); 40 | } 41 | 42 | #[test] 43 | fn test_div_peephole() { 44 | let arena = DroplessArena::new(); 45 | let types = Types::new(&arena); 46 | let mut parser = Parser::new("return 6/3;", &types); 47 | let stop = parser.parse().unwrap(); 48 | assert_eq!(parser.print(stop), "return 2;"); 49 | } 50 | 51 | #[test] 52 | fn test_minus_peephole() { 53 | let arena = DroplessArena::new(); 54 | let types = Types::new(&arena); 55 | let mut parser = Parser::new("return 6/-3;", &types); 56 | let stop = parser.parse().unwrap(); 57 | assert_eq!(parser.print(stop), "return -2;"); 58 | } 59 | 60 | #[test] 61 | fn test_example() { 62 | let arena = DroplessArena::new(); 63 | let types = Types::new(&arena); 64 | let mut parser = Parser::new("return 1+2*3+-5;", &types); 65 | let stop = parser.parse().unwrap(); 66 | assert_eq!(parser.print(stop), "return 2;"); 67 | } 68 | -------------------------------------------------------------------------------- /src/sea_of_nodes/tests/chapter03.rs: -------------------------------------------------------------------------------- 1 | use crate::datastructures::arena::DroplessArena; 2 | use crate::sea_of_nodes::parser::Parser; 3 | use crate::sea_of_nodes::tests::test_error; 4 | use crate::sea_of_nodes::types::Types; 5 | 6 | #[test] 7 | fn test_var_decl() { 8 | let arena = DroplessArena::new(); 9 | let types = Types::new(&arena); 10 | let mut parser = Parser::new("int a=1; return a;", &types); 11 | let stop = parser.parse().unwrap(); 12 | assert_eq!(parser.print(stop), "return 1;"); 13 | } 14 | 15 | #[test] 16 | fn test_var_add() { 17 | let arena = DroplessArena::new(); 18 | let types = Types::new(&arena); 19 | let mut parser = Parser::new("int a=1; int b=2; return a+b;", &types); 20 | let stop = parser.parse().unwrap(); 21 | assert_eq!(parser.print(stop), "return 3;"); 22 | } 23 | 24 | #[test] 25 | fn test_var_scope() { 26 | let arena = DroplessArena::new(); 27 | let types = Types::new(&arena); 28 | let mut parser = Parser::new( 29 | "int a=1; int b=2; int c=0; { int b=3; c=a+b; } return c;", 30 | &types, 31 | ); 32 | let stop = parser.parse().unwrap(); 33 | assert_eq!(parser.print(stop), "return 4;"); 34 | } 35 | 36 | #[test] 37 | fn test_var_scope_no_peephole() { 38 | let arena = DroplessArena::new(); 39 | let types = Types::new(&arena); 40 | let mut parser = Parser::new( 41 | "int a=1; int b=2; int !c=0; { int b=3; c=a+b; } return c; ", 42 | &types, 43 | ); 44 | parser.nodes.disable_peephole = true; 45 | let stop = parser.parse().unwrap(); 46 | assert_eq!(parser.print(stop), "return (1+3);"); 47 | } 48 | 49 | #[test] 50 | fn test_var_dist() { 51 | let arena = DroplessArena::new(); 52 | let types = Types::new(&arena); 53 | let mut parser = Parser::new( 54 | "int x0=1; int y0=2; int x1=3; int y1=4; return (x0-x1)*(x0-x1) + (y0-y1)*(y0-y1); ", 55 | &types, 56 | ); 57 | let stop = parser.parse().unwrap(); 58 | assert_eq!(parser.print(stop), "return 8;"); 59 | } 60 | 61 | #[test] 62 | fn test_self_assign() { 63 | test_error("int a=a; return a;", "Undefined name 'a'"); 64 | } 65 | 66 | #[test] 67 | fn test_redeclare_var() { 68 | test_error( 69 | "int a=1; int b=2; int c=0; int b=3; c=a+b;", 70 | "Redefining name 'b'", 71 | ); 72 | } 73 | 74 | #[test] 75 | fn test_bad_1() { 76 | test_error( 77 | "int a=1; int b=2; int !c=0; { int b=3; c=a+b;", 78 | "Syntax error, expected }: ", 79 | ); 80 | } 81 | -------------------------------------------------------------------------------- /src/sea_of_nodes/tests/chapter04.rs: -------------------------------------------------------------------------------- 1 | use crate::datastructures::arena::DroplessArena; 2 | use crate::sea_of_nodes::nodes::Op; 3 | use crate::sea_of_nodes::parser::Parser; 4 | use crate::sea_of_nodes::tests::test_error; 5 | use crate::sea_of_nodes::types::Types; 6 | 7 | #[test] 8 | fn test_peephole() { 9 | let arena = DroplessArena::new(); 10 | let types = Types::new(&arena); 11 | let mut parser = Parser::new("return 1+arg+2; ", &types); 12 | let stop = parser.parse().unwrap(); 13 | assert_eq!(parser.print(stop), "return (arg+3);"); 14 | } 15 | 16 | #[test] 17 | fn test_peephole_2() { 18 | let arena = DroplessArena::new(); 19 | let types = Types::new(&arena); 20 | let mut parser = Parser::new("return (1+arg)+2;", &types); 21 | let stop = parser.parse().unwrap(); 22 | assert_eq!(parser.print(stop), "return (arg+3);"); 23 | } 24 | 25 | #[test] 26 | fn test_add_0() { 27 | let arena = DroplessArena::new(); 28 | let types = Types::new(&arena); 29 | let mut parser = Parser::new("return 0+arg;", &types); 30 | let stop = parser.parse().unwrap(); 31 | assert_eq!(parser.print(stop), "return arg;"); 32 | } 33 | 34 | #[test] 35 | fn test_add_add_mul() { 36 | let arena = DroplessArena::new(); 37 | let types = Types::new(&arena); 38 | let mut parser = Parser::new("return arg+0+arg;", &types); 39 | let stop = parser.parse().unwrap(); 40 | assert_eq!(parser.print(stop), "return (arg*2);"); 41 | } 42 | 43 | #[test] 44 | fn test_peephole_3() { 45 | let arena = DroplessArena::new(); 46 | let types = Types::new(&arena); 47 | let mut parser = Parser::new("return 1+arg+2+arg+3; ", &types); 48 | let stop = parser.parse().unwrap(); 49 | assert_eq!(parser.print(stop), "return ((arg*2)+6);"); 50 | } 51 | 52 | #[test] 53 | fn test_mul_1() { 54 | let arena = DroplessArena::new(); 55 | let types = Types::new(&arena); 56 | let mut parser = Parser::new("return 1*arg;", &types); 57 | let stop = parser.parse().unwrap(); 58 | assert_eq!(parser.print(stop), "return arg;"); 59 | } 60 | 61 | #[test] 62 | fn test_var_arg() { 63 | let arena = DroplessArena::new(); 64 | let types = Types::new(&arena); 65 | let mut parser = Parser::new("return arg; ", &types); 66 | let stop = parser.parse().unwrap(); 67 | 68 | assert!(matches!(&parser.nodes[stop], Op::Stop)); 69 | let ret = stop.unique_input(&mut parser.nodes).expect("has one ret"); 70 | assert!(matches!(&parser.nodes[ret], Op::Return)); 71 | 72 | assert!(matches!( 73 | parser.nodes[parser.nodes.inputs[ret][0].unwrap()], 74 | Op::CProj(_) 75 | )); 76 | assert!(matches!( 77 | parser.nodes[parser.nodes.inputs[ret][1].unwrap()], 78 | Op::Proj(_) 79 | )); 80 | } 81 | 82 | #[test] 83 | fn test_constant_arg() { 84 | let arena = DroplessArena::new(); 85 | let types = Types::new(&arena); 86 | let mut parser = Parser::new_with_arg("return arg; ", &types, types.get_int(2)); 87 | let stop = parser.parse().unwrap(); 88 | assert_eq!(parser.print(stop), "return 2;"); 89 | } 90 | 91 | #[test] 92 | fn test_comp_eq() { 93 | let arena = DroplessArena::new(); 94 | let types = Types::new(&arena); 95 | let mut parser = Parser::new("return 3==3; ", &types); 96 | let stop = parser.parse().unwrap(); 97 | assert_eq!(parser.print(stop), "return 1;"); 98 | } 99 | 100 | #[test] 101 | fn test_comp_eq_2() { 102 | let arena = DroplessArena::new(); 103 | let types = Types::new(&arena); 104 | let mut parser = Parser::new("return 3==4; ", &types); 105 | let stop = parser.parse().unwrap(); 106 | assert_eq!(parser.print(stop), "return 0;"); 107 | } 108 | 109 | #[test] 110 | fn test_comp_neq() { 111 | let arena = DroplessArena::new(); 112 | let types = Types::new(&arena); 113 | let mut parser = Parser::new("return 3!=3; ", &types); 114 | let stop = parser.parse().unwrap(); 115 | assert_eq!(parser.print(stop), "return 0;"); 116 | } 117 | 118 | #[test] 119 | fn test_comp_neq_2() { 120 | let arena = DroplessArena::new(); 121 | let types = Types::new(&arena); 122 | let mut parser = Parser::new("return 3!=4; ", &types); 123 | let stop = parser.parse().unwrap(); 124 | assert_eq!(parser.print(stop), "return 1;"); 125 | } 126 | 127 | #[test] 128 | fn test_bug_1() { 129 | let arena = DroplessArena::new(); 130 | let types = Types::new(&arena); 131 | let mut parser = Parser::new("int a=arg+1; int !b=a; b=1; return a+2; ", &types); 132 | let stop = parser.parse().unwrap(); 133 | assert_eq!(parser.print(stop), "return (arg+3);"); 134 | } 135 | 136 | #[test] 137 | fn test_bug_2() { 138 | let arena = DroplessArena::new(); 139 | let types = Types::new(&arena); 140 | let mut parser = Parser::new("int !a=arg+1; a=a; return a; ", &types); 141 | let stop = parser.parse().unwrap(); 142 | assert_eq!(parser.print(stop), "return (arg+1);"); 143 | } 144 | 145 | #[test] 146 | fn test_bug_3() { 147 | test_error("inta=1; return a;", "Undefined name 'inta'"); 148 | } 149 | 150 | #[test] 151 | fn test_bug_4() { 152 | let arena = DroplessArena::new(); 153 | let types = Types::new(&arena); 154 | let mut parser = Parser::new("return -arg;", &types); 155 | let stop = parser.parse().unwrap(); 156 | assert_eq!(parser.print(stop), "return (-arg);"); 157 | } 158 | 159 | #[test] 160 | fn test_bug_5() { 161 | let arena = DroplessArena::new(); 162 | let types = Types::new(&arena); 163 | let mut parser = Parser::new("return arg--2;", &types); 164 | let stop = parser.parse().unwrap(); 165 | assert_eq!(parser.print(stop), "return (arg--2);"); 166 | } 167 | -------------------------------------------------------------------------------- /src/sea_of_nodes/tests/chapter05.rs: -------------------------------------------------------------------------------- 1 | use crate::datastructures::arena::DroplessArena; 2 | use crate::sea_of_nodes::parser::Parser; 3 | use crate::sea_of_nodes::tests::test_error; 4 | use crate::sea_of_nodes::types::Types; 5 | 6 | #[test] 7 | fn test_if_stmt() { 8 | let arena = DroplessArena::new(); 9 | let types = Types::new(&arena); 10 | let mut parser = Parser::new( 11 | "\ 12 | int a = 1; 13 | if (arg == 1) 14 | a = arg+2; 15 | else { 16 | a = arg-3; 17 | } 18 | return a; 19 | ", 20 | &types, 21 | ); 22 | let stop = parser.parse().unwrap(); 23 | parser.iterate(stop); 24 | parser.type_check(stop).unwrap(); 25 | 26 | assert_eq!(parser.print(stop), "return Phi(Region,(arg+2),(arg-3));"); 27 | } 28 | 29 | #[test] 30 | fn test_test() { 31 | let arena = DroplessArena::new(); 32 | let types = Types::new(&arena); 33 | let mut parser = Parser::new_with_arg( 34 | "\ 35 | int c = 3; 36 | int b = 2; 37 | if (arg == 1) { 38 | b = 3; 39 | c = 4; 40 | } 41 | return c;", 42 | &types, 43 | types.int_bot, 44 | ); 45 | let stop = parser.parse().unwrap(); 46 | parser.iterate(stop); 47 | parser.type_check(stop).unwrap(); 48 | 49 | assert_eq!(parser.print(stop), "return Phi(Region,4,3);"); 50 | } 51 | 52 | #[test] 53 | fn test_return_2() { 54 | let arena = DroplessArena::new(); 55 | let types = Types::new(&arena); 56 | let mut parser = Parser::new_with_arg( 57 | "\ 58 | if( arg==1 ) 59 | return 3; 60 | else 61 | return 4; 62 | ", 63 | &types, 64 | types.int_bot, 65 | ); 66 | let stop = parser.parse().unwrap(); 67 | assert_eq!(parser.print(stop), "Stop[ return 3; return 4; ]"); 68 | } 69 | 70 | #[test] 71 | fn test_if_merge_b() { 72 | let arena = DroplessArena::new(); 73 | let types = Types::new(&arena); 74 | let mut parser = Parser::new( 75 | "\ 76 | int a=arg+1; 77 | int b=0; 78 | if( arg==1 ) 79 | b=a; 80 | else 81 | b=a+1; 82 | return a+b;", 83 | &types, 84 | ); 85 | let stop = parser.parse().unwrap(); 86 | parser.iterate(stop); 87 | parser.type_check(stop).unwrap(); 88 | 89 | assert_eq!(parser.print(stop), "return ((arg*2)+Phi(Region,2,3));"); 90 | } 91 | 92 | #[test] 93 | fn test_if_merge_2() { 94 | let arena = DroplessArena::new(); 95 | let types = Types::new(&arena); 96 | let mut parser = Parser::new( 97 | "\ 98 | int a=arg+1; 99 | int b=arg+2; 100 | if( arg==1 ) 101 | b=b+a; 102 | else 103 | a=b+1; 104 | return a+b;", 105 | &types, 106 | ); 107 | let stop = parser.parse().unwrap(); 108 | parser.iterate(stop); 109 | parser.type_check(stop).unwrap(); 110 | 111 | assert_eq!( 112 | parser.print(stop), 113 | "return ((Phi(Region,(arg*2),arg)+arg)+Phi(Region,4,5));" 114 | ); 115 | } 116 | 117 | #[test] 118 | fn test_merge_3() { 119 | let arena = DroplessArena::new(); 120 | let types = Types::new(&arena); 121 | let mut parser = Parser::new_with_arg( 122 | "\ 123 | int a=1; 124 | if( arg==1 ) 125 | if( arg==2 ) 126 | a=2; 127 | else 128 | a=3; 129 | else if( arg==3 ) 130 | a=4; 131 | else 132 | a=5; 133 | return a; 134 | ", 135 | &types, 136 | types.int_bot, 137 | ); 138 | let stop = parser.parse().unwrap(); 139 | parser.iterate(stop); 140 | parser.type_check(stop).unwrap(); 141 | 142 | assert_eq!( 143 | parser.print(stop), 144 | "return Phi(Region,Phi(Region,2,3),Phi(Region,4,5));" 145 | ); 146 | } 147 | 148 | #[test] 149 | fn test_merge_4() { 150 | let arena = DroplessArena::new(); 151 | let types = Types::new(&arena); 152 | let mut parser = Parser::new_with_arg( 153 | "\ 154 | int a=0; 155 | int b=0; 156 | if( arg ) 157 | a=1; 158 | if( arg==0 ) 159 | b=2; 160 | return arg+a+b; 161 | ", 162 | &types, 163 | types.int_bot, 164 | ); 165 | let stop = parser.parse().unwrap(); 166 | assert_eq!( 167 | parser.print(stop), 168 | "return ((arg+Phi(Region,1,0))+Phi(Region,2,0));" 169 | ); 170 | } 171 | 172 | #[test] 173 | fn test_merge_5() { 174 | let arena = DroplessArena::new(); 175 | let types = Types::new(&arena); 176 | let mut parser = Parser::new( 177 | "\ 178 | int a=arg==2; 179 | if( arg==1 ) 180 | { 181 | a=arg==3; 182 | } 183 | return a;", 184 | &types, 185 | ); 186 | let stop = parser.parse().unwrap(); 187 | parser.iterate(stop); 188 | parser.type_check(stop).unwrap(); 189 | 190 | assert_eq!(parser.print(stop), "return (arg==Phi(Region,3,2));"); 191 | } 192 | 193 | #[test] 194 | fn test_true() { 195 | let arena = DroplessArena::new(); 196 | let types = Types::new(&arena); 197 | let mut parser = Parser::new("return true;", &types); 198 | let stop = parser.parse().unwrap(); 199 | assert_eq!(parser.print(stop), "return 1;"); 200 | } 201 | 202 | #[test] 203 | fn test_half_def() { 204 | test_error( 205 | "if( arg==1 ) int b=2; return b;", 206 | "Cannot define a new name on one arm of an if", 207 | ); 208 | } 209 | 210 | #[test] 211 | fn test_half_def_2() { 212 | test_error( 213 | "if( arg==1 ) { int b=2; } else { int b=3; } return b;", 214 | "Undefined name 'b'", 215 | ); 216 | } 217 | 218 | #[test] 219 | fn test_regress_1() { 220 | test_error( 221 | "if(arg==2) int a=1; else int b=2; return a;", 222 | "Cannot define a new name on one arm of an if", 223 | ); 224 | } 225 | 226 | #[test] 227 | fn test_bad_num() { 228 | test_error( 229 | "return 1-;", 230 | "Syntax error, expected an identifier or expression: ;", 231 | ); 232 | } 233 | 234 | #[test] 235 | fn test_keyword_1() { 236 | test_error( 237 | "int true=0; return true;", 238 | "Expected an identifier, found 'true'", 239 | ); 240 | } 241 | 242 | #[test] 243 | fn test_keyword_2() { 244 | test_error( 245 | "int else=arg; if(else) else=2; else else=1; return else;", 246 | "Expected an identifier, found 'else'", 247 | ); 248 | } 249 | 250 | #[test] 251 | fn test_keyword_3() { 252 | test_error( 253 | "int a=1; ififif(arg)inta=2;return a;", 254 | "Undefined name 'ififif'", 255 | ); 256 | } 257 | -------------------------------------------------------------------------------- /src/sea_of_nodes/tests/chapter06.rs: -------------------------------------------------------------------------------- 1 | use crate::datastructures::arena::DroplessArena; 2 | use crate::sea_of_nodes::nodes::Op; 3 | use crate::sea_of_nodes::parser::Parser; 4 | use crate::sea_of_nodes::types::Types; 5 | 6 | #[test] 7 | fn test_peephole_return() { 8 | let arena = DroplessArena::new(); 9 | let types = Types::new(&arena); 10 | let mut parser = Parser::new( 11 | "\ 12 | if( true ) return 2; 13 | return 1; 14 | ", 15 | &types, 16 | ); 17 | let stop = parser.parse().unwrap(); 18 | parser.iterate(stop); 19 | parser.type_check(stop).unwrap(); 20 | 21 | assert_eq!(parser.print(stop), "return 2;"); 22 | assert!(matches!(parser.nodes.ret_ctrl(stop), Op::CProj(_))); 23 | } 24 | 25 | #[test] 26 | fn test_peephole_rotate() { 27 | let arena = DroplessArena::new(); 28 | let types = Types::new(&arena); 29 | let mut parser = Parser::new( 30 | "\ 31 | int a = 1; 32 | if (arg) 33 | a = 2; 34 | return (arg < a) < 3; // Because (arg < a) is a bool/uint1/[0-1], its always less than 3 35 | ", 36 | &types, 37 | ); 38 | let stop = parser.parse().unwrap(); 39 | parser.iterate(stop); 40 | parser.type_check(stop).unwrap(); 41 | 42 | assert_eq!(parser.print(stop), "return 1;"); 43 | } 44 | 45 | #[test] 46 | fn test_peephole_cfg() { 47 | let arena = DroplessArena::new(); 48 | let types = Types::new(&arena); 49 | let mut parser = Parser::new( 50 | "\ 51 | int a=1; 52 | if( true ) 53 | a=2; 54 | else 55 | a=3; 56 | return a; 57 | ", 58 | &types, 59 | ); 60 | let stop = parser.parse().unwrap(); 61 | parser.iterate(stop); 62 | parser.type_check(stop).unwrap(); 63 | 64 | assert_eq!(parser.print(stop), "return 2;"); 65 | assert!(matches!(parser.nodes.ret_ctrl(stop), Op::CProj(_))); 66 | } 67 | 68 | #[test] 69 | fn test_if_if() { 70 | let arena = DroplessArena::new(); 71 | let types = Types::new(&arena); 72 | let mut parser = Parser::new_with_arg( 73 | "\ 74 | int a=1; 75 | if( arg!=1 ) 76 | a=2; 77 | else 78 | a=3; 79 | int b=4; 80 | if( a==2 ) 81 | b=42; 82 | else 83 | b=5; 84 | return b;", 85 | &types, 86 | types.int_bot, 87 | ); 88 | let stop = parser.parse().unwrap(); 89 | parser.iterate(stop); 90 | parser.type_check(stop).unwrap(); 91 | 92 | assert_eq!(parser.print(stop), "return Phi(Region,42,5);"); 93 | } 94 | 95 | #[test] 96 | fn test_if_arg_if() { 97 | let arena = DroplessArena::new(); 98 | let types = Types::new(&arena); 99 | let mut parser = Parser::new_with_arg( 100 | "\ 101 | int a=1; 102 | if( 1==1 ) 103 | a=2; 104 | else 105 | a=3; 106 | int b=4; 107 | if( arg==2 ) 108 | b=a; 109 | else 110 | b=5; 111 | return b;", 112 | &types, 113 | types.int_bot, 114 | ); 115 | let stop = parser.parse().unwrap(); 116 | parser.iterate(stop); 117 | parser.type_check(stop).unwrap(); 118 | 119 | assert_eq!(parser.print(stop), "return Phi(Region,2,5);"); 120 | } 121 | 122 | #[test] 123 | fn test_merge_3_with_2() { 124 | let arena = DroplessArena::new(); 125 | let types = Types::new(&arena); 126 | let mut parser = Parser::new_with_arg( 127 | "\ 128 | int a=1; 129 | if( arg==1 ) 130 | if( arg==2 ) 131 | a=2; 132 | else 133 | a=3; 134 | else if( arg==3 ) 135 | a=4; 136 | else 137 | a=5; 138 | return a; 139 | ", 140 | &types, 141 | types.get_int(2), 142 | ); 143 | let stop = parser.parse().unwrap(); 144 | assert_eq!(parser.print(stop), "return 5;"); 145 | } 146 | 147 | #[test] 148 | fn test_merge_3_with_1() { 149 | let arena = DroplessArena::new(); 150 | let types = Types::new(&arena); 151 | let mut parser = Parser::new_with_arg( 152 | "\ 153 | int a=1; 154 | if( arg==1 ) 155 | if( arg==2 ) 156 | a=2; 157 | else 158 | a=3; 159 | else if( arg==3 ) 160 | a=4; 161 | else 162 | a=5; 163 | return a; 164 | ", 165 | &types, 166 | types.get_int(1), 167 | ); 168 | let stop = parser.parse().unwrap(); 169 | parser.iterate(stop); 170 | parser.type_check(stop).unwrap(); 171 | 172 | assert_eq!(parser.print(stop), "return 3;"); 173 | } 174 | 175 | #[test] 176 | fn test_merge_3_peephole() { 177 | let arena = DroplessArena::new(); 178 | let types = Types::new(&arena); 179 | let mut parser = Parser::new_with_arg( 180 | "\ 181 | int a=1; 182 | if( arg==1 ) 183 | if( 1==2 ) 184 | a=2; 185 | else 186 | a=3; 187 | else if( arg==3 ) 188 | a=4; 189 | else 190 | a=5; 191 | return a; 192 | ", 193 | &types, 194 | types.int_bot, 195 | ); 196 | let stop = parser.parse().unwrap(); 197 | parser.iterate(stop); 198 | parser.type_check(stop).unwrap(); 199 | 200 | assert_eq!(parser.print(stop), "return Phi(Region,3,Phi(Region,4,5));"); 201 | } 202 | 203 | #[test] 204 | fn test_merge_3_peephole_1() { 205 | let arena = DroplessArena::new(); 206 | let types = Types::new(&arena); 207 | let mut parser = Parser::new_with_arg( 208 | "\ 209 | int a=1; 210 | if( arg==1 ) 211 | if( 1==2 ) 212 | a=2; 213 | else 214 | a=3; 215 | else if( arg==3 ) 216 | a=4; 217 | else 218 | a=5; 219 | return a; 220 | ", 221 | &types, 222 | types.get_int(1), 223 | ); 224 | let stop = parser.parse().unwrap(); 225 | parser.iterate(stop); 226 | parser.type_check(stop).unwrap(); 227 | 228 | assert_eq!(parser.print(stop), "return 3;"); 229 | } 230 | 231 | #[test] 232 | fn test_merge_3_peephole_3() { 233 | let arena = DroplessArena::new(); 234 | let types = Types::new(&arena); 235 | let mut parser = Parser::new_with_arg( 236 | "\ 237 | int a=1; 238 | if( arg==1 ) 239 | if( 1==2 ) 240 | a=2; 241 | else 242 | a=3; 243 | else if( arg==3 ) 244 | a=4; 245 | else 246 | a=5; 247 | return a; 248 | ", 249 | &types, 250 | types.get_int(3), 251 | ); 252 | let stop = parser.parse().unwrap(); 253 | parser.iterate(stop); 254 | parser.type_check(stop).unwrap(); 255 | 256 | assert_eq!(parser.print(stop), "return 4;"); 257 | } 258 | 259 | #[test] 260 | fn test_demo_1_non_const() { 261 | let arena = DroplessArena::new(); 262 | let types = Types::new(&arena); 263 | let mut parser = Parser::new( 264 | "\ 265 | int a = 0; 266 | int b = 1; 267 | if( arg ) { 268 | a = 2; 269 | if( arg ) { b = 2; } 270 | else b = 3; 271 | } 272 | return a+b; 273 | ", 274 | &types, 275 | ); 276 | let stop = parser.parse().unwrap(); 277 | parser.iterate(stop); 278 | parser.type_check(stop).unwrap(); 279 | 280 | assert_eq!(parser.print(stop), "return Phi(Region,4,1);"); 281 | } 282 | 283 | #[test] 284 | fn test_demo_1_true() { 285 | let arena = DroplessArena::new(); 286 | let types = Types::new(&arena); 287 | let mut parser = Parser::new_with_arg( 288 | "\ 289 | int a = 0; 290 | int b = 1; 291 | if( arg ) { 292 | a = 2; 293 | if( arg ) { b = 2; } 294 | else b = 3; 295 | } 296 | return a+b; 297 | ", 298 | &types, 299 | types.get_int(1), 300 | ); 301 | let stop = parser.parse().unwrap(); 302 | parser.iterate(stop); 303 | parser.type_check(stop).unwrap(); 304 | 305 | assert_eq!(parser.print(stop), "return 4;"); 306 | } 307 | 308 | #[test] 309 | fn test_demo_1_false() { 310 | let arena = DroplessArena::new(); 311 | let types = Types::new(&arena); 312 | let mut parser = Parser::new_with_arg( 313 | "\ 314 | int a = 0; 315 | int b = 1; 316 | if( arg ) { 317 | a = 2; 318 | if( arg ) { b = 2; } 319 | else b = 3; 320 | } 321 | return a+b; 322 | ", 323 | &types, 324 | types.get_int(0), 325 | ); 326 | let stop = parser.parse().unwrap(); 327 | parser.iterate(stop); 328 | parser.type_check(stop).unwrap(); 329 | 330 | assert_eq!(parser.print(stop), "return 1;"); 331 | } 332 | 333 | #[test] 334 | fn test_demo_2_non_const() { 335 | let arena = DroplessArena::new(); 336 | let types = Types::new(&arena); 337 | let mut parser = Parser::new( 338 | "\ 339 | int a = 0; 340 | int b = 1; 341 | int c = 0; 342 | if( arg ) { 343 | a = 1; 344 | if( arg==2 ) { c=2; } else { c=3; } 345 | if( arg ) { b = 2; } 346 | else b = 3; 347 | } 348 | return a+b+c; 349 | ", 350 | &types, 351 | ); 352 | let stop = parser.parse().unwrap(); 353 | parser.iterate(stop); 354 | parser.type_check(stop).unwrap(); 355 | 356 | assert_eq!( 357 | parser.print(stop), 358 | "return (Phi(Region,Phi(Region,2,3),0)+Phi(Region,3,1));" 359 | ); 360 | } 361 | 362 | #[test] 363 | fn test_demo_2_true() { 364 | let arena = DroplessArena::new(); 365 | let types = Types::new(&arena); 366 | let mut parser = Parser::new_with_arg( 367 | "\ 368 | int a = 0; 369 | int b = 1; 370 | int c = 0; 371 | if( arg ) { 372 | a = 1; 373 | if( arg==2 ) { c=2; } else { c=3; } 374 | if( arg ) { b = 2; } 375 | else b = 3; 376 | } 377 | return a+b+c; 378 | ", 379 | &types, 380 | types.get_int(1), 381 | ); 382 | let stop = parser.parse().unwrap(); 383 | parser.iterate(stop); 384 | parser.type_check(stop).unwrap(); 385 | 386 | assert_eq!(parser.print(stop), "return 6;"); 387 | } 388 | 389 | #[test] 390 | fn test_demo_2arg_2() { 391 | let arena = DroplessArena::new(); 392 | let types = Types::new(&arena); 393 | let mut parser = Parser::new_with_arg( 394 | "\ 395 | int a = 0; 396 | int b = 1; 397 | int c = 0; 398 | if( arg ) { 399 | a = 1; 400 | if( arg==2 ) { c=2; } else { c=3; } 401 | if( arg ) { b = 2; } 402 | else b = 3; 403 | } 404 | return a+b+c; 405 | ", 406 | &types, 407 | types.get_int(2), 408 | ); 409 | let stop = parser.parse().unwrap(); 410 | parser.iterate(stop); 411 | parser.type_check(stop).unwrap(); 412 | 413 | assert_eq!(parser.print(stop), "return 5;"); 414 | } 415 | -------------------------------------------------------------------------------- /src/sea_of_nodes/tests/chapter07.rs: -------------------------------------------------------------------------------- 1 | use crate::datastructures::arena::DroplessArena; 2 | use crate::sea_of_nodes::nodes::Op; 3 | use crate::sea_of_nodes::parser::Parser; 4 | use crate::sea_of_nodes::types::Types; 5 | 6 | #[test] 7 | fn test_example() { 8 | let arena = DroplessArena::new(); 9 | let types = Types::new(&arena); 10 | let mut parser = Parser::new( 11 | "\ 12 | while(arg < 10) { 13 | arg = arg + 1; 14 | } 15 | return arg; 16 | ", 17 | &types, 18 | ); 19 | parser.nodes.disable_peephole = true; 20 | let stop = parser.parse().unwrap(); 21 | assert_eq!(parser.print(stop), "return Phi(Loop,arg,(Phi_arg+1));"); 22 | assert!(matches!(parser.nodes.ret_ctrl(stop), Op::CProj(_))); 23 | } 24 | 25 | #[test] 26 | fn test_regression() { 27 | let arena = DroplessArena::new(); 28 | let types = Types::new(&arena); 29 | let mut parser = Parser::new( 30 | "\ 31 | int a = 1; 32 | if(arg){}else{ 33 | while(a < 10) { 34 | a = a + 1; 35 | } 36 | } 37 | return a; 38 | ", 39 | &types, 40 | ); 41 | let stop = parser.parse().unwrap(); 42 | parser.iterate(stop); 43 | parser.type_check(stop).unwrap(); 44 | 45 | assert_eq!( 46 | parser.print(stop), 47 | "return Phi(Region,1,Phi(Loop,1,(Phi_a+1)));" 48 | ); 49 | } 50 | 51 | #[test] 52 | fn test_while_nested() { 53 | let arena = DroplessArena::new(); 54 | let types = Types::new(&arena); 55 | let mut parser = Parser::new( 56 | "\ 57 | int sum = 0; 58 | int i = 0; 59 | while(i < arg) { 60 | i = i + 1; 61 | int j = 0; 62 | while( j < arg ) { 63 | sum = sum + j; 64 | j = j + 1; 65 | } 66 | } 67 | return sum; 68 | ", 69 | &types, 70 | ); 71 | let stop = parser.parse().unwrap(); 72 | parser.iterate(stop); 73 | parser.type_check(stop).unwrap(); 74 | 75 | assert_eq!( 76 | parser.print(stop), 77 | "return Phi(Loop,0,Phi(Loop,Phi_sum,(Phi_sum+Phi(Loop,0,(Phi_j+1)))));" 78 | ); 79 | } 80 | 81 | #[test] 82 | fn test_while_scope() { 83 | let arena = DroplessArena::new(); 84 | let types = Types::new(&arena); 85 | let mut parser = Parser::new( 86 | "\ 87 | int a = 1; 88 | int b = 2; 89 | while(a < 10) { 90 | if (a == 2) a = 3; 91 | else b = 4; 92 | } 93 | return b; 94 | ", 95 | &types, 96 | ); 97 | parser.nodes.disable_peephole = true; 98 | let stop = parser.parse().unwrap(); 99 | assert_eq!( 100 | parser.print(stop), 101 | "return Phi(Loop,2,Phi(Region,Phi_b,4));" 102 | ); 103 | assert!(matches!(parser.nodes.ret_ctrl(stop), Op::CProj(_))); 104 | } 105 | 106 | #[test] 107 | fn test_while_nested_if_and_inc() { 108 | let arena = DroplessArena::new(); 109 | let types = Types::new(&arena); 110 | let mut parser = Parser::new( 111 | "\ 112 | int a = 1; 113 | int b = 2; 114 | while(a < 10) { 115 | if (a == 2) a = 3; 116 | else b = 4; 117 | b = b + 1; 118 | a = a + 1; 119 | } 120 | return b; 121 | ", 122 | &types, 123 | ); 124 | let stop = parser.parse().unwrap(); 125 | parser.iterate(stop); 126 | parser.type_check(stop).unwrap(); 127 | 128 | assert_eq!( 129 | parser.print(stop), 130 | "return Phi(Loop,2,(Phi(Region,Phi_b,4)+1));" 131 | ); 132 | assert!(matches!(parser.nodes.ret_ctrl(stop), Op::CProj(_))); 133 | } 134 | 135 | #[test] 136 | fn test_while() { 137 | let arena = DroplessArena::new(); 138 | let types = Types::new(&arena); 139 | let mut parser = Parser::new( 140 | "\ 141 | int a = 1; 142 | while(a < 10) { 143 | a = a + 1; 144 | a = a + 2; 145 | } 146 | return a; 147 | ", 148 | &types, 149 | ); 150 | parser.nodes.disable_peephole = true; 151 | let stop = parser.parse().unwrap(); 152 | assert_eq!(parser.print(stop), "return Phi(Loop,1,((Phi_a+1)+2));"); 153 | assert!(matches!(parser.nodes.ret_ctrl(stop), Op::CProj(_))); 154 | } 155 | 156 | #[test] 157 | fn test_while_peep() { 158 | let arena = DroplessArena::new(); 159 | let types = Types::new(&arena); 160 | let mut parser = Parser::new( 161 | "\ 162 | int a = 1; 163 | while(a < 10) { 164 | a = a + 1; 165 | a = a + 2; 166 | } 167 | return a; 168 | ", 169 | &types, 170 | ); 171 | let stop = parser.parse().unwrap(); 172 | parser.iterate(stop); 173 | parser.type_check(stop).unwrap(); 174 | 175 | assert_eq!(parser.print(stop), "return Phi(Loop,1,(Phi_a+3));"); 176 | assert!(matches!(parser.nodes.ret_ctrl(stop), Op::CProj(_))); 177 | } 178 | 179 | #[test] 180 | fn test_while_2() { 181 | let arena = DroplessArena::new(); 182 | let types = Types::new(&arena); 183 | let mut parser = Parser::new( 184 | "\ 185 | int a = 1; 186 | while(arg) a = 2; 187 | return a; 188 | ", 189 | &types, 190 | ); 191 | parser.nodes.disable_peephole = true; 192 | let stop = parser.parse().unwrap(); 193 | assert_eq!(parser.print(stop), "return Phi(Loop,1,2);"); 194 | assert!(matches!(parser.nodes.ret_ctrl(stop), Op::CProj(_))); 195 | } 196 | 197 | #[test] 198 | fn test_while_2_peep() { 199 | let arena = DroplessArena::new(); 200 | let types = Types::new(&arena); 201 | let mut parser = Parser::new( 202 | "\ 203 | int a = 1; 204 | while(arg) a = 2; 205 | return a; 206 | ", 207 | &types, 208 | ); 209 | let stop = parser.parse().unwrap(); 210 | parser.iterate(stop); 211 | parser.type_check(stop).unwrap(); 212 | 213 | assert_eq!(parser.print(stop), "return Phi(Loop,1,2);"); 214 | assert!(matches!(parser.nodes.ret_ctrl(stop), Op::CProj(_))); 215 | } 216 | 217 | #[test] 218 | fn test_while_3() { 219 | let arena = DroplessArena::new(); 220 | let types = Types::new(&arena); 221 | let mut parser = Parser::new( 222 | "\ 223 | int a = 1; 224 | while(a < 10) { 225 | int b = a + 1; 226 | a = b + 2; 227 | } 228 | return a; 229 | ", 230 | &types, 231 | ); 232 | parser.nodes.disable_peephole = true; 233 | let stop = parser.parse().unwrap(); 234 | assert_eq!(parser.print(stop), "return Phi(Loop,1,((Phi_a+1)+2));"); 235 | assert!(matches!(parser.nodes.ret_ctrl(stop), Op::CProj(_))); 236 | } 237 | 238 | #[test] 239 | fn test_while_3_peep() { 240 | let arena = DroplessArena::new(); 241 | let types = Types::new(&arena); 242 | let mut parser = Parser::new( 243 | "\ 244 | int a = 1; 245 | while(a < 10) { 246 | int b = a + 1; 247 | a = b + 2; 248 | } 249 | return a; 250 | ", 251 | &types, 252 | ); 253 | let stop = parser.parse().unwrap(); 254 | parser.iterate(stop); 255 | parser.type_check(stop).unwrap(); 256 | 257 | assert_eq!(parser.print(stop), "return Phi(Loop,1,(Phi_a+3));"); 258 | assert!(matches!(parser.nodes.ret_ctrl(stop), Op::CProj(_))); 259 | } 260 | 261 | #[test] 262 | fn test_while_4() { 263 | let arena = DroplessArena::new(); 264 | let types = Types::new(&arena); 265 | let mut parser = Parser::new( 266 | "\ 267 | int a = 1; 268 | int b = 2; 269 | while(a < 10) { 270 | int b = a + 1; 271 | a = b + 2; 272 | } 273 | return a; 274 | ", 275 | &types, 276 | ); 277 | parser.nodes.disable_peephole = true; 278 | let stop = parser.parse().unwrap(); 279 | assert_eq!(parser.print(stop), "return Phi(Loop,1,((Phi_a+1)+2));"); 280 | assert!(matches!(parser.nodes.ret_ctrl(stop), Op::CProj(_))); 281 | } 282 | 283 | #[test] 284 | fn test_while_4_peep() { 285 | let arena = DroplessArena::new(); 286 | let types = Types::new(&arena); 287 | let mut parser = Parser::new( 288 | "\ 289 | int a = 1; 290 | int b = 2; 291 | while(a < 10) { 292 | int b = a + 1; 293 | a = b + 2; 294 | } 295 | return a; 296 | ", 297 | &types, 298 | ); 299 | let stop = parser.parse().unwrap(); 300 | parser.iterate(stop); 301 | parser.type_check(stop).unwrap(); 302 | 303 | assert_eq!(parser.print(stop), "return Phi(Loop,1,(Phi_a+3));"); 304 | assert!(matches!(parser.nodes.ret_ctrl(stop), Op::CProj(_))); 305 | } 306 | -------------------------------------------------------------------------------- /src/sea_of_nodes/tests/chapter08.rs: -------------------------------------------------------------------------------- 1 | use crate::datastructures::arena::DroplessArena; 2 | use crate::sea_of_nodes::nodes::{Op, ProjOp}; 3 | use crate::sea_of_nodes::parser::Parser; 4 | use crate::sea_of_nodes::tests::evaluator::evaluate; 5 | use crate::sea_of_nodes::tests::evaluator::Object; 6 | use crate::sea_of_nodes::tests::test_error; 7 | use crate::sea_of_nodes::types::Types; 8 | 9 | #[test] 10 | fn test_ex_6() { 11 | let arena = DroplessArena::new(); 12 | let types = Types::new(&arena); 13 | let mut parser = Parser::new( 14 | "\ 15 | while(arg < 10) { 16 | arg = arg + 1; 17 | if (arg == 5) 18 | break; 19 | if (arg == 6) 20 | break; 21 | } 22 | return arg; 23 | ", 24 | &types, 25 | ); 26 | let stop = parser.parse().unwrap(); 27 | parser.iterate(stop); 28 | parser.type_check(stop).unwrap(); 29 | 30 | assert_eq!( 31 | parser.print(stop), 32 | "return Phi(Region,Phi(Region,Phi(Loop,arg,(Phi_arg+1)),Add),Add);" 33 | ); 34 | assert!(matches!(parser.nodes.ret_ctrl(stop), Op::Region { .. })); 35 | assert_eq!( 36 | Object::Long(5), 37 | evaluate(&parser.nodes, stop, Some(1), None).object 38 | ); 39 | assert_eq!( 40 | Object::Long(10), 41 | evaluate(&parser.nodes, stop, Some(6), None).object 42 | ); 43 | } 44 | 45 | #[test] 46 | fn test_ex_5() { 47 | let arena = DroplessArena::new(); 48 | let types = Types::new(&arena); 49 | let mut parser = Parser::new( 50 | "\ 51 | int a = 1; 52 | while(arg < 10) { 53 | arg = arg + 1; 54 | if (arg == 5) 55 | continue; 56 | if (arg == 7) 57 | continue; 58 | a = a + 1; 59 | } 60 | return a; 61 | ", 62 | &types, 63 | ); 64 | let stop = parser.parse().unwrap(); 65 | parser.iterate(stop); 66 | parser.type_check(stop).unwrap(); 67 | 68 | assert_eq!( 69 | parser.print(stop), 70 | "return Phi(Loop,1,Phi(Region,Phi_a,(Phi_a+1)));" 71 | ); 72 | assert!(matches!(parser.nodes.ret_ctrl(stop), Op::CProj(_))); 73 | } 74 | 75 | #[test] 76 | fn test_ex_4() { 77 | let arena = DroplessArena::new(); 78 | let types = Types::new(&arena); 79 | let mut parser = Parser::new( 80 | "\ 81 | while(arg < 10) { 82 | arg = arg + 1; 83 | if (arg == 5) 84 | continue; 85 | if (arg == 6) 86 | break; 87 | } 88 | return arg; 89 | ", 90 | &types, 91 | ); 92 | let stop = parser.parse().unwrap(); 93 | parser.iterate(stop); 94 | parser.type_check(stop).unwrap(); 95 | 96 | assert_eq!( 97 | parser.print(stop), 98 | "return Phi(Region,Phi(Loop,arg,(Phi_arg+1)),Add);" 99 | ); 100 | assert!(matches!(parser.nodes.ret_ctrl(stop), Op::Region { .. })); 101 | } 102 | 103 | #[test] 104 | fn test_ex_3() { 105 | let arena = DroplessArena::new(); 106 | let types = Types::new(&arena); 107 | let mut parser = Parser::new( 108 | "\ 109 | while(arg < 10) { 110 | arg = arg + 1; 111 | if (arg == 6) 112 | break; 113 | } 114 | return arg; 115 | ", 116 | &types, 117 | ); 118 | let stop = parser.parse().unwrap(); 119 | parser.iterate(stop); 120 | parser.type_check(stop).unwrap(); 121 | 122 | assert_eq!( 123 | parser.print(stop), 124 | "return Phi(Region,Phi(Loop,arg,(Phi_arg+1)),Add);" 125 | ); 126 | assert!(matches!(parser.nodes.ret_ctrl(stop), Op::Region { .. })); 127 | } 128 | 129 | #[test] 130 | fn test_ex_2() { 131 | let arena = DroplessArena::new(); 132 | let types = Types::new(&arena); 133 | let mut parser = Parser::new( 134 | "\ 135 | while(arg < 10) { 136 | arg = arg + 1; 137 | if (arg == 5) 138 | continue; 139 | if (arg == 6) 140 | continue; 141 | } 142 | return arg; 143 | ", 144 | &types, 145 | ); 146 | let stop = parser.parse().unwrap(); 147 | parser.iterate(stop); 148 | parser.type_check(stop).unwrap(); 149 | 150 | assert_eq!(parser.print(stop), "return Phi(Loop,arg,(Phi_arg+1));"); 151 | assert!(matches!(parser.nodes.ret_ctrl(stop), Op::CProj(_))); 152 | } 153 | 154 | #[test] 155 | fn test_ex_1() { 156 | let arena = DroplessArena::new(); 157 | let types = Types::new(&arena); 158 | let mut parser = Parser::new( 159 | "\ 160 | while(arg < 10) { 161 | arg = arg + 1; 162 | if (arg == 5) 163 | continue; 164 | } 165 | return arg; 166 | ", 167 | &types, 168 | ); 169 | let stop = parser.parse().unwrap(); 170 | parser.iterate(stop); 171 | parser.type_check(stop).unwrap(); 172 | 173 | assert_eq!(parser.print(stop), "return Phi(Loop,arg,(Phi_arg+1));"); 174 | assert!(matches!(parser.nodes.ret_ctrl(stop), Op::CProj(_))); 175 | } 176 | 177 | #[test] 178 | fn test_regress_1() { 179 | let arena = DroplessArena::new(); 180 | let types = Types::new(&arena); 181 | let mut parser = Parser::new( 182 | "\ 183 | while( arg < 10 ) { 184 | int a = arg+2; 185 | if( a > 4 ) 186 | break; 187 | } 188 | return arg; 189 | ", 190 | &types, 191 | ); 192 | let stop = parser.parse().unwrap(); 193 | parser.iterate(stop); 194 | parser.type_check(stop).unwrap(); 195 | 196 | assert_eq!(parser.print(stop), "return arg;"); 197 | } 198 | 199 | #[test] 200 | fn test_regress_2() { 201 | let arena = DroplessArena::new(); 202 | let types = Types::new(&arena); 203 | let mut parser = Parser::new( 204 | "if(1) return 0; else while(arg>- -arg) arg=arg+1; return 0;", 205 | &types, 206 | ); 207 | let stop = parser.parse().unwrap(); 208 | parser.iterate(stop); 209 | parser.type_check(stop).unwrap(); 210 | 211 | assert_eq!(parser.print(stop), "return 0;"); 212 | } 213 | 214 | #[test] 215 | fn test_break_outside_loop() { 216 | test_error( 217 | "\ 218 | if(arg <= 10) { 219 | break; 220 | arg = arg + 1; 221 | } 222 | return arg; 223 | ", 224 | "No active loop for a break or continue", 225 | ); 226 | } 227 | 228 | #[test] 229 | fn test_regress_3() { 230 | let arena = DroplessArena::new(); 231 | let types = Types::new(&arena); 232 | let mut parser = Parser::new( 233 | "\ 234 | while(arg < 10) { 235 | break; 236 | } 237 | return arg; 238 | ", 239 | &types, 240 | ); 241 | let stop = parser.parse().unwrap(); 242 | parser.iterate(stop); 243 | parser.type_check(stop).unwrap(); 244 | 245 | assert_eq!(parser.print(stop), "return arg;"); 246 | } 247 | 248 | #[test] 249 | fn test_regress_4() { 250 | let arena = DroplessArena::new(); 251 | let types = Types::new(&arena); 252 | let mut parser = Parser::new( 253 | "\ 254 | int a = 1; 255 | while(arg < 10) { 256 | a = a + 1; 257 | if (arg > 2) { 258 | int a = 17; 259 | break; 260 | } 261 | } 262 | return a; 263 | ", 264 | &types, 265 | ); 266 | let stop = parser.parse().unwrap(); 267 | parser.iterate(stop); 268 | parser.type_check(stop).unwrap(); 269 | 270 | assert_eq!( 271 | parser.print(stop), 272 | "return Phi(Region,Phi(Loop,1,(Phi_a+1)),Add);" 273 | ); 274 | assert!(matches!(parser.nodes.ret_ctrl(stop), Op::Region { .. })); 275 | } 276 | 277 | #[test] 278 | fn test_regress_5() { 279 | let arena = DroplessArena::new(); 280 | let types = Types::new(&arena); 281 | let mut parser = Parser::new( 282 | "\ 283 | int a = 1; 284 | while(1) { 285 | a = a + 1; 286 | if (a<10) continue; 287 | break; 288 | } 289 | return a; 290 | ", 291 | &types, 292 | ); 293 | let stop = parser.parse().unwrap(); 294 | parser.iterate(stop); 295 | parser.type_check(stop).unwrap(); 296 | 297 | assert_eq!(parser.print(stop), "return (Phi(Loop,1,Add)+1);"); 298 | assert!(matches!( 299 | parser.nodes.ret_ctrl(stop), 300 | Op::CProj(ProjOp { index: 1, .. }) 301 | )); 302 | } 303 | -------------------------------------------------------------------------------- /src/sea_of_nodes/tests/chapter09.rs: -------------------------------------------------------------------------------- 1 | use crate::datastructures::arena::DroplessArena; 2 | use crate::sea_of_nodes::parser::Parser; 3 | use crate::sea_of_nodes::tests::evaluator::evaluate; 4 | use crate::sea_of_nodes::tests::evaluator::Object; 5 | use crate::sea_of_nodes::types::Types; 6 | 7 | #[test] 8 | fn test_jig() { 9 | let arena = DroplessArena::new(); 10 | let types = Types::new(&arena); 11 | let mut parser = Parser::new( 12 | "\ 13 | int v0=0; 14 | arg=0; 15 | while(v0) { 16 | while(1) if(arg*arg*0==0) {} 17 | while(0) {} 18 | arg=1; 19 | } 20 | return 0; 21 | ", 22 | &types, 23 | ); 24 | let stop = parser.parse().unwrap(); 25 | parser.iterate(stop); 26 | parser.type_check(stop).unwrap(); 27 | } 28 | 29 | #[test] 30 | fn test_gvn_1() { 31 | let arena = DroplessArena::new(); 32 | let types = Types::new(&arena); 33 | let mut parser = Parser::new( 34 | "\ 35 | int x = arg + arg; 36 | if(arg < 10) { 37 | return arg + arg; 38 | } 39 | else { 40 | x = x + 1; 41 | } 42 | return x; 43 | ", 44 | &types, 45 | ); 46 | let stop = parser.parse().unwrap(); 47 | assert_eq!( 48 | parser.print(stop), 49 | "Stop[ return (arg*2); return (Mul+1); ]" 50 | ); 51 | assert_eq!( 52 | Object::Long(2), 53 | evaluate(&parser.nodes, stop, Some(1), None).object 54 | ); 55 | assert_eq!( 56 | Object::Long(23), 57 | evaluate(&parser.nodes, stop, Some(11), None).object 58 | ); 59 | } 60 | 61 | #[test] 62 | fn test_gvn_2() { 63 | let arena = DroplessArena::new(); 64 | let types = Types::new(&arena); 65 | let mut parser = Parser::new( 66 | "\ 67 | return arg*arg-arg*arg; 68 | ", 69 | &types, 70 | ); 71 | let stop = parser.parse().unwrap(); 72 | assert_eq!(parser.print(stop), "return 0;"); 73 | assert_eq!( 74 | Object::Long(0), 75 | evaluate(&parser.nodes, stop, Some(1), None).object 76 | ); 77 | } 78 | 79 | #[test] 80 | fn test_worklist_1() { 81 | let arena = DroplessArena::new(); 82 | let types = Types::new(&arena); 83 | let mut parser = Parser::new( 84 | "\ 85 | int step = 1; 86 | while (arg < 10) { 87 | arg = arg + step + 1; 88 | } 89 | return arg; 90 | ", 91 | &types, 92 | ); 93 | let stop = parser.parse().unwrap(); 94 | parser.iterate(stop); 95 | parser.type_check(stop).unwrap(); 96 | 97 | assert_eq!(parser.print(stop), "return Phi(Loop,arg,(Phi_arg+2));"); 98 | assert_eq!( 99 | Object::Long(11), 100 | evaluate(&parser.nodes, stop, Some(1), None).object 101 | ); 102 | } 103 | 104 | #[test] 105 | fn test_worklist_2() { 106 | let arena = DroplessArena::new(); 107 | let types = Types::new(&arena); 108 | let mut parser = Parser::new( 109 | "\ 110 | int cond = 0; 111 | int one = 1; 112 | while (arg < 10) { 113 | if (cond) one = 2; 114 | arg = arg + one*3 + 1; 115 | } 116 | return arg; 117 | ", 118 | &types, 119 | ); 120 | let stop = parser.parse().unwrap(); 121 | parser.iterate(stop); 122 | parser.type_check(stop).unwrap(); 123 | 124 | assert_eq!(parser.print(stop), "return Phi(Loop,arg,(Phi_arg+4));"); 125 | assert_eq!( 126 | Object::Long(13), 127 | evaluate(&parser.nodes, stop, Some(1), None).object 128 | ); 129 | } 130 | 131 | #[test] 132 | fn test_worklist_3() { 133 | let arena = DroplessArena::new(); 134 | let types = Types::new(&arena); 135 | let mut parser = Parser::new( 136 | "\ 137 | int v1 = 0; 138 | int v2 = 0; 139 | int v3 = 0; 140 | int v4 = 0; 141 | int v5 = 0; 142 | int v6 = 0; 143 | int v7 = 0; 144 | int v8 = 0; 145 | while (arg) { 146 | if (v1) v2 = 1; 147 | if (v2) v3 = 1; 148 | if (v3) v4 = 1; 149 | if (v4) v5 = 1; 150 | if (v5) v6 = 1; 151 | if (v6) v7 = 1; 152 | if (v7) v8 = 1; 153 | arg = arg + v8 + 1; 154 | } 155 | return arg; 156 | ", 157 | &types, 158 | ); 159 | let stop = parser.parse().unwrap(); 160 | parser.iterate(stop); 161 | parser.type_check(stop).unwrap(); 162 | 163 | assert_eq!(parser.print(stop), "return 0;"); 164 | } 165 | 166 | #[test] 167 | fn test_region_peep_bug() { 168 | let arena = DroplessArena::new(); 169 | let types = Types::new(&arena); 170 | let mut parser = Parser::new( 171 | "\ 172 | int v0=0; 173 | int v1=0; 174 | while(v1+arg) { 175 | arg=0; 176 | int v2=v0; 177 | while(arg+1) {} 178 | v0=1; 179 | v1=v2; 180 | } 181 | ", 182 | &types, 183 | ); 184 | let stop = parser.parse().unwrap(); 185 | parser.iterate(stop); 186 | parser.type_check(stop).unwrap(); 187 | 188 | assert_eq!(parser.print(stop), "Stop[ return 0; return 0; ]"); 189 | } 190 | 191 | #[test] 192 | fn test_while_0() { 193 | let arena = DroplessArena::new(); 194 | let types = Types::new(&arena); 195 | let mut parser = Parser::new("while(0) continue; if(0) arg=0;", &types); 196 | let stop = parser.parse().unwrap(); 197 | parser.iterate(stop); 198 | parser.type_check(stop).unwrap(); 199 | 200 | assert_eq!(parser.print(stop), "return 0;"); 201 | } 202 | 203 | #[test] 204 | fn test_while_1() { 205 | let arena = DroplessArena::new(); 206 | let types = Types::new(&arena); 207 | let mut parser = Parser::new( 208 | "\ 209 | if(0) while(0) { 210 | int arg=arg; 211 | while(0) {} 212 | } 213 | ", 214 | &types, 215 | ); 216 | let stop = parser.parse().unwrap(); 217 | parser.iterate(stop); 218 | parser.type_check(stop).unwrap(); 219 | 220 | assert_eq!(parser.print(stop), "return 0;"); 221 | } 222 | 223 | #[test] 224 | fn test_precedence() { 225 | let arena = DroplessArena::new(); 226 | let types = Types::new(&arena); 227 | let mut parser = Parser::new("return 3-1+2;", &types); 228 | let stop = parser.parse().unwrap(); 229 | parser.iterate(stop); 230 | parser.type_check(stop).unwrap(); 231 | 232 | assert_eq!(parser.print(stop), "return 4;"); 233 | } 234 | 235 | #[test] 236 | fn test_swap_2() { 237 | let arena = DroplessArena::new(); 238 | let types = Types::new(&arena); 239 | let mut parser = Parser::new("return 1+(1+1);", &types); 240 | let stop = parser.parse().unwrap(); 241 | parser.iterate(stop); 242 | parser.type_check(stop).unwrap(); 243 | 244 | assert_eq!(parser.print(stop), "return 3;"); 245 | } 246 | 247 | #[test] 248 | fn test_fuzz_0() { 249 | let arena = DroplessArena::new(); 250 | let types = Types::new(&arena); 251 | let mut parser = Parser::new( 252 | "\ 253 | int one = 1; 254 | int a = 0; 255 | int zero = 0; 256 | while(arg) { 257 | a = -(one + a + 2); 258 | arg = arg + 1; 259 | one = one + zero; 260 | } 261 | return a; 262 | ", 263 | &types, 264 | ); 265 | let stop = parser.parse().unwrap(); 266 | parser.iterate(stop); 267 | parser.type_check(stop).unwrap(); 268 | 269 | assert_eq!(parser.print(stop), "return Phi(Loop,0,(-(Phi_a+3)));"); 270 | } 271 | 272 | #[test] 273 | fn test_fuzz_1() { 274 | let arena = DroplessArena::new(); 275 | let types = Types::new(&arena); 276 | let mut parser = Parser::new( 277 | "\ 278 | while(1) {} 279 | while(arg) break; 280 | while(arg) arg=0; 281 | arg=0; 282 | int v0=0!=0<-0; 283 | return -0+0+0; 284 | ", 285 | &types, 286 | ); 287 | let stop = parser.parse().unwrap(); 288 | parser.iterate(stop); 289 | parser.type_check(stop).unwrap(); 290 | 291 | assert_eq!(parser.print(stop), "return 0;"); 292 | } 293 | 294 | #[test] 295 | fn test_fuzz_2() { 296 | let arena = DroplessArena::new(); 297 | let types = Types::new(&arena); 298 | let mut parser = Parser::new("return 0+-0;", &types); 299 | let stop = parser.parse().unwrap(); 300 | parser.iterate(stop); 301 | parser.type_check(stop).unwrap(); 302 | 303 | assert_eq!(parser.print(stop), "return 0;"); 304 | } 305 | 306 | #[test] 307 | fn test_fuzz_3() { 308 | let arena = DroplessArena::new(); 309 | let types = Types::new(&arena); 310 | let mut parser = Parser::new("int v0=0; while(0==69) while(v0) return 0;", &types); 311 | let stop = parser.parse().unwrap(); 312 | parser.iterate(stop); 313 | parser.type_check(stop).unwrap(); 314 | 315 | assert_eq!(parser.print(stop), "return 0;"); 316 | } 317 | 318 | #[test] 319 | fn test_fuzz_4() { 320 | let arena = DroplessArena::new(); 321 | let types = Types::new(&arena); 322 | let mut parser = Parser::new( 323 | "\ 324 | while(1) { 325 | arg=0<=0; 326 | if(1<0) while(arg==-0) arg=arg-arg; 327 | } 328 | ", 329 | &types, 330 | ); 331 | let stop = parser.parse().unwrap(); 332 | parser.iterate(stop); 333 | parser.type_check(stop).unwrap(); 334 | 335 | assert_eq!(parser.print(stop), "return 0;"); 336 | } 337 | 338 | #[test] 339 | fn test_fuzz_5() { 340 | let arena = DroplessArena::new(); 341 | let types = Types::new(&arena); 342 | let mut parser = Parser::new( 343 | "\ 344 | { 345 | int v0=0; 346 | while(1) 347 | int v1=0--0; 348 | while(v0) 349 | break; 350 | while(-v0) { 351 | while(0+0+v0) continue; 352 | break; 353 | } 354 | if(-0!=-0+0+v0) while(0+0+0+0) 355 | break; 356 | } 357 | return 0!=0; 358 | ", 359 | &types, 360 | ); 361 | let stop = parser.parse().unwrap(); 362 | parser.iterate(stop); 363 | parser.type_check(stop).unwrap(); 364 | 365 | assert_eq!(parser.print(stop), "return 0;"); 366 | } 367 | 368 | #[test] 369 | fn test_fuzz_6() { 370 | let arena = DroplessArena::new(); 371 | let types = Types::new(&arena); 372 | let mut parser = Parser::new( 373 | "\ 374 | int v0=0; 375 | while(0==1) while(v0) 376 | v0=1+v0; 377 | ", 378 | &types, 379 | ); 380 | let stop = parser.parse().unwrap(); 381 | parser.iterate(stop); 382 | parser.type_check(stop).unwrap(); 383 | 384 | assert_eq!(parser.print(stop), "return 0;"); 385 | } 386 | 387 | #[test] 388 | fn test_fuzz_7() { 389 | let arena = DroplessArena::new(); 390 | let types = Types::new(&arena); 391 | let mut parser = Parser::new( 392 | "\ 393 | while(1) {} 394 | int v0=0; 395 | while(v0) 396 | {} 397 | int v1=0; 398 | while(1) 399 | v1=1; 400 | return v1+v0; 401 | ", 402 | &types, 403 | ); 404 | let stop = parser.parse().unwrap(); 405 | parser.iterate(stop); 406 | parser.type_check(stop).unwrap(); 407 | 408 | assert_eq!(parser.print(stop), "return 0;"); 409 | } 410 | 411 | #[test] 412 | fn test_fuzz_8() { 413 | let arena = DroplessArena::new(); 414 | let types = Types::new(&arena); 415 | let mut parser = Parser::new("while(arg) arg = arg - 1; return arg;", &types); 416 | let stop = parser.parse().unwrap(); 417 | parser.iterate(stop); 418 | parser.type_check(stop).unwrap(); 419 | 420 | assert_eq!(parser.print(stop), "return 0;"); 421 | } 422 | 423 | #[test] 424 | fn test_meet() { 425 | let arena = DroplessArena::new(); 426 | let types = Types::new(&arena); 427 | let mut t1 = types.top; 428 | let mut t2 = *types.int_top; 429 | 430 | assert_eq!(*types.int_top.clone(), t1.meet(t2, &types)); 431 | assert_eq!(*types.int_top.clone(), t2.meet(t1, &types)); 432 | t1 = types.bot; 433 | t2 = *types.int_bot; 434 | assert_eq!(types.bot.clone(), t1.meet(t2, &types)); 435 | assert_eq!(types.bot.clone(), t2.meet(t1, &types)); 436 | } 437 | -------------------------------------------------------------------------------- /src/sea_of_nodes/tests/chapter10.rs: -------------------------------------------------------------------------------- 1 | use crate::datastructures::arena::DroplessArena; 2 | use crate::sea_of_nodes::parser::Parser; 3 | use crate::sea_of_nodes::tests::evaluator::evaluate; 4 | use crate::sea_of_nodes::tests::test_error; 5 | use crate::sea_of_nodes::tests::test_error_iterate; 6 | use crate::sea_of_nodes::types::Types; 7 | 8 | #[test] 9 | fn test_fuzzer() { 10 | let arena = DroplessArena::new(); 11 | let types = Types::new(&arena); 12 | let mut parser = Parser::new( 13 | "\ 14 | int a = arg/3; 15 | int b = arg*5; 16 | int x = arg*7; 17 | int y = arg/11; 18 | int p; int g; int h; 19 | if( (arg/13)==0 ) { 20 | p = x + y; 21 | g = x; 22 | h = y; 23 | } else { 24 | p = a + b; 25 | g = a; 26 | h = b; 27 | } 28 | int r = g+h; 29 | return p-r; 30 | ", 31 | &types, 32 | ); 33 | let stop = parser.parse().unwrap(); 34 | parser.iterate(stop); 35 | parser.type_check(stop).unwrap(); 36 | 37 | assert_eq!(parser.print(stop), "return 0;"); 38 | } 39 | 40 | #[test] 41 | fn test_struct() { 42 | let arena = DroplessArena::new(); 43 | let types = Types::new(&arena); 44 | let mut parser = Parser::new( 45 | "\ 46 | struct Bar { 47 | int a; 48 | int b; 49 | }; 50 | struct Foo { 51 | int x; 52 | }; 53 | Foo? foo = null; 54 | Bar !bar = new Bar; 55 | bar.a = 1; 56 | bar.a = 2; 57 | return bar.a; 58 | ", 59 | &types, 60 | ); 61 | let stop = parser.parse().unwrap(); 62 | parser.iterate(stop); 63 | parser.type_check(stop).unwrap(); 64 | 65 | assert_eq!(parser.print(stop), "return 2;"); 66 | } 67 | 68 | #[test] 69 | fn test_example() { 70 | let arena = DroplessArena::new(); 71 | let types = Types::new(&arena); 72 | let mut parser = Parser::new( 73 | "\ 74 | struct Vector2D { int x; int y; }; 75 | Vector2D !v = new Vector2D; 76 | v.x = 1; 77 | if (arg) 78 | v.y = 2; 79 | else 80 | v.y = 3; 81 | return v; 82 | ", 83 | &types, 84 | ); 85 | let stop = parser.parse().unwrap(); 86 | parser.iterate(stop); 87 | parser.type_check(stop).unwrap(); 88 | 89 | assert_eq!(parser.print(stop), "return Vector2D;"); 90 | } 91 | 92 | #[test] 93 | fn test_bug() { 94 | test_error( 95 | "\ 96 | struct s0 { 97 | int v0; 98 | }; 99 | s0? v1=null; 100 | int v3=v1.zAicm; 101 | ", 102 | "Accessing unknown field 'zAicm' from 'null'", 103 | ); 104 | } 105 | 106 | #[test] 107 | fn test_bug_2() { 108 | test_error( 109 | "\ 110 | struct s0 { int v0; }; 111 | arg=0+new s0.0; 112 | ", 113 | "Expected an identifier, found 'null'", 114 | ); 115 | } 116 | 117 | #[test] 118 | fn test_loop() { 119 | let arena = DroplessArena::new(); 120 | let types = Types::new(&arena); 121 | let mut parser = Parser::new( 122 | "\ 123 | struct Bar { int a; }; 124 | Bar !bar = new Bar; 125 | while (arg) { 126 | bar.a = bar.a + 2; 127 | arg = arg + 1; 128 | } 129 | return bar.a; 130 | ", 131 | &types, 132 | ); 133 | let stop = parser.parse().unwrap(); 134 | parser.iterate(stop); 135 | parser.type_check(stop).unwrap(); 136 | 137 | assert_eq!(parser.print(stop), "return Phi(Loop,0,(Phi_a+2));"); 138 | } 139 | 140 | #[test] 141 | fn test_if() { 142 | test_error_iterate( 143 | "\ 144 | struct Bar { int a; }; 145 | Bar !bar = new Bar; 146 | if (arg) bar = null; 147 | bar.a = 1; 148 | return bar.a; 149 | ", 150 | "Type null is not of declared type *Bar", 151 | ); 152 | } 153 | 154 | #[test] 155 | fn test_if_2() { 156 | test_error_iterate( 157 | "\ 158 | struct Bar { int a; }; 159 | Bar? !bar = null; 160 | if (arg) bar = new Bar; 161 | bar.a = 1; 162 | return bar.a; 163 | ", 164 | "Might be null accessing 'a'", 165 | ); 166 | } 167 | 168 | #[test] 169 | fn test_if_3() { 170 | test_error( 171 | "\ 172 | struct Bar { int a; }; 173 | Bar bar = null; 174 | if (arg) bar = null; 175 | bar.a = 1; 176 | return bar.a; 177 | ", 178 | "Type null is not of declared type *Bar", 179 | ); 180 | } 181 | 182 | #[test] 183 | fn test_if_or_null() { 184 | let arena = DroplessArena::new(); 185 | let types = Types::new(&arena); 186 | let mut parser = Parser::new( 187 | "\ 188 | struct Bar { int a; }; 189 | Bar? !bar = new Bar; 190 | if (arg) bar = null; 191 | if( bar ) bar.a = 1; 192 | return bar; 193 | ", 194 | &types, 195 | ); 196 | let stop = parser.parse().unwrap(); 197 | parser.iterate(stop); 198 | parser.type_check(stop).unwrap(); 199 | 200 | assert_eq!( 201 | parser.print(stop), 202 | "return Phi(Region,(*void)Phi(Region,null,Bar),null);" 203 | ); 204 | } 205 | 206 | #[test] 207 | fn test_if_or_null_2() { 208 | let arena = DroplessArena::new(); 209 | let types = Types::new(&arena); 210 | let mut parser = Parser::new( 211 | "\ 212 | struct Bar { int a; }; 213 | Bar? !bar = new Bar; 214 | if (arg) bar = null; 215 | int rez = 3; 216 | if( !bar ) rez=4; 217 | else bar.a = 1; 218 | return rez; 219 | ", 220 | &types, 221 | ); 222 | let stop = parser.parse().unwrap(); 223 | parser.iterate(stop); 224 | parser.type_check(stop).unwrap(); 225 | 226 | assert_eq!(parser.print(stop), "return Phi(Region,4,3);"); 227 | } 228 | 229 | #[test] 230 | fn test_while_with_null_inside() { 231 | test_error_iterate( 232 | "\ 233 | struct s0 {int v0;}; 234 | s0? !v0 = new s0; 235 | int ret = 0; 236 | while(arg) { 237 | ret = v0.v0; 238 | v0 = null; 239 | arg = arg - 1; 240 | } 241 | return ret; 242 | ", 243 | "Might be null accessing 'v0'", 244 | ); 245 | } 246 | 247 | #[test] 248 | fn test_redeclare_struct() { 249 | test_error( 250 | "\ 251 | struct s0 { 252 | int v0; 253 | }; 254 | s0? v1=new s0; 255 | s0? v1; 256 | v1=new s0; 257 | ", 258 | "Redefining name 'v1'", 259 | ); 260 | } 261 | 262 | #[test] 263 | fn test_iter() { 264 | let arena = DroplessArena::new(); 265 | let types = Types::new(&arena); 266 | let mut parser = Parser::new( 267 | "\ 268 | struct Iter { 269 | int x; 270 | int len; 271 | }; 272 | Iter !i = new Iter; 273 | i.len = arg; 274 | int sum=0; 275 | while( i.x < i.len ) { 276 | sum = sum + i.x; 277 | i.x = i.x + 1; 278 | } 279 | return sum; 280 | ", 281 | &types, 282 | ); 283 | let stop = parser.parse().unwrap(); 284 | parser.iterate(stop); 285 | parser.type_check(stop).unwrap(); 286 | 287 | assert_eq!( 288 | parser.print(stop), 289 | "return Phi(Loop,0,(Phi(Loop,0,(Phi_x+1))+Phi_sum));" 290 | ); 291 | } 292 | 293 | #[test] 294 | fn test_1() { 295 | let arena = DroplessArena::new(); 296 | let types = Types::new(&arena); 297 | let mut parser = Parser::new( 298 | "\ 299 | struct s0 {int v0;}; 300 | s0 !ret = new s0; 301 | while(arg) { 302 | s0 !v0 = new s0; 303 | v0.v0 = arg; 304 | arg = arg-1; 305 | if (arg==5) ret=v0; 306 | 307 | } 308 | return ret; 309 | ", 310 | &types, 311 | ); 312 | let stop = parser.parse().unwrap(); 313 | parser.iterate(stop); 314 | parser.type_check(stop).unwrap(); 315 | 316 | assert_eq!( 317 | parser.print(stop), 318 | "return Phi(Loop,s0,Phi(Region,s0,Phi_ret));" 319 | ); 320 | } 321 | 322 | #[test] 323 | fn test_2() { 324 | let arena = DroplessArena::new(); 325 | let types = Types::new(&arena); 326 | let mut parser = Parser::new( 327 | "\ 328 | struct s0 {int v0;}; 329 | s0 !ret = new s0; 330 | s0 !v0 = new s0; 331 | while(arg) { 332 | v0.v0 = arg; 333 | arg = arg-1; 334 | if (arg==5) ret=v0; 335 | 336 | } 337 | return ret; 338 | ", 339 | &types, 340 | ); 341 | let stop = parser.parse().unwrap(); 342 | parser.iterate(stop); 343 | parser.type_check(stop).unwrap(); 344 | 345 | assert_eq!( 346 | parser.print(stop), 347 | "return Phi(Loop,s0,Phi(Region,s0,Phi_ret));" 348 | ); 349 | } 350 | 351 | #[test] 352 | fn test_3() { 353 | let arena = DroplessArena::new(); 354 | let types = Types::new(&arena); 355 | let mut parser = Parser::new( 356 | "\ 357 | struct s0 {int v0;}; 358 | s0 !ret = new s0; 359 | while(arg < 10) { 360 | s0 !v0 = new s0; 361 | if (arg == 5) ret=v0; 362 | arg = arg + 1; 363 | } 364 | return ret; 365 | ", 366 | &types, 367 | ); 368 | let stop = parser.parse().unwrap(); 369 | parser.iterate(stop); 370 | parser.type_check(stop).unwrap(); 371 | 372 | assert_eq!( 373 | parser.print(stop), 374 | "return Phi(Loop,s0,Phi(Region,s0,Phi_ret));" 375 | ); 376 | } 377 | 378 | #[test] 379 | fn test_bug_3() { 380 | let arena = DroplessArena::new(); 381 | let types = Types::new(&arena); 382 | let mut parser = Parser::new( 383 | "\ 384 | struct s0 { int f0; }; 385 | return new s0; 386 | int v0=null.f0; 387 | ", 388 | &types, 389 | ); 390 | let stop = parser.parse().unwrap(); 391 | parser.iterate(stop); 392 | parser.type_check(stop).unwrap(); 393 | 394 | assert_eq!(parser.print(stop), "return s0;"); 395 | assert_eq!( 396 | "Obj{f0=0}", 397 | evaluate(&parser.nodes, stop, Some(0), None).to_string() 398 | ); 399 | } 400 | 401 | #[test] 402 | fn test_bug_4() { 403 | let arena = DroplessArena::new(); 404 | let types = Types::new(&arena); 405 | let mut parser = Parser::new( 406 | "\ 407 | if(0) { 408 | while(0) if(arg) continue; 409 | int v0=0; 410 | while(1) { 411 | int arg=-arg; 412 | v0=arg; 413 | } 414 | } 415 | ", 416 | &types, 417 | ); 418 | let stop = parser.parse().unwrap(); 419 | parser.iterate(stop); 420 | parser.type_check(stop).unwrap(); 421 | 422 | assert_eq!(parser.print(stop), "return 0;"); 423 | } 424 | 425 | #[test] 426 | fn test_bug_5() { 427 | let arena = DroplessArena::new(); 428 | let types = Types::new(&arena); 429 | let mut parser = Parser::new( 430 | "\ 431 | struct s0 { 432 | int f0; 433 | }; 434 | if(0) return 0; 435 | else return new s0; 436 | if(new s0.f0) return 0; 437 | ", 438 | &types, 439 | ); 440 | let stop = parser.parse().unwrap(); 441 | parser.iterate(stop); 442 | parser.type_check(stop).unwrap(); 443 | 444 | assert_eq!(parser.print(stop), "return s0;"); 445 | } 446 | 447 | #[test] 448 | fn test_bug_6_missed_worklist() { 449 | let arena = DroplessArena::new(); 450 | let types = Types::new(&arena); 451 | let mut parser = Parser::new( 452 | "\ 453 | while(0) {} 454 | int v4=0; 455 | while(0 2 ) c = 1; 40 | return c; 41 | ", 42 | &types, 43 | ); 44 | let stop = parser.parse().unwrap(); 45 | parser.iterate(stop); 46 | parser.type_check(stop).unwrap(); 47 | 48 | assert_eq!(parser.print(stop), "return 99;"); 49 | assert_eq!( 50 | Object::Long(99), 51 | evaluate(&parser.nodes, stop, Some(0), None).object 52 | ); 53 | } 54 | 55 | #[test] 56 | fn test_u_8() { 57 | let arena = DroplessArena::new(); 58 | let types = Types::new(&arena); 59 | let mut parser = Parser::new( 60 | "\ 61 | u8 b = 123; 62 | b = b + 456;// Truncate 63 | return b; 64 | ", 65 | &types, 66 | ); 67 | let stop = parser.parse().unwrap(); 68 | parser.iterate(stop); 69 | parser.type_check(stop).unwrap(); 70 | 71 | assert_eq!(parser.print(stop), "return 67;"); 72 | assert_eq!( 73 | Object::Long(67), 74 | evaluate(&parser.nodes, stop, Some(0), None).object 75 | ); 76 | } 77 | 78 | #[test] 79 | fn test_u_8_while() { 80 | let arena = DroplessArena::new(); 81 | let types = Types::new(&arena); 82 | let mut parser = Parser::new( 83 | "\ 84 | u8 b = 123; 85 | while( b ) b = b + 456;// Truncate 86 | return b; 87 | ", 88 | &types, 89 | ); 90 | let stop = parser.parse().unwrap(); 91 | parser.iterate(stop); 92 | parser.type_check(stop).unwrap(); 93 | 94 | assert_eq!(parser.print(stop), "return 0;"); 95 | } 96 | 97 | #[test] 98 | fn test_u_1() { 99 | let arena = DroplessArena::new(); 100 | let types = Types::new(&arena); 101 | let mut parser = Parser::new( 102 | "\ 103 | bool b = 123; 104 | b = b + 456;// Truncate 105 | u1 c = b; // No more truncate needed 106 | return c; 107 | ", 108 | &types, 109 | ); 110 | let stop = parser.parse().unwrap(); 111 | parser.iterate(stop); 112 | parser.type_check(stop).unwrap(); 113 | 114 | assert_eq!(parser.print(stop), "return 1;"); 115 | assert_eq!( 116 | Object::Long(1), 117 | evaluate(&parser.nodes, stop, Some(0), None).object 118 | ); 119 | } 120 | 121 | #[test] 122 | fn test_and() { 123 | let arena = DroplessArena::new(); 124 | let types = Types::new(&arena); 125 | let mut parser = Parser::new( 126 | "\ 127 | int b = 123; 128 | b = b+456 & 31; // Precedence 129 | return b; 130 | ", 131 | &types, 132 | ); 133 | let stop = parser.parse().unwrap(); 134 | parser.iterate(stop); 135 | parser.type_check(stop).unwrap(); 136 | 137 | assert_eq!(parser.print(stop), "return 3;"); 138 | assert_eq!( 139 | Object::Long(3), 140 | evaluate(&parser.nodes, stop, Some(0), None).object 141 | ); 142 | } 143 | 144 | #[test] 145 | fn test_ref_load() { 146 | let arena = DroplessArena::new(); 147 | let types = Types::new(&arena); 148 | let mut parser = Parser::new( 149 | "\ 150 | struct Foo { u1 b; }; 151 | Foo !f = new Foo; 152 | f.b = 123; 153 | return f.b; 154 | ", 155 | &types, 156 | ); 157 | let stop = parser.parse().unwrap(); 158 | parser.iterate(stop); 159 | parser.type_check(stop).unwrap(); 160 | 161 | assert_eq!(parser.print(stop), "return 1;"); 162 | assert_eq!( 163 | Object::Long(1), 164 | evaluate(&parser.nodes, stop, Some(0), None).object 165 | ); 166 | } 167 | 168 | #[test] 169 | fn test_signed() { 170 | let arena = DroplessArena::new(); 171 | let types = Types::new(&arena); 172 | let mut parser = Parser::new( 173 | "\ 174 | i8 b = 255; // Chopped 175 | return b; // Sign extend 176 | ", 177 | &types, 178 | ); 179 | let stop = parser.parse().unwrap(); 180 | parser.iterate(stop); 181 | parser.type_check(stop).unwrap(); 182 | 183 | assert_eq!(parser.print(stop), "return -1;"); 184 | assert_eq!( 185 | Object::Long(-1), 186 | evaluate(&parser.nodes, stop, Some(0), None).object 187 | ); 188 | } 189 | 190 | #[test] 191 | fn test_i_8() { 192 | let arena = DroplessArena::new(); 193 | let types = Types::new(&arena); 194 | let mut parser = Parser::new( 195 | "\ 196 | i8 b = arg; 197 | b = b + 1;// Truncate 198 | return b; 199 | ", 200 | &types, 201 | ); 202 | let stop = parser.parse().unwrap(); 203 | parser.iterate(stop); 204 | parser.type_check(stop).unwrap(); 205 | 206 | assert_eq!( 207 | parser.print(stop), 208 | "return (((((arg<<56)>>56)+1)<<56)>>56);" 209 | ); 210 | assert_eq!( 211 | Object::Long(1), 212 | evaluate(&parser.nodes, stop, Some(0), None).object 213 | ); 214 | assert_eq!( 215 | Object::Long(-128), 216 | evaluate(&parser.nodes, stop, Some(127), None).object 217 | ); 218 | } 219 | 220 | #[test] 221 | fn test_mask() { 222 | let arena = DroplessArena::new(); 223 | let types = Types::new(&arena); 224 | let mut parser = Parser::new( 225 | "\ 226 | u16 mask = (1<<16)-1; // AND mask 227 | int c = 123456789 & mask; 228 | return c; // 229 | ", 230 | &types, 231 | ); 232 | let stop = parser.parse().unwrap(); 233 | parser.iterate(stop); 234 | parser.type_check(stop).unwrap(); 235 | 236 | assert_eq!(parser.print(stop), "return 52501;"); 237 | assert_eq!( 238 | Object::Long(52501), 239 | evaluate(&parser.nodes, stop, Some(0), None).object 240 | ); 241 | } 242 | 243 | #[test] 244 | fn test_or() { 245 | let arena = DroplessArena::new(); 246 | let types = Types::new(&arena); 247 | let mut parser = Parser::new( 248 | "\ 249 | return (arg | 123 ^ 456) >>> 1; 250 | ", 251 | &types, 252 | ); 253 | let stop = parser.parse().unwrap(); 254 | parser.iterate(stop); 255 | parser.type_check(stop).unwrap(); 256 | 257 | assert_eq!(parser.print(stop), "return (((arg|123)^456)>>>1);"); 258 | assert_eq!( 259 | Object::Long(217), 260 | evaluate(&parser.nodes, stop, Some(0), None).object 261 | ); 262 | } 263 | 264 | #[test] 265 | fn test_mask_float() { 266 | test_error_iterate( 267 | "\ 268 | flt f = arg; 269 | arg = f & 0; 270 | return arg; 271 | ", 272 | "Cannot '&' FltBot", 273 | ); 274 | } 275 | 276 | #[test] 277 | fn test_clone_and() { 278 | let arena = DroplessArena::new(); 279 | let types = Types::new(&arena); 280 | let mut parser = Parser::new( 281 | "\ 282 | int v0=0; 283 | u32 v1 = 1&(1<>>0) { 312 | while(0) { 313 | u8 v1=0; 314 | v0=0>>>0; 315 | v1=arg; 316 | while(v1+0) {} 317 | } 318 | } 319 | return v0; 320 | ", 321 | &types, 322 | ); 323 | let stop = parser.parse().unwrap(); 324 | parser.iterate(stop); 325 | parser.type_check(stop).unwrap(); 326 | 327 | assert_eq!(parser.print(stop), "return 0;"); 328 | assert_eq!( 329 | Object::Long(0), 330 | evaluate(&parser.nodes, stop, Some(0), None).object 331 | ); 332 | } 333 | 334 | #[test] 335 | fn test_types() { 336 | let arena = DroplessArena::new(); 337 | let types = Types::new(&arena); 338 | let mut parser = Parser::new( 339 | "\ 340 | i8 xi8 = 123456789; if( xi8 != 21 ) return -8; 341 | i16 xi16 = 123456789; if( xi16 != -13035 ) return -16; 342 | i32 xi32 = 123456789; if( xi32 != 123456789 ) return -32; 343 | i64 xi64 = 123456789; if( xi64 != 123456789 ) return -64; 344 | int xint = 123456789; if( xint != 123456789 ) return -64; 345 | 346 | u1 ui1 = 123456789; if( ui1 != 1 ) return 1; 347 | u8 ui8 = 123456789; if( ui8 != 21 ) return 8; 348 | u16 ui16 = 123456789; if( ui16 != 52501 ) return 16; 349 | u32 ui32 = 123456789; if( ui32 != 123456789 ) return 32; 350 | 351 | flt fflt = 3.141592653589793; if( fflt != 3.141592653589793 ) return 3; 352 | f64 ff64 = 3.141592653589793; if( ff64 != 3.141592653589793 ) return 3; 353 | f32 ff32 = 3.141592653589793; if( ff32 != 3.1415927410125732) return 5; 354 | 355 | return 0; 356 | ", 357 | &types, 358 | ); 359 | let stop = parser.parse().unwrap(); 360 | parser.iterate(stop); 361 | parser.type_check(stop).unwrap(); 362 | 363 | assert_eq!(parser.print(stop), "return 0;"); 364 | assert_eq!( 365 | Object::Long(0), 366 | evaluate(&parser.nodes, stop, Some(0), None).object 367 | ); 368 | } 369 | -------------------------------------------------------------------------------- /src/sea_of_nodes/tests/chapter16.rs: -------------------------------------------------------------------------------- 1 | use crate::datastructures::arena::DroplessArena; 2 | use crate::sea_of_nodes::parser::Parser; 3 | use crate::sea_of_nodes::tests::evaluator::evaluate; 4 | use crate::sea_of_nodes::tests::evaluator::Object; 5 | use crate::sea_of_nodes::tests::test_error_iterate; 6 | use crate::sea_of_nodes::types::Types; 7 | 8 | #[test] 9 | fn test_jig() { 10 | let arena = DroplessArena::new(); 11 | let types = Types::new(&arena); 12 | let mut parser = Parser::new( 13 | "\ 14 | return 3.14; 15 | ", 16 | &types, 17 | ); 18 | let stop = parser.parse().unwrap(); 19 | parser.iterate(stop); 20 | parser.type_check(stop).unwrap(); 21 | 22 | assert_eq!(parser.print(stop), "return 3.14;"); 23 | assert_eq!( 24 | Object::Double(3.14), 25 | evaluate(&parser.nodes, stop, Some(0), None).object 26 | ); 27 | } 28 | 29 | #[test] 30 | fn test_multi_0() { 31 | let arena = DroplessArena::new(); 32 | let types = Types::new(&arena); 33 | let mut parser = Parser::new( 34 | "\ 35 | int x, y; 36 | return x+y; 37 | ", 38 | &types, 39 | ); 40 | let stop = parser.parse().unwrap(); 41 | parser.iterate(stop); 42 | parser.type_check(stop).unwrap(); 43 | 44 | assert_eq!(parser.print(stop), "return 0;"); 45 | assert_eq!( 46 | Object::Long(0), 47 | evaluate(&parser.nodes, stop, Some(0), None).object 48 | ); 49 | } 50 | 51 | #[test] 52 | fn test_multi_1() { 53 | let arena = DroplessArena::new(); 54 | let types = Types::new(&arena); 55 | let mut parser = Parser::new( 56 | "\ 57 | int x=2, y=x+1; 58 | return x+y; 59 | ", 60 | &types, 61 | ); 62 | let stop = parser.parse().unwrap(); 63 | parser.iterate(stop); 64 | parser.type_check(stop).unwrap(); 65 | 66 | assert_eq!(parser.print(stop), "return 5;"); 67 | assert_eq!( 68 | Object::Long(5), 69 | evaluate(&parser.nodes, stop, Some(0), None).object 70 | ); 71 | } 72 | 73 | #[test] 74 | fn test_final_1() { 75 | let arena = DroplessArena::new(); 76 | let types = Types::new(&arena); 77 | let mut parser = Parser::new( 78 | "\ 79 | int x=2, y=3; 80 | if( arg ) { int x = y; x = x*x; y=x; } // Shadow final x 81 | return y; 82 | ", 83 | &types, 84 | ); 85 | let stop = parser.parse().unwrap(); 86 | parser.iterate(stop); 87 | parser.type_check(stop).unwrap(); 88 | 89 | assert_eq!(parser.print(stop), "return Phi(Region,9,3);"); 90 | assert_eq!( 91 | Object::Long(3), 92 | evaluate(&parser.nodes, stop, Some(0), None).object 93 | ); 94 | assert_eq!( 95 | Object::Long(9), 96 | evaluate(&parser.nodes, stop, Some(1), None).object 97 | ); 98 | } 99 | 100 | #[test] 101 | fn test_construct_0() { 102 | let arena = DroplessArena::new(); 103 | let types = Types::new(&arena); 104 | let mut parser = Parser::new( 105 | "\ 106 | struct X { int x=3; }; 107 | X z = new X; 108 | return z.x; 109 | ", 110 | &types, 111 | ); 112 | let stop = parser.parse().unwrap(); 113 | parser.iterate(stop); 114 | parser.type_check(stop).unwrap(); 115 | 116 | assert_eq!(parser.print(stop), "return 3;"); 117 | assert_eq!( 118 | Object::Long(3), 119 | evaluate(&parser.nodes, stop, Some(0), None).object 120 | ); 121 | } 122 | 123 | #[test] 124 | fn test_construct_1() { 125 | let arena = DroplessArena::new(); 126 | let types = Types::new(&arena); 127 | let mut parser = Parser::new( 128 | "\ 129 | struct X { int !x; }; 130 | X z = new X { x=3; }; 131 | return z.x; 132 | ", 133 | &types, 134 | ); 135 | let stop = parser.parse().unwrap(); 136 | parser.iterate(stop); 137 | parser.type_check(stop).unwrap(); 138 | 139 | assert_eq!(parser.print(stop), "return 3;"); 140 | assert_eq!( 141 | Object::Long(3), 142 | evaluate(&parser.nodes, stop, Some(0), None).object 143 | ); 144 | } 145 | 146 | #[test] 147 | fn test_construct_2() { 148 | let arena = DroplessArena::new(); 149 | let types = Types::new(&arena); 150 | let mut parser = Parser::new( 151 | "\ 152 | struct X { int x=3; }; 153 | X z = new X { x = 4; }; 154 | return z.x; 155 | ", 156 | &types, 157 | ); 158 | let stop = parser.parse().unwrap(); 159 | parser.iterate(stop); 160 | parser.type_check(stop).unwrap(); 161 | 162 | assert_eq!(parser.print(stop), "return 4;"); 163 | assert_eq!( 164 | Object::Long(4), 165 | evaluate(&parser.nodes, stop, Some(0), None).object 166 | ); 167 | } 168 | 169 | #[test] 170 | fn test_struct_final_0() { 171 | let arena = DroplessArena::new(); 172 | let types = Types::new(&arena); 173 | let mut parser = Parser::new( 174 | "\ 175 | struct Point { int !x, !y; }; 176 | Point p = new Point { x=3; y=4; }; 177 | return p; 178 | ", 179 | &types, 180 | ); 181 | let stop = parser.parse().unwrap(); 182 | parser.iterate(stop); 183 | parser.type_check(stop).unwrap(); 184 | 185 | assert_eq!(parser.print(stop), "return (const)Point;"); 186 | assert_eq!( 187 | "Obj{x=3,y=4}", 188 | evaluate(&parser.nodes, stop, Some(0), None).to_string() 189 | ); 190 | } 191 | 192 | #[test] 193 | fn test_struct_final_1() { 194 | test_error_iterate( 195 | "\ 196 | struct Point { int x=3, y=4; }; 197 | Point p = new Point { x=5; y=6; }; 198 | p.x++; 199 | return p; 200 | ", 201 | "Cannot modify final field 'x'", 202 | ); 203 | } 204 | 205 | #[test] 206 | fn test_struct_final_2() { 207 | test_error_iterate( 208 | "\ 209 | struct Point { int x=3, y=4; }; 210 | Point p = new Point; 211 | p.x++; 212 | return p; 213 | ", 214 | "Cannot modify final field 'x'", 215 | ); 216 | } 217 | 218 | #[test] 219 | fn test_struct_final_3() { 220 | test_error_iterate( 221 | "\ 222 | struct Point { var x; var y; }; 223 | Point p = new Point; 224 | p.x++; 225 | return p; 226 | ", 227 | "'Point' is not fully initialized, field 'x' needs to be set in a constructor", 228 | ); 229 | } 230 | 231 | #[test] 232 | fn test_struct_final_4() { 233 | test_error_iterate( 234 | "\ 235 | struct Point { val x=3; val y=4; }; 236 | Point p = new Point; 237 | p.x++; 238 | return p; 239 | ", 240 | "Cannot reassign final 'x'", 241 | ); 242 | } 243 | 244 | #[test] 245 | fn test_struct_final_5() { 246 | let arena = DroplessArena::new(); 247 | let types = Types::new(&arena); 248 | let mut parser = Parser::new( 249 | "\ 250 | struct Point { var x=3; var y=4; }; 251 | Point !p = new Point; 252 | p.x++; 253 | return p; 254 | ", 255 | &types, 256 | ); 257 | let stop = parser.parse().unwrap(); 258 | parser.iterate(stop); 259 | parser.type_check(stop).unwrap(); 260 | 261 | assert_eq!(parser.print(stop), "return Point;"); 262 | assert_eq!( 263 | "Obj{x=4,y=4}", 264 | evaluate(&parser.nodes, stop, Some(0), None).to_string() 265 | ); 266 | } 267 | 268 | #[test] 269 | fn test_linked_list_1() { 270 | let arena = DroplessArena::new(); 271 | let types = Types::new(&arena); 272 | let mut parser = Parser::new( 273 | "\ 274 | struct LLI { LLI? next; int i; }; 275 | LLI? !head = null; 276 | while( arg ) { 277 | head = new LLI { next=head; i=arg; }; 278 | arg = arg-1; 279 | } 280 | if( !head ) return 0; 281 | LLI? next = head.next; 282 | if( !next ) return 1; 283 | return next.i; 284 | ", 285 | &types, 286 | ); 287 | let stop = parser.parse().unwrap(); 288 | parser.iterate(stop); 289 | parser.type_check(stop).unwrap(); 290 | 291 | assert_eq!(parser.print(stop), "Stop[ return 0; return 1; return .i; ]"); 292 | assert_eq!( 293 | Object::Long(0), 294 | evaluate(&parser.nodes, stop, Some(0), None).object 295 | ); 296 | assert_eq!( 297 | Object::Long(1), 298 | evaluate(&parser.nodes, stop, Some(1), None).object 299 | ); 300 | assert_eq!( 301 | Object::Long(2), 302 | evaluate(&parser.nodes, stop, Some(3), None).object 303 | ); 304 | } 305 | 306 | #[test] 307 | fn test_linked_list_2() { 308 | let arena = DroplessArena::new(); 309 | let types = Types::new(&arena); 310 | let mut parser = Parser::new( 311 | "\ 312 | struct LLI { LLI? next; int i; }; 313 | LLI? !head = null; 314 | while( arg ) { 315 | head = new LLI { 316 | next=head; 317 | // Any old code in the constructor 318 | int !tmp=arg; 319 | while( arg > 10 ) { 320 | tmp = tmp + arg; 321 | arg = arg - 1; 322 | } 323 | i=tmp; 324 | }; 325 | arg = arg-1; 326 | } 327 | if( !head ) return 0; 328 | LLI? next = head.next; 329 | if( !next ) return 1; 330 | return next.i; 331 | ", 332 | &types, 333 | ); 334 | let stop = parser.parse().unwrap(); 335 | parser.iterate(stop); 336 | parser.type_check(stop).unwrap(); 337 | 338 | assert_eq!(parser.print(stop), "Stop[ return 0; return 1; return .i; ]"); 339 | assert_eq!( 340 | Object::Long(0), 341 | evaluate(&parser.nodes, stop, Some(0), None).object 342 | ); 343 | assert_eq!( 344 | Object::Long(1), 345 | evaluate(&parser.nodes, stop, Some(1), None).object 346 | ); 347 | assert_eq!( 348 | Object::Long(2), 349 | evaluate(&parser.nodes, stop, Some(11), None).object 350 | ); 351 | } 352 | 353 | #[test] 354 | fn test_square() { 355 | let arena = DroplessArena::new(); 356 | let types = Types::new(&arena); 357 | let mut parser = Parser::new( 358 | "\ 359 | struct Square { 360 | flt !side = arg; 361 | // Newtons approximation to the square root, computed in a constructor. 362 | // The actual allocation will copy in this result as the initial 363 | // value for 'diag'. 364 | flt !diag = arg*arg/2; 365 | while( 1 ) { 366 | flt next = (side/diag + diag)/2; 367 | if( next == diag ) break; 368 | diag = next; 369 | } 370 | }; 371 | return new Square; 372 | ", 373 | &types, 374 | ); 375 | let stop = parser.parse().unwrap(); 376 | parser.iterate(stop); 377 | parser.type_check(stop).unwrap(); 378 | 379 | assert_eq!(parser.print(stop), "return Square;"); 380 | assert_eq!( 381 | "Obj{side=3.0,diag=1.7320508075688772}", 382 | evaluate(&parser.nodes, stop, Some(3), None).to_string() 383 | ); 384 | assert_eq!( 385 | "Obj{side=4.0,diag=2.0}", 386 | evaluate(&parser.nodes, stop, Some(4), None).to_string() 387 | ); 388 | } 389 | -------------------------------------------------------------------------------- /src/sea_of_nodes/tests/scheduler_test.rs: -------------------------------------------------------------------------------- 1 | use crate::datastructures::arena::DroplessArena; 2 | use crate::sea_of_nodes::parser::Parser; 3 | use crate::sea_of_nodes::tests::evaluator::{EResult, Evaluator, Heap, Object}; 4 | use crate::sea_of_nodes::types::Types; 5 | 6 | fn assert_obj(heap: &Heap, obj: EResult, name: &str, fields: &[Object]) { 7 | let EResult::Value(Object::Obj(o)) = obj else { 8 | unreachable!() 9 | }; 10 | assert_eq!(heap.objs[o].ty.name(), name); 11 | assert_eq!(heap.objs[o].fields, fields); 12 | } 13 | 14 | #[test] 15 | fn test_store_in_if() { 16 | let arena = DroplessArena::new(); 17 | let types = Types::new(&arena); 18 | let mut parser = Parser::new( 19 | "\ 20 | struct S { 21 | int f; 22 | }; 23 | S !v0=new S; 24 | if(arg) v0.f=1; 25 | return v0; 26 | ", 27 | &types, 28 | ); 29 | let stop = parser.parse().unwrap(); 30 | parser.iterate(stop); 31 | let mut eval = Evaluator::new(**stop, &parser.nodes); 32 | 33 | let result = eval.evaluate(0, 10); 34 | assert_obj(&eval.heap, result, "S", &[Object::Long(0)]); 35 | 36 | let result = eval.evaluate(1, 10); 37 | assert_obj(&eval.heap, result, "S", &[Object::Long(1)]); 38 | } 39 | 40 | #[test] 41 | fn test_store_in_if_2() { 42 | let arena = DroplessArena::new(); 43 | let types = Types::new(&arena); 44 | let mut parser = Parser::new( 45 | "\ 46 | struct S { 47 | int f; 48 | }; 49 | S !v=new S; 50 | v.f = 2; 51 | int i=new S.f; 52 | i=v.f; 53 | if (arg) v.f=1; 54 | return i; 55 | ", 56 | &types, 57 | ); 58 | let stop = parser.parse().unwrap(); 59 | parser.iterate(stop); 60 | let mut eval = Evaluator::new(**stop, &parser.nodes); 61 | assert_eq!(eval.evaluate(0, 10), EResult::Value(Object::Long(2))); 62 | assert_eq!(eval.evaluate(0, 10), EResult::Value(Object::Long(2))); 63 | } 64 | -------------------------------------------------------------------------------- /src/sea_of_nodes/tests/type_test.rs: -------------------------------------------------------------------------------- 1 | use crate::datastructures::arena::DroplessArena; 2 | use crate::sea_of_nodes::types::{Field, MemPtr, Ty, TyStruct, Type, Types}; 3 | use std::ptr; 4 | 5 | // Test basic properties and GLB 6 | #[test] 7 | fn test_type_ad_hoc() { 8 | let arena = DroplessArena::new(); 9 | let types = Types::new(&arena); 10 | 11 | let s1 = types.get_struct( 12 | "s1", 13 | &[ 14 | Field { 15 | fname: "a", 16 | ty: *types.int_bot, 17 | alias: u32::MAX, 18 | final_field: false, 19 | }, 20 | Field { 21 | fname: "b", 22 | ty: *types.int_bot, 23 | alias: u32::MAX - 1, 24 | final_field: false, 25 | }, 26 | ], 27 | ); 28 | let s2 = types.get_struct( 29 | "s2", 30 | &[ 31 | Field { 32 | fname: "a", 33 | ty: *types.int_bot, 34 | alias: u32::MAX - 2, 35 | final_field: false, 36 | }, 37 | Field { 38 | fname: "b", 39 | ty: *types.int_bot, 40 | alias: u32::MAX - 3, 41 | final_field: false, 42 | }, 43 | ], 44 | ); 45 | assert_eq!(s1, s1.glb(&types)); 46 | assert_ne!(s1, s1.dual(&types)); 47 | assert_eq!(s1.make_ro(&types), *s1.dual(&types).glb(&types)); 48 | 49 | let m1 = types.get_mem(1, *types.int_zero); 50 | let m2 = types.get_mem(2, *types.int_u16); 51 | let m3 = types.get_mem(3, *types.float_bot); 52 | let m4 = types.get_mem(4, *types.int_bot); 53 | 54 | assert_ne!(m1, m2); 55 | assert_ne!(m2, m3); 56 | assert_ne!(m3, m4); 57 | 58 | assert_eq!(types.struct_bot, s1.meet(s2, &types)); 59 | assert_eq!( 60 | *types.get_mem(u32::MAX, *types.int_u16), 61 | m1.meet(*m2, &types) 62 | ); 63 | assert_eq!(*types.mem_bot, m2.meet(*m3, &types)); 64 | assert_eq!(*types.mem_bot, m3.meet(*m4, &types)); 65 | 66 | assert_eq!(*types.get_mem(1, *types.int_bot), m1.glb(&types)); 67 | assert_eq!(*types.get_mem(1, *types.int_zero), m1.dual(&types)); 68 | assert_eq!(m4.dual(&types), m4.glb(&types).dual(&types)); 69 | 70 | let ptr1 = types.get_mem_ptr(s1, false); 71 | assert!(matches!(**ptr1, Type::MemPtr(MemPtr {to, nil: false}) if to == s1)); 72 | let ptr2 = types.get_mem_ptr(s2, false); 73 | assert!(matches!(**ptr2, Type::MemPtr(MemPtr {to, nil: false}) if to == s2)); 74 | 75 | let ptr1nil = types.get_mem_ptr(s1, true); 76 | assert!(matches!(**ptr1nil, Type::MemPtr(MemPtr {to, nil: true}) if to == s1)); 77 | let ptr2nil = types.get_mem_ptr(s2, true); 78 | assert!(matches!(**ptr2nil, Type::MemPtr(MemPtr {to, nil: true}) if to == s2)); 79 | 80 | assert_ne!(ptr1, ptr2); 81 | assert_ne!(*ptr1, ptr1.glb(&types)); 82 | assert_eq!(*ptr1nil, ptr1.glb(&types)); 83 | 84 | assert_eq!(*ptr1, ptr1.dual(&types).dual(&types)); 85 | assert_eq!( 86 | ptr1.glb(&types).make_ro(&types), 87 | ptr1.dual(&types).glb(&types) 88 | ); 89 | assert_eq!( 90 | *types.get_mem_ptr(types.struct_bot, true), 91 | ptr1.meet(*ptr2nil, &types) 92 | ); 93 | assert_eq!(ptr1.glb(&types), ptr1.meet(*types.ptr_null, &types)); 94 | 95 | let top = types.ptr_top; 96 | let bot = types.get_mem_ptr(types.struct_bot, true); 97 | let ptr = types.get_mem_ptr(types.struct_bot, false); 98 | let null = types.ptr_null; 99 | 100 | assert_eq!(*bot, ptr.meet(*null, &types)); 101 | assert_eq!(*ptr, ptr1.meet(*ptr2, &types)); 102 | assert_eq!(*top, null.join(*ptr1, &types)); 103 | assert_eq!(*top, ptr.join(*null, &types)); 104 | 105 | let _ptr1_dual = ptr1.dual(&types); 106 | let _nullable_ptr1_dual = ptr1nil.dual(&types); 107 | } 108 | 109 | // Test theoretical properties. 110 | // This is a symmetric complete bounded (ranked) lattice. 111 | // Also, the meet is commutative and associative. 112 | // The lattice has a dual (symmetric), and join is ~(~x meet ~y). 113 | // See https://en.wikipedia.org/wiki/Lattice_(order). 114 | #[test] 115 | fn test_lattice_theory() { 116 | let arena = DroplessArena::new(); 117 | let types = Types::new(&arena); 118 | 119 | let ts = types.gather(); 120 | 121 | // Confirm commutative & complete 122 | for &t0 in &ts { 123 | for &t1 in &ts { 124 | types.check_commute(t0, t1); 125 | types.check_symmetric(t0, t1); 126 | } 127 | } 128 | 129 | // Confirm associative 130 | for &t0 in &ts { 131 | for &t1 in &ts { 132 | for &t2 in &ts { 133 | types.assoc(t0, t1, t2); 134 | } 135 | } 136 | } 137 | 138 | // Confirm symmetry. If A isa B, then A.join(C) isa B.join(C) 139 | for &t0 in &ts { 140 | for &t1 in &ts { 141 | if t0.isa(t1, &types) { 142 | for &t2 in &ts { 143 | let t02 = t0.join(t2, &types); 144 | let t12 = t1.join(t2, &types); 145 | let mt = t02.meet(t12, &types); 146 | assert_same(mt, t12); 147 | } 148 | } 149 | } 150 | } 151 | } 152 | 153 | /// Test cyclic types and meets 154 | #[test] 155 | fn test_cyclic_0() { 156 | let arena = DroplessArena::new(); 157 | let types = Types::new(&arena); 158 | 159 | let (_, _, s1, _) = TyStruct::test_data(&types); 160 | 161 | let d0 = s1.dual(&types); 162 | let d1 = d0.dual(&types); 163 | assert_same(*s1, *d1); 164 | } 165 | 166 | fn assert_same<'t>(a: Ty<'t>, b: Ty<'t>) { 167 | assert_eq!(a, b); 168 | assert!(ptr::eq(a.data(), b.data())); 169 | } 170 | 171 | impl<'t> Types<'t> { 172 | // By design in meet, args are already flipped to order _type, which forces 173 | // symmetry for things with badly ordered _type fields. The question is 174 | // still interesting for other orders. 175 | fn check_commute(&self, t0: Ty<'t>, t1: Ty<'t>) { 176 | if t0 == t1 { 177 | return; 178 | } 179 | // if( t0.is_simple() && !t1.is_simple() ) return; // By design, flipped the only allowed order 180 | let mta = t0.meet(t1, self); 181 | let mtb = t1.meet(t0, self); // Reverse args and try again 182 | assert_same(mta, mtb); 183 | } 184 | 185 | // A & B = MT 186 | // Expect: ~A & ~MT == ~A 187 | // Expect: ~B & ~MT == ~B 188 | fn check_symmetric(&self, t0: Ty<'t>, t1: Ty<'t>) { 189 | if t1 == t0 { 190 | return; 191 | }; 192 | let mt = t0.meet(t1, self); 193 | let dm = mt.dual(self); 194 | let d0 = t0.dual(self); 195 | let d1 = t1.dual(self); 196 | let ta = dm.meet(d1, self); 197 | let tb = dm.meet(d0, self); 198 | assert_same(ta, d1); 199 | assert_same(tb, d0); 200 | } 201 | 202 | fn assoc(&self, t0: Ty<'t>, t1: Ty<'t>, t2: Ty<'t>) { 203 | let t01 = t0.meet(t1, self); 204 | let t12 = t1.meet(t2, self); 205 | let t01_2 = t01.meet(t2, self); 206 | let t0_12 = t0.meet(t12, self); 207 | assert_same(t01_2, t0_12); 208 | } 209 | 210 | fn gather(&self) -> Vec> { 211 | let struct_test = self.get_struct( 212 | "test", 213 | &[Field { 214 | fname: "test", 215 | ty: *self.int_zero, 216 | alias: u32::MAX - 1, 217 | final_field: false, 218 | }], 219 | ); 220 | let (_, _, s1, s2) = TyStruct::test_data(self); 221 | let ptr_test = self.get_mem_ptr(struct_test, false); 222 | let mut ts = vec![ 223 | self.bot, 224 | self.ctrl, 225 | // 226 | *self.int_zero, 227 | self.bot, 228 | // 229 | *self.get_mem(1, *self.int_zero), 230 | *self.mem_bot, 231 | // 232 | *self.ptr_null, 233 | *self.ptr_bot, 234 | *ptr_test, 235 | // 236 | *struct_test, 237 | *self.struct_bot, 238 | *s1, 239 | *s2, 240 | // 241 | *self.get_tuple_from_array([*self.int_bot, *ptr_test]), 242 | ]; 243 | let t2 = ts.iter().map(|t| t.dual(self)).collect::>(); 244 | ts.extend(t2); 245 | ts 246 | } 247 | } 248 | -------------------------------------------------------------------------------- /src/sea_of_nodes/types.rs: -------------------------------------------------------------------------------- 1 | use crate::datastructures::arena::DroplessArena; 2 | pub use crate::sea_of_nodes::types::field::Field; 3 | pub use crate::sea_of_nodes::types::r#type::*; 4 | pub use crate::sea_of_nodes::types::ty::{Ty, TyFloat, TyInt, TyMem, TyMemPtr, TyStruct, TyTuple}; 5 | pub use crate::sea_of_nodes::types::type_float::Float; 6 | pub use crate::sea_of_nodes::types::type_integer::Int; 7 | pub use crate::sea_of_nodes::types::type_mem::Mem; 8 | pub use crate::sea_of_nodes::types::type_mem_ptr::MemPtr; 9 | pub use crate::sea_of_nodes::types::type_struct::Struct; 10 | pub use crate::sea_of_nodes::types::type_tuple::Tuple; 11 | use std::cell::RefCell; 12 | use std::collections::{HashMap, HashSet}; 13 | 14 | mod field; 15 | mod ty; 16 | mod r#type; 17 | mod type_float; 18 | mod type_integer; 19 | mod type_mem; 20 | mod type_mem_ptr; 21 | mod type_struct; 22 | mod type_tuple; 23 | 24 | /// Types are interned, so that equality checks and hashing are cheap. 25 | /// 26 | /// Interior mutability is used for interning, so there is no need to pass 27 | /// around mutable references. 28 | /// 29 | /// For references, `&'t Types<'t>` should be preferred over `&'a Types<'t>`, 30 | /// because we don't want `Ty<'t>` references that outlive their `Types`: 31 | /// 32 | /// ```compile_fail 33 | /// # use simple_rust::datastructures::arena ::DroplessArena; 34 | /// # use simple_rust::sea_of_nodes::types::{Ty, Types}; 35 | /// 36 | /// fn get_t<'t>(types: &'t Types<'t>) -> Ty<'t> {types.ty_bot}; 37 | /// 38 | /// let arena = DroplessArena::new(); 39 | /// let types = Types::new(&arena); 40 | /// let t1 = get_t(&types); 41 | /// drop(types); 42 | /// let types = Types::new(&arena); 43 | /// let t2 = get_t(&types); 44 | /// assert_ne!(t1, t2); 45 | /// ``` 46 | /// Without `&'t` in the function parameter (or without `drop(types)`) this would 47 | /// successfully compile and run. 48 | pub struct Types<'a> { 49 | interner: Interner<'a>, 50 | struct_offsets: RefCell, &'a [usize]>>, 51 | 52 | pub bot: Ty<'a>, 53 | pub top: Ty<'a>, 54 | pub ctrl: Ty<'a>, 55 | pub xctrl: Ty<'a>, 56 | pub int_top: TyInt<'a>, 57 | pub int_bot: TyInt<'a>, 58 | pub int_zero: TyInt<'a>, 59 | pub int_one: TyInt<'a>, 60 | pub int_two: TyInt<'a>, 61 | pub int_u1: TyInt<'a>, 62 | pub int_bool: TyInt<'a>, 63 | pub int_false: TyInt<'a>, 64 | pub int_true: TyInt<'a>, 65 | pub int_i8: TyInt<'a>, 66 | pub int_i16: TyInt<'a>, 67 | pub int_i32: TyInt<'a>, 68 | pub int_u8: TyInt<'a>, 69 | pub int_u16: TyInt<'a>, 70 | pub int_u32: TyInt<'a>, 71 | pub float_top: TyFloat<'a>, 72 | pub float_t32: TyFloat<'a>, 73 | pub float_zero: TyFloat<'a>, 74 | pub float_b32: TyFloat<'a>, 75 | pub float_bot: TyFloat<'a>, 76 | pub if_both: TyTuple<'a>, 77 | pub if_neither: TyTuple<'a>, 78 | pub if_true: TyTuple<'a>, 79 | pub if_false: TyTuple<'a>, 80 | pub struct_bot: TyStruct<'a>, 81 | pub struct_top: TyStruct<'a>, 82 | pub mem_bot: TyMem<'a>, 83 | pub mem_top: TyMem<'a>, 84 | pub ptr_top: TyMemPtr<'a>, 85 | pub ptr_bot: TyMemPtr<'a>, 86 | pub ptr_null: TyMemPtr<'a>, 87 | pub ptr_void: TyMemPtr<'a>, 88 | } 89 | 90 | impl<'a> Interner<'a> { 91 | fn get_float(&self, sz: i8, con: f64) -> TyFloat<'a> { 92 | self.intern(Type::Float(Float::new(sz, con))) 93 | .to_float() 94 | .unwrap() 95 | } 96 | 97 | fn get_int(&self, min: i64, max: i64) -> TyInt<'a> { 98 | self.intern(Type::Int(Int { min, max })).to_int().unwrap() 99 | } 100 | 101 | fn get_mem(&self, alias: u32, t: Ty<'a>) -> TyMem<'a> { 102 | self.intern(Type::Mem(Mem { alias, t })).to_mem().unwrap() 103 | } 104 | 105 | fn get_ptr(&self, obj: TyStruct<'a>, nil: bool) -> TyMemPtr<'a> { 106 | self.intern(Type::MemPtr(MemPtr { to: obj, nil })) 107 | .to_mem_ptr() 108 | .unwrap() 109 | } 110 | 111 | fn get_struct(&self, name: &'a str, fields: Option<&'a [Field<'a>]>) -> TyStruct<'a> { 112 | self.intern(Type::Struct(Struct { name, fields })) 113 | .to_struct() 114 | .unwrap() 115 | } 116 | 117 | fn get_tuple(&self, tuple: &'a [Ty<'a>]) -> TyTuple<'a> { 118 | self.intern(Type::Tuple(tuple)).to_tuple().unwrap() 119 | } 120 | } 121 | 122 | impl<'a> Types<'a> { 123 | pub fn new(arena: &'a DroplessArena) -> Self { 124 | let interner = Interner::new(arena); 125 | let intern = |x| interner.intern(x); 126 | 127 | let bot = intern(Type::Bot); 128 | let top = intern(Type::Top); 129 | let ctrl = intern(Type::Ctrl); 130 | let xctrl = intern(Type::XCtrl); 131 | 132 | let int_zero = interner.get_int(0, 0); 133 | let int_one = interner.get_int(1, 1); 134 | let int_u1 = interner.get_int(0, 1); 135 | 136 | let struct_top = interner.get_struct("$TOP", Some(&[])); 137 | let struct_bot = interner.get_struct("$BOT", Some(&[])); 138 | 139 | Self { 140 | struct_offsets: RefCell::new(HashMap::new()), 141 | bot, 142 | top, 143 | ctrl, 144 | xctrl, 145 | int_top: interner.get_int(i64::MAX, i64::MIN), 146 | int_bot: interner.get_int(i64::MIN, i64::MAX), 147 | int_zero, 148 | int_one, 149 | int_two: interner.get_int(2, 2), 150 | int_u1, 151 | int_bool: int_u1, 152 | int_false: int_zero, 153 | int_true: int_one, 154 | int_i8: interner.get_int(-128, 127), 155 | int_i16: interner.get_int(-32768, 32767), 156 | int_i32: interner.get_int(-1 << 31, (1 << 31) - 1), 157 | int_u8: interner.get_int(0, 255), 158 | int_u16: interner.get_int(0, 65535), 159 | int_u32: interner.get_int(0, (1 << 32) - 1), 160 | float_top: interner.get_float(-64, 0.0), 161 | float_t32: interner.get_float(-32, 0.0), 162 | float_zero: interner.get_float(0, 0.0), 163 | float_b32: interner.get_float(32, 0.0), 164 | float_bot: interner.get_float(64, 0.0), 165 | if_both: interner.get_tuple(arena.alloc([ctrl, ctrl])), 166 | if_neither: interner.get_tuple(arena.alloc([xctrl, xctrl])), 167 | if_true: interner.get_tuple(arena.alloc([ctrl, xctrl])), 168 | if_false: interner.get_tuple(arena.alloc([xctrl, ctrl])), 169 | struct_bot, 170 | struct_top, 171 | mem_top: interner.get_mem(u32::MAX, top), 172 | mem_bot: interner.get_mem(u32::MAX, bot), 173 | ptr_bot: interner.get_ptr(struct_bot, true), 174 | ptr_top: interner.get_ptr(struct_top, false), 175 | ptr_null: interner.get_ptr(struct_top, true), 176 | ptr_void: interner.get_ptr(struct_bot, false), 177 | interner, 178 | } 179 | } 180 | 181 | pub fn get_bool(&self, value: bool) -> TyInt<'a> { 182 | if value { 183 | self.int_true 184 | } else { 185 | self.int_false 186 | } 187 | } 188 | 189 | pub fn get_int(&self, value: i64) -> TyInt<'a> { 190 | self.interner.get_int(value, value) 191 | } 192 | 193 | pub fn make_int(&self, min: i64, max: i64) -> TyInt<'a> { 194 | self.interner.get_int(min, max) 195 | } 196 | 197 | pub fn get_float(&self, constant: f64) -> TyFloat<'a> { 198 | self.interner.get_float(0, constant) 199 | } 200 | 201 | pub fn make_float(&self, sz: i8, constant: f64) -> TyFloat<'a> { 202 | self.interner.get_float(sz, constant) 203 | } 204 | 205 | pub fn get_tuple_from_slice(&self, types: &[Ty<'a>]) -> TyTuple<'a> { 206 | self.interner 207 | .get_tuple(self.interner.arena.alloc_slice_copy(types)) 208 | } 209 | 210 | pub fn get_tuple_from_array(&self, types: [Ty<'a>; N]) -> TyTuple<'a> { 211 | self.interner.get_tuple(self.interner.arena.alloc(types)) 212 | } 213 | 214 | pub fn get_str(&self, name: &str) -> &'a str { 215 | self.interner.intern_str(name) 216 | } 217 | 218 | pub fn get_slice(&self, slice: &[T]) -> &'a [T] { 219 | self.interner.arena.alloc_slice_copy(slice) 220 | } 221 | 222 | pub fn get_struct(&self, name: &'a str, fields: &[Field<'a>]) -> TyStruct<'a> { 223 | let fields = Some(&*self.interner.arena.alloc_slice_copy(fields)); 224 | self.interner 225 | .intern(Type::Struct(Struct { name, fields })) 226 | .to_struct() 227 | .unwrap() 228 | } 229 | 230 | pub fn make_ary( 231 | &self, 232 | len: TyInt<'a>, 233 | len_alias: u32, 234 | body: Ty<'a>, 235 | body_alias: u32, 236 | ) -> TyStruct<'a> { 237 | debug_assert!(!body.to_mem_ptr().is_some_and(|t| !t.data().nil)); 238 | let name = self.get_str(&format!("[{}]", body.str())); 239 | let fields = self.interner.arena.alloc([ 240 | Field { 241 | fname: "#", 242 | ty: *len, 243 | alias: len_alias, 244 | final_field: true, 245 | }, 246 | Field { 247 | fname: "[]", 248 | ty: body, 249 | alias: body_alias, 250 | final_field: false, 251 | }, 252 | ]); 253 | self.interner.get_struct(name, Some(fields)) 254 | } 255 | pub fn make_struct_fref(&self, name: &'a str) -> TyStruct<'a> { 256 | self.interner.get_struct(name, None) 257 | } 258 | 259 | pub fn get_mem_ptr(&self, to: TyStruct<'a>, nil: bool) -> TyMemPtr<'a> { 260 | self.interner.get_ptr(to, nil) 261 | } 262 | 263 | pub fn get_mem(&self, alias: u32, ty: Ty<'a>) -> TyMem<'a> { 264 | self.interner.get_mem(alias, ty) 265 | } 266 | } 267 | 268 | struct Interner<'a> { 269 | arena: &'a DroplessArena, 270 | 271 | // If we ever want multithreading this could be a sharded hashmap like in rustc. 272 | // See InternedSet in rustc_middle/src/ty/context.rs 273 | type_to_ty: RefCell, Ty<'a>>>, 274 | 275 | strings: RefCell>, 276 | } 277 | 278 | impl<'t> Interner<'t> { 279 | fn new(arena: &'t DroplessArena) -> Self { 280 | Self { 281 | arena, 282 | type_to_ty: Default::default(), 283 | strings: Default::default(), 284 | } 285 | } 286 | 287 | fn intern(&self, t: Type<'t>) -> Ty<'t> { 288 | *self 289 | .type_to_ty 290 | .borrow_mut() 291 | .raw_entry_mut() 292 | .from_key(&t) 293 | .or_insert_with(|| { 294 | let copy = &*self.arena.alloc(t); 295 | let ty = Ty::new(copy); 296 | (copy, ty) 297 | }) 298 | .1 299 | } 300 | 301 | fn intern_str(&self, s: &str) -> &'t str { 302 | self.strings 303 | .borrow_mut() 304 | .get_or_insert_with(s, |_| self.arena.alloc_str(s)) 305 | } 306 | } 307 | 308 | #[cfg(test)] 309 | mod tests { 310 | use super::Interner; 311 | use crate::datastructures::arena::DroplessArena; 312 | use crate::sea_of_nodes::types::{Int, Type}; 313 | use std::ptr; 314 | 315 | #[test] 316 | fn test_interner() { 317 | let arena = DroplessArena::new(); 318 | let interner = Interner::new(&arena); 319 | 320 | let ty_bot_1 = interner.intern(Type::Bot); 321 | let ty_bot_2 = interner.intern(Type::Bot); 322 | assert!(ptr::eq(ty_bot_1.data(), ty_bot_2.data())); 323 | 324 | let ty_42 = interner.intern(Type::Int(Int { min: 42, max: 42 })); 325 | let ty_2 = interner.intern(Type::Int(Int { min: 2, max: 2 })); 326 | let ty_42_too = interner.intern(Type::Int(Int { min: 42, max: 42 })); 327 | 328 | assert!(!ptr::eq(ty_42.data(), ty_2.data())); 329 | assert!(ptr::eq(ty_42.data(), ty_42_too.data())); 330 | 331 | let t1 = interner.intern(Type::Tuple( 332 | arena.alloc([ty_bot_1, ty_bot_2, ty_42, ty_2, ty_42_too]), 333 | )); 334 | let t2 = interner.intern(Type::Tuple( 335 | arena.alloc([ty_bot_2, ty_bot_1, ty_42_too, ty_2, ty_42]), 336 | )); 337 | let t3 = interner.intern(Type::Tuple( 338 | arena.alloc([ty_bot_1, ty_bot_2, ty_42, ty_2, ty_2]), 339 | )); 340 | assert!(ptr::eq(t1.data(), t2.data())); 341 | assert!(!ptr::eq(t1.data(), t3.data())); 342 | } 343 | 344 | #[test] 345 | fn test_strings() { 346 | let arena = DroplessArena::new(); 347 | let interner = Interner::new(&arena); 348 | let a = interner.intern_str("foo"); 349 | let b = interner.intern_str("bar"); 350 | let aa = interner.intern_str("foo"); 351 | let aaa = interner.intern_str(a); 352 | let bb = interner.intern_str(b); 353 | 354 | assert!(ptr::eq(a, aa)); 355 | assert!(ptr::eq(a, aaa)); 356 | assert_ne!(a, b); 357 | assert!(ptr::eq(b, bb)); 358 | } 359 | } 360 | -------------------------------------------------------------------------------- /src/sea_of_nodes/types/field.rs: -------------------------------------------------------------------------------- 1 | use crate::sea_of_nodes::types::Ty; 2 | 3 | /// Represents a field in a struct. This is not a Type in the type system. 4 | /// The pair {fieldName,type} uniquely identifies a field. 5 | #[derive(Eq, PartialEq, Copy, Clone, Hash, Debug)] 6 | pub struct Field<'t> { 7 | /// Field name 8 | pub fname: &'t str, 9 | /// Type of the field 10 | pub ty: Ty<'t>, 11 | /// Unique memory alias, not sensibly part of a "type" but very convenient here. 12 | pub alias: u32, 13 | /// Field must be written to exactly once, no more, no less 14 | pub final_field: bool, 15 | } 16 | -------------------------------------------------------------------------------- /src/sea_of_nodes/types/ty.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::{Debug, Display, Formatter}; 2 | use std::hash::{Hash, Hasher}; 3 | use std::ops::Deref; 4 | use std::{fmt, ptr}; 5 | 6 | use crate::sea_of_nodes::types::{Float, Int, Mem, MemPtr, Struct, Tuple, Type}; 7 | 8 | /// A reference to an interned type. 9 | /// Equality and hashing is based on the value of the pointer. 10 | /// `Option` should still be 8 bytes because the reference is never null. 11 | #[derive(Copy, Clone, Eq)] 12 | pub struct Ty<'t>(&'t Type<'t>); 13 | 14 | const _: () = assert!(size_of::() == size_of::>()); 15 | 16 | impl<'t> Ty<'t> { 17 | pub fn new(t: &'t Type<'t>) -> Self { 18 | Self(t) 19 | } 20 | pub fn data(&self) -> &'t Type<'t> { 21 | self.0 22 | } 23 | } 24 | 25 | impl PartialEq for Ty<'_> { 26 | fn eq(&self, other: &Self) -> bool { 27 | ptr::eq(self.0 as *const Type, other.0 as *const Type) 28 | } 29 | } 30 | 31 | impl Hash for Ty<'_> { 32 | fn hash(&self, state: &mut H) { 33 | (self.0 as *const Type).hash(state) 34 | } 35 | } 36 | 37 | impl Debug for Ty<'_> { 38 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 39 | Debug::fmt(self.0, f) 40 | } 41 | } 42 | 43 | impl<'t> Display for Ty<'t> { 44 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 45 | Display::fmt(self.0, f) 46 | } 47 | } 48 | 49 | impl<'a> Deref for Ty<'a> { 50 | type Target = Type<'a>; 51 | 52 | fn deref(&self) -> &Self::Target { 53 | self.0 54 | } 55 | } 56 | 57 | macro_rules! impl_subtype { 58 | ($Subtype:ident($Variant:ident$(<$v:lifetime>)?) { $cast:ident $checkcast:ident $as_cast:ident } ) => { 59 | #[derive(Copy, Clone, Eq, PartialEq, Hash)] 60 | pub struct $Subtype<'t>(Ty<'t>); 61 | 62 | impl<'t> $Subtype<'t> { 63 | pub fn data(self) -> &'t $Variant$(<$v> where $v : 't)? { 64 | match self.0.data() { 65 | Type::$Variant(x) => x, 66 | _ => unreachable!(), 67 | } 68 | } 69 | } 70 | 71 | impl<'t> TryFrom> for $Subtype<'t> { 72 | type Error = (); 73 | 74 | fn try_from(value: Ty<'t>) -> Result { 75 | match &*value { 76 | Type::$Variant(_) => Ok(Self(value)), 77 | _ => Err(()), 78 | } 79 | } 80 | } 81 | impl<'t> Ty<'t> { 82 | pub fn $cast(self) -> Option<$Subtype<'t>> { 83 | self.try_into().ok() 84 | } 85 | pub fn $checkcast(self) -> bool { 86 | self.$cast().is_some() 87 | } 88 | pub fn $as_cast(self) -> $Subtype<'t> { 89 | self.try_into().unwrap() 90 | } 91 | } 92 | 93 | impl<'t> From<$Subtype<'t>> for Ty<'t> { 94 | fn from(t: $Subtype<'t>) -> Self { t.0 } 95 | } 96 | impl<'t> Deref for $Subtype<'t> { 97 | type Target = Ty<'t>; 98 | fn deref(&self) -> &Self::Target { 99 | &self.0 100 | } 101 | } 102 | 103 | 104 | impl Debug for $Subtype<'_> { 105 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 106 | Debug::fmt(&self.0, f) 107 | } 108 | } 109 | impl Display for $Subtype<'_> { 110 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 111 | Display::fmt(&self.0, f) 112 | } 113 | } 114 | 115 | }; 116 | } 117 | 118 | impl_subtype!(TyInt(Int) { to_int is_int as_int }); 119 | impl_subtype!(TyFloat(Float) { to_float is_float as_float }); 120 | impl_subtype!(TyTuple(Tuple<'t>) { to_tuple is_tuple as_tuple }); 121 | impl_subtype!(TyStruct(Struct<'t>) { to_struct is_struct as_struct }); 122 | impl_subtype!(TyMemPtr(MemPtr<'t>) { to_mem_ptr is_mem_ptr as_mem_ptr }); 123 | impl_subtype!(TyMem(Mem<'t>) { to_mem is_mem as_mem }); 124 | -------------------------------------------------------------------------------- /src/sea_of_nodes/types/type_float.rs: -------------------------------------------------------------------------------- 1 | use crate::sea_of_nodes::types::ty::TyFloat; 2 | use crate::sea_of_nodes::types::Types; 3 | 4 | #[derive(Eq, PartialEq, Copy, Clone, Hash, Debug)] 5 | pub struct Float { 6 | pub sz: i8, 7 | /// f64 bits as integer for easier eq/hashing 8 | con: u64, 9 | } 10 | 11 | impl Float { 12 | pub fn new(sz: i8, con: f64) -> Self { 13 | Self { 14 | sz, 15 | con: con.to_bits(), 16 | } 17 | } 18 | 19 | pub fn con(&self) -> f64 { 20 | f64::from_bits(self.con) 21 | } 22 | 23 | pub fn is_f32(&self) -> bool { 24 | let v = self.con(); 25 | v as f32 as f64 == v 26 | } 27 | } 28 | 29 | impl<'t> TyFloat<'t> { 30 | pub fn sz(self) -> i8 { 31 | self.data().sz 32 | } 33 | pub fn is_f32(&self) -> bool { 34 | self.data().is_f32() 35 | } 36 | 37 | pub fn value(self) -> Option { 38 | let d = self.data(); 39 | (d.sz == 0).then_some(d.con()) 40 | } 41 | 42 | pub fn meet(self, that: TyFloat<'t>, tys: &Types<'t>) -> TyFloat<'t> { 43 | // Larger size in i1, smaller in i0 44 | let (i0, i1) = if self.sz() < that.sz() { 45 | (self, that) 46 | } else { 47 | (that, self) 48 | }; 49 | 50 | if i1.sz() == 64 { 51 | tys.float_bot 52 | } else if i0.sz() == -64 { 53 | i1 54 | } else if i1.sz() == 32 { 55 | if i0.sz() == 0 && !i0.is_f32() { 56 | tys.float_bot 57 | } else { 58 | tys.float_b32 59 | } 60 | } else if i1.sz() != 0 { 61 | i1 62 | } else if i0.sz() == -32 { 63 | // i1 is a constant 64 | if i1.is_f32() { 65 | i1 66 | } else { 67 | tys.float_bot 68 | } 69 | } else { 70 | // Since both are constants, and are never equals (contract) unequals 71 | // constants fall to bottom 72 | if i0.is_f32() && i1.is_f32() { 73 | tys.float_b32 74 | } else { 75 | tys.float_bot 76 | } 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/sea_of_nodes/types/type_integer.rs: -------------------------------------------------------------------------------- 1 | use crate::sea_of_nodes::types::ty::TyInt; 2 | use crate::sea_of_nodes::types::Types; 3 | 4 | #[derive(Eq, PartialEq, Copy, Clone, Hash, Debug)] 5 | pub struct Int { 6 | pub min: i64, 7 | pub max: i64, 8 | } 9 | 10 | impl<'t> TyInt<'t> { 11 | pub fn min(self) -> i64 { 12 | self.data().min 13 | } 14 | 15 | pub fn max(self) -> i64 { 16 | self.data().max 17 | } 18 | 19 | pub fn value(self) -> Option { 20 | let d = self.data(); 21 | (d.min == d.max).then_some(d.min) 22 | } 23 | 24 | pub fn is_constant(self) -> bool { 25 | self.value().is_some() 26 | } 27 | 28 | /// AND-mask of forced zeros. e.g. unsigned types will return their mask; 29 | /// u8 will return 0xFF. But also a range of 16-18 (0x10-0x12) will return 30 | /// 0x13 - no value in the range {16,17,18} will allow bit 0x04 to be set. 31 | pub fn mask(self, tys: &Types<'t>) -> i64 { 32 | if self.is_high(tys) { 33 | return 0; 34 | } 35 | if let Some(v) = self.value() { 36 | return v; 37 | } 38 | //if( _min<0 ) return -1L; 39 | //if( _max==Long.MAX_VALUE ) return -1L; 40 | // Those bit positions which differ min to max 41 | let x = self.min() ^ self.max(); 42 | // Highest '1' bit in the differ set. Since the range is from min to 43 | // max, all values below here are possible. 44 | let highest_one_bit = |i: i64| i & (i64::MIN as u64 >> i.leading_zeros()) as i64; 45 | let ff1 = highest_one_bit(x); 46 | // Make a all-1's mask from ff1, and set over the same bits (either min 47 | // or max is ok). 48 | self.min() | ff1.wrapping_sub(1) | ff1 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/sea_of_nodes/types/type_mem.rs: -------------------------------------------------------------------------------- 1 | use crate::sea_of_nodes::types::Ty; 2 | 3 | #[derive(Eq, PartialEq, Copy, Clone, Hash, Debug)] 4 | pub struct Mem<'t> { 5 | pub alias: u32, 6 | /// Memory contents, some scalar type 7 | pub t: Ty<'t>, 8 | } 9 | -------------------------------------------------------------------------------- /src/sea_of_nodes/types/type_mem_ptr.rs: -------------------------------------------------------------------------------- 1 | use crate::sea_of_nodes::types::TyStruct; 2 | 3 | #[derive(Eq, PartialEq, Copy, Clone, Hash, Debug)] 4 | pub struct MemPtr<'t> { 5 | pub to: TyStruct<'t>, 6 | pub nil: bool, 7 | } 8 | -------------------------------------------------------------------------------- /src/sea_of_nodes/types/type_struct.rs: -------------------------------------------------------------------------------- 1 | use crate::sea_of_nodes::types::{Field, TyStruct, Types}; 2 | 3 | #[derive(Eq, PartialEq, Copy, Clone, Hash, Debug)] 4 | pub struct Struct<'t> { 5 | pub name: &'t str, 6 | /// None means that this is a forward reference 7 | pub fields: Option<&'t [Field<'t>]>, 8 | } 9 | 10 | impl<'t> TyStruct<'t> { 11 | pub fn name(self) -> &'t str { 12 | self.data().name 13 | } 14 | 15 | pub fn fields(self) -> &'t [Field<'t>] { 16 | self.data().fields.unwrap() 17 | } 18 | 19 | pub fn find(self, fname: &str) -> Option { 20 | self.fields().iter().position(|f| f.fname == fname) 21 | } 22 | 23 | pub fn field(self, fname: &str) -> Option<&Field<'t>> { 24 | let index = self.find(fname)?; 25 | Some(&self.fields()[index]) 26 | } 27 | 28 | pub fn find_alias(self, alias: u32) -> Option { 29 | self.fields().iter().position(|f| f.alias == alias) 30 | } 31 | 32 | pub fn is_ary(self) -> bool { 33 | self.fields().len() == 2 && self.fields()[1].fname == "[]" 34 | } 35 | 36 | pub fn ary_base(self, tys: &Types<'t>) -> i64 { 37 | debug_assert!(self.is_ary()); 38 | self.offset(1, tys) 39 | } 40 | 41 | pub fn ary_scale(self, tys: &Types<'t>) -> i64 { 42 | debug_assert!(self.is_ary()); 43 | self.fields()[1].ty.log_size(tys) as i64 44 | } 45 | 46 | pub fn offset(self, index: usize, tys: &Types<'t>) -> i64 { 47 | tys.struct_offsets 48 | .borrow_mut() 49 | .entry(self) 50 | .or_insert_with(|| tys.get_slice(&self.offsets(tys)))[index] as i64 51 | } 52 | 53 | /// Field byte offsets 54 | fn offsets(self, tys: &Types<'t>) -> Vec { 55 | // Compute a layout for a collection of fields 56 | let fields = self.fields(); // No forward refs 57 | 58 | // Compute a layout 59 | let mut cnts = [0, 0, 0, 0]; // Count of fields at log field size 60 | for f in fields { 61 | cnts[f.ty.log_size(tys)] += 1; // Log size is 0(byte), 1(i16/u16), 2(i32/f32), 3(i64/dbl) 62 | } 63 | 64 | // Base common struct fields go here, e.g. Mark/Klass 65 | let mut off = 0; 66 | 67 | // Compute offsets to the start of each power-of-2 aligned fields. 68 | let mut offs = [0, 0, 0, 0]; 69 | for i in (0..4).rev() { 70 | offs[i] = off; 71 | off += cnts[i] << i; 72 | } 73 | // Assign offsets to all fields. 74 | // Really a hidden radix sort. 75 | let mut result = Vec::with_capacity(fields.len() + 1); 76 | for f in fields { 77 | let log = f.ty.log_size(tys); 78 | result.push(offs[log]); // Field offset 79 | offs[log] += 1 << log; // Next field offset at same alignment 80 | cnts[log] -= 1; // Count down, should be all zero at end 81 | } 82 | result.push((off + 7) & !7); // Round out to max alignment 83 | result 84 | } 85 | 86 | pub fn meet(self, that: TyStruct<'t>, tys: &Types<'t>) -> TyStruct<'t> { 87 | if self == tys.struct_bot || that == tys.struct_top { 88 | return self; 89 | } 90 | if that == tys.struct_bot || self == tys.struct_top { 91 | return that; 92 | } 93 | // Within the same compilation unit, struct names are unique. If the 94 | // names differ, its different structs. Across many compilation units, 95 | // structs with the same name but different field layouts can be 96 | // interned... which begs the question: 97 | // "What is the meet of structs from two different compilation units?" 98 | // And the answer is: "don't ask". 99 | if self.name() != that.name() { 100 | return tys.struct_bot; // It's a struct; that's about all we know 101 | } 102 | let Some(s_fs) = self.data().fields else { 103 | return that; 104 | }; 105 | let Some(t_fs) = that.data().fields else { 106 | return self; 107 | }; 108 | if s_fs.len() != t_fs.len() { 109 | return tys.struct_bot; 110 | } 111 | 112 | // Just do field meets 113 | let mut flds = Vec::with_capacity(s_fs.len()); 114 | for (f0, f1) in s_fs.iter().zip(t_fs) { 115 | if f0.fname != f1.fname || f0.alias != f1.alias { 116 | return tys.struct_bot; 117 | } 118 | flds.push(Field { 119 | fname: f0.fname, 120 | ty: f0.ty.meet(f1.ty, tys), 121 | alias: f0.alias, 122 | final_field: f0.final_field | f1.final_field, 123 | }) 124 | } 125 | tys.get_struct(self.name(), &flds) 126 | } 127 | 128 | pub fn dual(self, tys: &Types<'t>) -> TyStruct<'t> { 129 | if self == tys.struct_top { 130 | tys.struct_bot 131 | } else if self == tys.struct_bot { 132 | tys.struct_top 133 | } else if let Some(fields) = self.data().fields { 134 | let fields = fields 135 | .iter() 136 | .map(|f| Field { 137 | fname: f.fname, 138 | ty: f.ty.dual(tys), 139 | alias: f.alias, 140 | final_field: !f.final_field, 141 | }) 142 | .collect::>(); 143 | tys.get_struct(self.name(), &fields) 144 | } else { 145 | self 146 | } 147 | } 148 | 149 | pub fn glb(self, tys: &Types<'t>) -> TyStruct<'t> { 150 | if let Some(fields) = self.data().fields { 151 | let new_fields = fields 152 | .iter() 153 | .map(|f| Field { 154 | ty: f.ty.glb(tys), 155 | ..*f 156 | }) 157 | .collect::>(); 158 | tys.get_struct(self.name(), &new_fields) 159 | } else { 160 | self 161 | } 162 | } 163 | 164 | pub fn lub(self, tys: &Types<'t>) -> TyStruct<'t> { 165 | if let Some(fields) = self.data().fields { 166 | let new_fields = fields 167 | .iter() 168 | .map(|f| Field { 169 | ty: f.ty.lub(tys), 170 | ..*f 171 | }) 172 | .collect::>(); 173 | tys.get_struct(self.name(), &new_fields) 174 | } else { 175 | self 176 | } 177 | } 178 | 179 | #[cfg(test)] 180 | pub(crate) fn test_data( 181 | tys: &Types<'t>, 182 | ) -> (TyStruct<'t>, TyStruct<'t>, TyStruct<'t>, TyStruct<'t>) { 183 | let s1f = tys.make_struct_fref("S1"); 184 | let s2f = tys.make_struct_fref("S2"); 185 | let s1 = tys.get_struct( 186 | "S1", 187 | &[ 188 | Field { 189 | fname: "a", 190 | ty: *tys.int_bot, 191 | alias: u32::MAX, 192 | final_field: false, 193 | }, 194 | Field { 195 | fname: "s2", 196 | ty: *tys.get_mem_ptr(s2f, false), 197 | alias: u32::MAX - 1, 198 | final_field: false, 199 | }, 200 | ], 201 | ); 202 | let s2 = tys.get_struct( 203 | "S2", 204 | &[ 205 | Field { 206 | fname: "b", 207 | ty: *tys.float_bot, 208 | alias: u32::MAX - 2, 209 | final_field: false, 210 | }, 211 | Field { 212 | fname: "s1", 213 | ty: *tys.get_mem_ptr(s1f, false), 214 | alias: u32::MAX - 3, 215 | final_field: false, 216 | }, 217 | ], 218 | ); 219 | (s1f, s2f, s1, s2) 220 | } 221 | } 222 | -------------------------------------------------------------------------------- /src/sea_of_nodes/types/type_tuple.rs: -------------------------------------------------------------------------------- 1 | use crate::sea_of_nodes::types::Ty; 2 | 3 | pub type Tuple<'t> = &'t [Ty<'t>]; 4 | --------------------------------------------------------------------------------