region-based memory management in a c-like form
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: start thinking about MIR analysis passes

+912 -259
+3
.gitignore
··· 4 4 *.c 5 5 *.ll 6 6 *.s 7 + *.bc 8 + *.dot 9 + *.png
+347 -3
Cargo.lock
··· 63 63 version = "0.1.0" 64 64 65 65 [[package]] 66 + name = "bitflags" 67 + version = "2.10.0" 68 + source = "registry+https://github.com/rust-lang/crates.io-index" 69 + checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" 70 + 71 + [[package]] 72 + name = "block-buffer" 73 + version = "0.10.4" 74 + source = "registry+https://github.com/rust-lang/crates.io-index" 75 + checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" 76 + dependencies = [ 77 + "generic-array", 78 + ] 79 + 80 + [[package]] 66 81 name = "cc" 67 82 version = "1.2.51" 68 83 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 71 86 "find-msvc-tools", 72 87 "shlex", 73 88 ] 89 + 90 + [[package]] 91 + name = "cfg-if" 92 + version = "1.0.4" 93 + source = "registry+https://github.com/rust-lang/crates.io-index" 94 + checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" 74 95 75 96 [[package]] 76 97 name = "clap" ··· 103 124 "heck", 104 125 "proc-macro2", 105 126 "quote", 106 - "syn", 127 + "syn 2.0.111", 107 128 ] 108 129 109 130 [[package]] ··· 139 160 checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" 140 161 141 162 [[package]] 163 + name = "cpufeatures" 164 + version = "0.2.17" 165 + source = "registry+https://github.com/rust-lang/crates.io-index" 166 + checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" 167 + dependencies = [ 168 + "libc", 169 + ] 170 + 171 + [[package]] 172 + name = "crypto-common" 173 + version = "0.1.7" 174 + source = "registry+https://github.com/rust-lang/crates.io-index" 175 + checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" 176 + dependencies = [ 177 + "generic-array", 178 + "typenum", 179 + ] 180 + 181 + [[package]] 182 + name = "digest" 183 + version = "0.10.7" 184 + source = "registry+https://github.com/rust-lang/crates.io-index" 185 + checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" 186 + dependencies = [ 187 + "block-buffer", 188 + "crypto-common", 189 + ] 190 + 191 + [[package]] 192 + name = "dot-generator" 193 + version = "0.2.0" 194 + source = "registry+https://github.com/rust-lang/crates.io-index" 195 + checksum = "0aaac7ada45f71873ebce336491d1c1bc4a7c8042c7cea978168ad59e805b871" 196 + dependencies = [ 197 + "dot-structures", 198 + ] 199 + 200 + [[package]] 201 + name = "dot-structures" 202 + version = "0.1.2" 203 + source = "registry+https://github.com/rust-lang/crates.io-index" 204 + checksum = "498cfcded997a93eb31edd639361fa33fd229a8784e953b37d71035fe3890b7b" 205 + 206 + [[package]] 207 + name = "errno" 208 + version = "0.3.14" 209 + source = "registry+https://github.com/rust-lang/crates.io-index" 210 + checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" 211 + dependencies = [ 212 + "libc", 213 + "windows-sys", 214 + ] 215 + 216 + [[package]] 217 + name = "fastrand" 218 + version = "2.3.0" 219 + source = "registry+https://github.com/rust-lang/crates.io-index" 220 + checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" 221 + 222 + [[package]] 142 223 name = "find-msvc-tools" 143 224 version = "0.1.6" 144 225 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 147 228 [[package]] 148 229 name = "frontend" 149 230 version = "0.1.0" 231 + dependencies = [ 232 + "graphviz-rust", 233 + ] 234 + 235 + [[package]] 236 + name = "generic-array" 237 + version = "0.14.7" 238 + source = "registry+https://github.com/rust-lang/crates.io-index" 239 + checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" 240 + dependencies = [ 241 + "typenum", 242 + "version_check", 243 + ] 244 + 245 + [[package]] 246 + name = "getrandom" 247 + version = "0.3.4" 248 + source = "registry+https://github.com/rust-lang/crates.io-index" 249 + checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" 250 + dependencies = [ 251 + "cfg-if", 252 + "libc", 253 + "r-efi", 254 + "wasip2", 255 + ] 256 + 257 + [[package]] 258 + name = "graphviz-rust" 259 + version = "0.9.6" 260 + source = "registry+https://github.com/rust-lang/crates.io-index" 261 + checksum = "db134cb611668917cabf340af9a39518426f9a10827b4cedcb4cdcf84443f6d0" 262 + dependencies = [ 263 + "dot-generator", 264 + "dot-structures", 265 + "into-attr", 266 + "into-attr-derive", 267 + "pest", 268 + "pest_derive", 269 + "rand", 270 + "tempfile", 271 + ] 150 272 151 273 [[package]] 152 274 name = "heck" ··· 175 297 dependencies = [ 176 298 "proc-macro2", 177 299 "quote", 178 - "syn", 300 + "syn 2.0.111", 301 + ] 302 + 303 + [[package]] 304 + name = "into-attr" 305 + version = "0.1.1" 306 + source = "registry+https://github.com/rust-lang/crates.io-index" 307 + checksum = "18b48c537e49a709e678caec3753a7dba6854661a1eaa27675024283b3f8b376" 308 + dependencies = [ 309 + "dot-structures", 310 + ] 311 + 312 + [[package]] 313 + name = "into-attr-derive" 314 + version = "0.2.1" 315 + source = "registry+https://github.com/rust-lang/crates.io-index" 316 + checksum = "ecac7c1ae6cd2c6a3a64d1061a8bdc7f52ff62c26a831a2301e54c1b5d70d5b1" 317 + dependencies = [ 318 + "dot-generator", 319 + "dot-structures", 320 + "into-attr", 321 + "quote", 322 + "syn 1.0.109", 179 323 ] 180 324 181 325 [[package]] ··· 195 339 version = "0.2.178" 196 340 source = "registry+https://github.com/rust-lang/crates.io-index" 197 341 checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091" 342 + 343 + [[package]] 344 + name = "linux-raw-sys" 345 + version = "0.11.0" 346 + source = "registry+https://github.com/rust-lang/crates.io-index" 347 + checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" 198 348 199 349 [[package]] 200 350 name = "llvm-sys" ··· 211 361 ] 212 362 213 363 [[package]] 364 + name = "memchr" 365 + version = "2.7.6" 366 + source = "registry+https://github.com/rust-lang/crates.io-index" 367 + checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" 368 + 369 + [[package]] 214 370 name = "once_cell" 215 371 version = "1.21.3" 216 372 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 223 379 checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" 224 380 225 381 [[package]] 382 + name = "pest" 383 + version = "2.8.4" 384 + source = "registry+https://github.com/rust-lang/crates.io-index" 385 + checksum = "cbcfd20a6d4eeba40179f05735784ad32bdaef05ce8e8af05f180d45bb3e7e22" 386 + dependencies = [ 387 + "memchr", 388 + "ucd-trie", 389 + ] 390 + 391 + [[package]] 392 + name = "pest_derive" 393 + version = "2.8.4" 394 + source = "registry+https://github.com/rust-lang/crates.io-index" 395 + checksum = "51f72981ade67b1ca6adc26ec221be9f463f2b5839c7508998daa17c23d94d7f" 396 + dependencies = [ 397 + "pest", 398 + "pest_generator", 399 + ] 400 + 401 + [[package]] 402 + name = "pest_generator" 403 + version = "2.8.4" 404 + source = "registry+https://github.com/rust-lang/crates.io-index" 405 + checksum = "dee9efd8cdb50d719a80088b76f81aec7c41ed6d522ee750178f83883d271625" 406 + dependencies = [ 407 + "pest", 408 + "pest_meta", 409 + "proc-macro2", 410 + "quote", 411 + "syn 2.0.111", 412 + ] 413 + 414 + [[package]] 415 + name = "pest_meta" 416 + version = "2.8.4" 417 + source = "registry+https://github.com/rust-lang/crates.io-index" 418 + checksum = "bf1d70880e76bdc13ba52eafa6239ce793d85c8e43896507e43dd8984ff05b82" 419 + dependencies = [ 420 + "pest", 421 + "sha2", 422 + ] 423 + 424 + [[package]] 425 + name = "ppv-lite86" 426 + version = "0.2.21" 427 + source = "registry+https://github.com/rust-lang/crates.io-index" 428 + checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" 429 + dependencies = [ 430 + "zerocopy", 431 + ] 432 + 433 + [[package]] 226 434 name = "proc-macro2" 227 435 version = "1.0.103" 228 436 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 241 449 ] 242 450 243 451 [[package]] 452 + name = "r-efi" 453 + version = "5.3.0" 454 + source = "registry+https://github.com/rust-lang/crates.io-index" 455 + checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" 456 + 457 + [[package]] 458 + name = "rand" 459 + version = "0.9.2" 460 + source = "registry+https://github.com/rust-lang/crates.io-index" 461 + checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" 462 + dependencies = [ 463 + "rand_chacha", 464 + "rand_core", 465 + ] 466 + 467 + [[package]] 468 + name = "rand_chacha" 469 + version = "0.9.0" 470 + source = "registry+https://github.com/rust-lang/crates.io-index" 471 + checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" 472 + dependencies = [ 473 + "ppv-lite86", 474 + "rand_core", 475 + ] 476 + 477 + [[package]] 478 + name = "rand_core" 479 + version = "0.9.3" 480 + source = "registry+https://github.com/rust-lang/crates.io-index" 481 + checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" 482 + dependencies = [ 483 + "getrandom", 484 + ] 485 + 486 + [[package]] 244 487 name = "regex-lite" 245 488 version = "0.1.8" 246 489 source = "registry+https://github.com/rust-lang/crates.io-index" 247 490 checksum = "8d942b98df5e658f56f20d592c7f868833fe38115e65c33003d8cd224b0155da" 248 491 249 492 [[package]] 493 + name = "rustix" 494 + version = "1.1.3" 495 + source = "registry+https://github.com/rust-lang/crates.io-index" 496 + checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34" 497 + dependencies = [ 498 + "bitflags", 499 + "errno", 500 + "libc", 501 + "linux-raw-sys", 502 + "windows-sys", 503 + ] 504 + 505 + [[package]] 250 506 name = "semver" 251 507 version = "1.0.27" 252 508 source = "registry+https://github.com/rust-lang/crates.io-index" 253 509 checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" 254 510 255 511 [[package]] 512 + name = "sha2" 513 + version = "0.10.9" 514 + source = "registry+https://github.com/rust-lang/crates.io-index" 515 + checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" 516 + dependencies = [ 517 + "cfg-if", 518 + "cpufeatures", 519 + "digest", 520 + ] 521 + 522 + [[package]] 256 523 name = "shlex" 257 524 version = "1.3.0" 258 525 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 266 533 267 534 [[package]] 268 535 name = "syn" 536 + version = "1.0.109" 537 + source = "registry+https://github.com/rust-lang/crates.io-index" 538 + checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" 539 + dependencies = [ 540 + "proc-macro2", 541 + "quote", 542 + "unicode-ident", 543 + ] 544 + 545 + [[package]] 546 + name = "syn" 269 547 version = "2.0.111" 270 548 source = "registry+https://github.com/rust-lang/crates.io-index" 271 549 checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" ··· 276 554 ] 277 555 278 556 [[package]] 557 + name = "tempfile" 558 + version = "3.24.0" 559 + source = "registry+https://github.com/rust-lang/crates.io-index" 560 + checksum = "655da9c7eb6305c55742045d5a8d2037996d61d8de95806335c7c86ce0f82e9c" 561 + dependencies = [ 562 + "fastrand", 563 + "getrandom", 564 + "once_cell", 565 + "rustix", 566 + "windows-sys", 567 + ] 568 + 569 + [[package]] 279 570 name = "thiserror" 280 571 version = "2.0.17" 281 572 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 292 583 dependencies = [ 293 584 "proc-macro2", 294 585 "quote", 295 - "syn", 586 + "syn 2.0.111", 296 587 ] 297 588 298 589 [[package]] 590 + name = "typenum" 591 + version = "1.19.0" 592 + source = "registry+https://github.com/rust-lang/crates.io-index" 593 + checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" 594 + 595 + [[package]] 596 + name = "ucd-trie" 597 + version = "0.1.7" 598 + source = "registry+https://github.com/rust-lang/crates.io-index" 599 + checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" 600 + 601 + [[package]] 299 602 name = "unicode-ident" 300 603 version = "1.0.22" 301 604 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 308 611 checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" 309 612 310 613 [[package]] 614 + name = "version_check" 615 + version = "0.9.5" 616 + source = "registry+https://github.com/rust-lang/crates.io-index" 617 + checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" 618 + 619 + [[package]] 620 + name = "wasip2" 621 + version = "1.0.1+wasi-0.2.4" 622 + source = "registry+https://github.com/rust-lang/crates.io-index" 623 + checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" 624 + dependencies = [ 625 + "wit-bindgen", 626 + ] 627 + 628 + [[package]] 311 629 name = "windows-link" 312 630 version = "0.2.1" 313 631 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 321 639 dependencies = [ 322 640 "windows-link", 323 641 ] 642 + 643 + [[package]] 644 + name = "wit-bindgen" 645 + version = "0.46.0" 646 + source = "registry+https://github.com/rust-lang/crates.io-index" 647 + checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" 648 + 649 + [[package]] 650 + name = "zerocopy" 651 + version = "0.8.31" 652 + source = "registry+https://github.com/rust-lang/crates.io-index" 653 + checksum = "fd74ec98b9250adb3ca554bdde269adf631549f51d8a8f8f0a10b50f1cb298c3" 654 + dependencies = [ 655 + "zerocopy-derive", 656 + ] 657 + 658 + [[package]] 659 + name = "zerocopy-derive" 660 + version = "0.8.31" 661 + source = "registry+https://github.com/rust-lang/crates.io-index" 662 + checksum = "d8a8d209fdf45cf5138cbb5a506f6b52522a25afccc534d1475dad8e31105c6a" 663 + dependencies = [ 664 + "proc-macro2", 665 + "quote", 666 + "syn 2.0.111", 667 + ]
+3 -6
cli/src/main.rs
··· 1 1 use clap::Parser; 2 2 use codegen::{CodegenBackend, codegen}; 3 3 use frontend::do_frontend; 4 - use frontend::visualize::MIRVisualizer; 5 4 6 5 #[derive(Parser, Debug)] 7 6 #[command(author, version)] ··· 11 10 backend: CodegenBackend, 12 11 #[arg(short, long)] 13 12 verbose: bool, 13 + #[arg(short, long)] 14 + mir: bool 14 15 } 15 16 16 17 fn main() { 17 18 let options = Options::parse(); 18 - let (module, ctx) = match do_frontend(&options.file) { 19 + let (module, ctx) = match do_frontend(&options.file, options.mir) { 19 20 Ok(result) => result, 20 21 Err(e) => { 21 22 eprintln!("{}", e); 22 23 std::process::exit(1); 23 24 } 24 25 }; 25 - 26 - if options.verbose { 27 - module.visualize(0); 28 - } 29 26 30 27 codegen(CodegenBackend::LLVM, module, ctx, options.verbose).unwrap(); 31 28 }
+1
frontend/Cargo.toml
··· 4 4 edition = "2024" 5 5 6 6 [dependencies] 7 + graphviz-rust = "0.9.6"
+7 -5
frontend/src/lib.rs
··· 9 9 mod typecheck; 10 10 11 11 pub use ctx::{Ctx, Symbol}; 12 - pub use mir::{Function, visualize}; 13 - pub use span::{Diagnostic, DUMMY_SPAN, SourceMap, Span, SpanExt, Spanned}; 14 12 pub use lex::BinOp; 13 + pub use mir::{Function, visualize}; 14 + pub use span::{Diagnostic, SourceMap, Span, SpanExt, Spanned}; 15 15 16 16 use ast_visitor::{AstToMIR, AstVisitor}; 17 17 use std::collections::VecDeque; 18 18 use std::fs::File; 19 19 use std::io::Read; 20 20 21 - pub fn do_frontend(file: &str) -> Result<(mir::Module, Ctx), String> { 21 + pub fn do_frontend(file: &str, output_mir: bool) -> Result<(mir::Module, Ctx), String> { 22 22 let mut file = File::open(file).map_err(|e| format!("failed to open file: {}", e))?; 23 23 24 24 let mut contents = String::new(); 25 - file.read_to_string(&mut contents).map_err(|e| format!("failed to read file: {}", e))?; 25 + file.read_to_string(&mut contents) 26 + .map_err(|e| format!("failed to read file: {}", e))?; 26 27 contents = contents.trim().to_string(); 27 28 28 29 let source_map = SourceMap::new(contents.clone()); ··· 42 43 let mut ast_visitor = AstToMIR::new(&ctx); 43 44 ast_visitor.visit_module(module); 44 45 45 - Ok((ast_visitor.produce_module(), ctx)) 46 + let module = mir::run_passes(ast_visitor.produce_module(), output_mir); 47 + Ok((module, ctx)) 46 48 }
+123
frontend/src/mir/domtree.rs
··· 1 + use std::collections::HashMap; 2 + 3 + pub trait Searchable<V: Clone> { 4 + type NodeId: Clone + Eq + std::hash::Hash; 5 + 6 + /// Required methods: 7 + /// Depth-first search traversal of the graph starting from the given node, applying the visit 8 + /// function to each node 9 + fn dfs<F>(&self, start: Self::NodeId, visit: &mut F) 10 + where 11 + F: FnMut(Self::NodeId); 12 + 13 + /// Returns the nodes in the graph, order is not guaranteed 14 + fn nodes(&self) -> Vec<Self::NodeId>; 15 + 16 + /// Derived method: 17 + /// Collects the nodes in post order starting from the given node 18 + fn post_order_collect(&self, start: Self::NodeId) -> Vec<Self::NodeId> { 19 + let mut visited = Vec::new(); 20 + self.dfs(start, &mut |id| { 21 + visited.push(id.clone()); 22 + }); 23 + visited 24 + } 25 + } 26 + 27 + pub trait DomTree<V>: Searchable<V> 28 + where 29 + V: Clone + Eq + std::hash::Hash, 30 + { 31 + /// Required methods: 32 + /// 33 + /// Returns the dominator of a node in the dominator tree 34 + fn dom(&self, start: Self::NodeId) -> Option<Self::NodeId>; 35 + /// Sets the dominator of a node in the dominator tree 36 + fn set_dom(&mut self, node: Self::NodeId, dom: Option<Self::NodeId>); 37 + /// Clears the dominator tree, which is a precondition for computing the dominator tree 38 + fn reset_dom(&mut self); 39 + /// Returns the predecessors of a node in the dominator tree 40 + fn preds(&self, node: Self::NodeId) -> Vec<Self::NodeId>; 41 + 42 + /// Derived methods: 43 + /// Helper method to compute the dominance intersection of two nodes 44 + fn intersect( 45 + &self, 46 + order_map: &HashMap<Self::NodeId, usize>, 47 + mut node1: Self::NodeId, 48 + mut node2: Self::NodeId, 49 + ) -> Self::NodeId { 50 + while node1 != node2 { 51 + if order_map[&node1] > order_map[&node2] { 52 + node1 = self 53 + .dom(node1.clone()) 54 + .expect("Every node must eventually have a dominator"); 55 + } else { 56 + node2 = self 57 + .dom(node2.clone()) 58 + .expect("Every node must eventually have a dominator"); 59 + } 60 + } 61 + node1 62 + } 63 + 64 + /// Computes the dominator tree starting from the given node 65 + /// https://en.wikipedia.org/wiki/Dominator_(graph_theory)#Algorithms 66 + fn compute_dom_tree(&mut self, start: Self::NodeId) { 67 + self.reset_dom(); 68 + self.set_dom(start.clone(), Some(start.clone())); 69 + 70 + let mut order: Vec<Self::NodeId> = self.post_order_collect(start); 71 + order.reverse(); 72 + 73 + let order_map: HashMap<Self::NodeId, usize> = order 74 + .iter() 75 + .enumerate() 76 + .map(|(i, id)| (id.clone(), i)) 77 + .collect(); 78 + 79 + let mut changed = true; 80 + while changed { 81 + changed = false; 82 + 83 + // for each n in N - {n0}: 84 + for node in order.iter().skip(1) { 85 + let preds = &self.preds(node.clone()); 86 + 87 + let mut new_dominator_opt = None; 88 + for pred in preds { 89 + if self.dom(pred.clone()).is_some() { 90 + new_dominator_opt = Some(pred.clone()); 91 + break; 92 + } 93 + } 94 + let mut new_dominator = match new_dominator_opt { 95 + Some(n) => n, 96 + None => continue, // if no predecessor has a computed dominator, skip 97 + }; 98 + 99 + for pred in preds { 100 + if self.dom(pred.clone()).is_some() { 101 + new_dominator = self.intersect(&order_map, new_dominator, pred.clone()); 102 + } 103 + } 104 + 105 + if self.dom(node.clone()) != Some(new_dominator.clone()) { 106 + self.set_dom(node.clone(), Some(new_dominator)); 107 + changed = true; 108 + } 109 + } 110 + } 111 + } 112 + 113 + /// Returns the dominator tree as a HashMap 114 + fn dom_tree(&self) -> HashMap<Self::NodeId, Self::NodeId> { 115 + let mut dom_tree = HashMap::new(); 116 + for node in self.nodes() { 117 + if let Some(dom) = self.dom(node.clone()) { 118 + dom_tree.insert(node.clone(), dom); 119 + } 120 + } 121 + dom_tree 122 + } 123 + }
+148
frontend/src/mir/graph.rs
··· 1 + use std::collections::{HashMap, HashSet}; 2 + 3 + use graphviz_rust::{ 4 + dot_generator::*, 5 + dot_structures::*, 6 + printer::{DotPrinter, PrinterContext}, 7 + }; 8 + 9 + use super::visit::visit_block_succs; 10 + use crate::mir::{ 11 + domtree::{DomTree, Searchable}, 12 + *, 13 + }; 14 + 15 + #[derive(Debug, Default)] 16 + pub struct FlowNode { 17 + predecessors: HashSet<BlockId>, 18 + successors: HashSet<BlockId>, 19 + } 20 + 21 + pub struct FlowGraph { 22 + blocks: HashMap<BlockId, FlowNode>, 23 + entry: Option<BlockId>, 24 + dominators: HashMap<BlockId, Option<BlockId>>, 25 + } 26 + 27 + impl FlowGraph { 28 + pub fn new() -> Self { 29 + Self { 30 + blocks: HashMap::new(), 31 + entry: None, 32 + dominators: HashMap::new(), 33 + } 34 + } 35 + 36 + pub fn compute(&mut self, func: &Function) { 37 + self.blocks.clear(); 38 + self.entry = None; 39 + 40 + if !func.blocks.is_empty() { 41 + self.entry = Some(func.blocks[0].block_id); 42 + } 43 + 44 + for block in func.blocks.iter() { 45 + self.blocks.entry(block.block_id).or_default(); 46 + } 47 + 48 + for block in func.blocks.iter() { 49 + visit_block_succs(func, block, |to| { 50 + self.add_edge(block.block_id, to); 51 + }); 52 + } 53 + 54 + if let Some(first_block) = func.blocks.first() { 55 + self.entry = Some(first_block.block_id); 56 + } 57 + } 58 + 59 + fn add_edge(&mut self, from: BlockId, to: BlockId) { 60 + self.blocks.entry(from).or_default().successors.insert(to); 61 + self.blocks.entry(to).or_default().predecessors.insert(from); 62 + } 63 + } 64 + 65 + impl Searchable<BlockId> for FlowGraph { 66 + type NodeId = BlockId; 67 + 68 + fn dfs<F>(&self, start: Self::NodeId, visit: &mut F) 69 + where 70 + F: FnMut(Self::NodeId), 71 + { 72 + let mut visited = HashSet::new(); 73 + let mut stack = vec![start]; 74 + 75 + while let Some(node) = stack.pop() { 76 + if !visited.contains(&node) { 77 + visited.insert(node); 78 + visit(node); 79 + stack.extend(self.blocks[&node].successors.iter()); 80 + } 81 + } 82 + } 83 + 84 + fn nodes(&self) -> Vec<Self::NodeId> { 85 + self.blocks.keys().cloned().collect() 86 + } 87 + } 88 + 89 + impl DomTree<BlockId> for FlowGraph { 90 + fn dom(&self, start: Self::NodeId) -> Option<Self::NodeId> { 91 + self.dominators.get(&start).unwrap_or(&None).clone() 92 + } 93 + 94 + fn set_dom(&mut self, node: Self::NodeId, dom: Option<Self::NodeId>) { 95 + self.dominators.insert(node, dom); 96 + } 97 + 98 + fn reset_dom(&mut self) { 99 + self.dominators.iter_mut().for_each(|(_, dom)| *dom = None); 100 + } 101 + 102 + fn preds(&self, node: Self::NodeId) -> Vec<Self::NodeId> { 103 + self.blocks 104 + .get(&node) 105 + .unwrap() 106 + .predecessors 107 + .iter() 108 + .copied() 109 + .collect() 110 + } 111 + } 112 + 113 + // Graphviz functions 114 + impl FlowGraph { 115 + pub fn to_dot(&self) -> Graph { 116 + let mut stmts: Vec<Stmt> = vec![ 117 + stmt!(attr!("rankdir", "TB")), 118 + stmt!(attr!("fontname", esc "Helvetica")), 119 + ]; 120 + 121 + if let Some(entry) = self.entry { 122 + stmts.push(stmt!( 123 + node!(esc format!("bb{}", entry); attr!("style", "filled"), attr!("fillcolor", "lightgreen")) 124 + )); 125 + } 126 + 127 + for block_id in self.blocks.keys() { 128 + let label = format!("BB{}", block_id); 129 + stmts.push(stmt!( 130 + node!(esc format!("bb{}", block_id); attr!("label", esc label)) 131 + )); 132 + } 133 + 134 + for (from, flow_node) in &self.blocks { 135 + for to in &flow_node.successors { 136 + stmts.push(stmt!( 137 + edge!(node_id!(esc format!("bb{}", from)) => node_id!(esc format!("bb{}", to))) 138 + )); 139 + } 140 + } 141 + 142 + graph!(strict di id!("FlowGraph"), stmts) 143 + } 144 + 145 + pub fn to_dot_string(&self) -> String { 146 + self.to_dot().print(&mut PrinterContext::default()) 147 + } 148 + }
+250
frontend/src/mir/ir.rs
··· 1 + use std::fmt::Display; 2 + 3 + use crate::lex::BinOp; 4 + 5 + // corresponds to locals in the defining function 6 + pub type LocalId = usize; 7 + pub type BlockId = usize; 8 + 9 + #[derive(Debug, PartialEq, Eq, Clone)] 10 + pub enum Ty { 11 + Bool, 12 + Char, 13 + Int, 14 + Unit, 15 + Array(Box<Ty>, usize), 16 + DynArray(Box<Ty>), 17 + Tuple(Vec<Ty>), 18 + Ptr(Box<Ty>), 19 + TaggedUnion(Vec<(u8, Ty)>), 20 + Record(Vec<Ty>), 21 + } 22 + 23 + impl Ty { 24 + pub fn bytes(&self) -> usize { 25 + match self { 26 + Ty::Bool => 1, 27 + Ty::Char => 1, 28 + Ty::Int => 4, 29 + Ty::Unit => 0, 30 + Ty::Array(ty, len) => ty.bytes() * *len, 31 + Ty::DynArray(ty) => ty.bytes(), 32 + Ty::Tuple(tys) => tys.iter().map(|t| t.bytes()).sum(), 33 + Ty::Ptr(_) => 4, 34 + // this just returns the size of the largest field, not including the tag byte 35 + Ty::TaggedUnion(tags_tys) => tags_tys.iter().map(|(tag, ty)| ty.bytes()).max().unwrap(), 36 + Ty::Record(tys) => tys.iter().map(|t| t.bytes()).sum(), 37 + } 38 + } 39 + } 40 + 41 + impl Display for Ty { 42 + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 43 + match self { 44 + Ty::Bool => write!(f, "bool"), 45 + Ty::Char => write!(f, "char"), 46 + Ty::Int => write!(f, "int"), 47 + Ty::Unit => write!(f, "unit"), 48 + Ty::Array(ty, len) => write!(f, "[{}]{{ {} }}", ty, len), 49 + Ty::DynArray(ty) => write!(f, "dyn{{ {} }}", ty), 50 + Ty::Tuple(tys) => write!( 51 + f, 52 + "({})", 53 + tys.iter() 54 + .map(|t| t.to_string()) 55 + .collect::<Vec<String>>() 56 + .join(", ") 57 + ), 58 + Ty::Ptr(ty) => write!(f, "^{}", ty), 59 + Ty::TaggedUnion(tags_tys) => { 60 + let tags_tys_str = tags_tys 61 + .iter() 62 + .map(|(tag, ty)| format!("{}:<{}>", tag, ty)) 63 + .collect::<Vec<String>>() 64 + .join("|"); 65 + 66 + write!(f, "{}", tags_tys_str) 67 + } 68 + 69 + Ty::Record(tys) => write!( 70 + f, 71 + "{{ {} }}", 72 + tys.iter() 73 + .map(|t| t.to_string()) 74 + .collect::<Vec<String>>() 75 + .join(", ") 76 + ), 77 + } 78 + } 79 + } 80 + 81 + #[derive(Debug, PartialEq, Eq, Clone)] 82 + pub enum PlaceKind { 83 + /// array index operations 84 + Index(LocalId), 85 + /// field idx, and a type for validation 86 + Field(usize, Ty), 87 + Deref, 88 + } 89 + 90 + #[derive(Debug, PartialEq, Eq, Clone)] 91 + // places are memory locations 92 + pub struct Place { 93 + pub local: LocalId, 94 + // we use the place kind to determine where we are looking in the local 95 + // for example, if we are looking at a field, we need to know the field index 96 + // if we are looking at an array index, we need to know the array index 97 + pub kind: PlaceKind, 98 + } 99 + 100 + impl Place { 101 + pub fn new(local: LocalId, kind: PlaceKind) -> Self { 102 + Self { local, kind } 103 + } 104 + } 105 + 106 + #[derive(Debug, PartialEq, Eq)] 107 + pub enum Operand { 108 + Constant(i32), 109 + Copy(Place), 110 + } 111 + 112 + #[derive(Debug, PartialEq, Eq, Clone)] 113 + pub enum AllocKind { 114 + Array(Ty), 115 + DynArray(Ty), 116 + Tuple(Vec<Ty>), 117 + Record(Vec<Ty>), 118 + Variant(u8, Ty), 119 + } 120 + 121 + impl Display for AllocKind { 122 + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 123 + match self { 124 + AllocKind::Array(ty) => write!(f, "array<{}>", ty), 125 + AllocKind::Tuple(tys) => { 126 + let ty_str = tys 127 + .iter() 128 + .map(|t| t.to_string()) 129 + .collect::<Vec<String>>() 130 + .join(" * "); 131 + 132 + write!(f, "{}", ty_str) 133 + } 134 + AllocKind::DynArray(ty) => write!(f, "dyn_array<{}>", ty), 135 + AllocKind::Record(tys) => { 136 + let ty_str = tys 137 + .iter() 138 + .map(|t| t.to_string()) 139 + .collect::<Vec<String>>() 140 + .join(","); 141 + 142 + write!(f, "{{{}}}", ty_str) 143 + } 144 + AllocKind::Variant(tag, ty) => write!(f, "Variant({})<{}>", tag, ty), 145 + } 146 + } 147 + } 148 + 149 + #[derive(Debug, PartialEq, Eq)] 150 + pub enum RValue { 151 + Use(Operand), 152 + BinOp(BinOp, Operand, Operand), 153 + Alloc(AllocKind, Vec<Operand>), 154 + } 155 + 156 + impl RValue { 157 + pub fn place(&self) -> Option<Place> { 158 + match self { 159 + RValue::Use(op) => match op { 160 + Operand::Constant(_) => None, 161 + Operand::Copy(p) => Some(p.clone()), 162 + }, 163 + _ => None, 164 + } 165 + } 166 + } 167 + 168 + #[derive(Debug, PartialEq, Eq)] 169 + pub enum Statement { 170 + // This is in SSA form, so assigning defines a new local 171 + Assign(LocalId, RValue), 172 + Phi(LocalId, Vec<LocalId>), 173 + } 174 + 175 + #[derive(Debug, PartialEq, Eq)] 176 + pub enum Terminator { 177 + Return, 178 + Br(BlockId), 179 + // usize is index of the local for the condition 180 + BrIf(usize, BlockId, BlockId), 181 + Call { 182 + function_name: String, 183 + args: Vec<RValue>, 184 + destination: Option<LocalId>, 185 + target: BlockId, 186 + }, 187 + } 188 + 189 + #[derive(Debug, PartialEq, Eq)] 190 + pub struct BasicBlock { 191 + pub block_id: BlockId, 192 + pub stmts: Vec<Statement>, 193 + // TODO: should this possibly be an optional at some points 194 + pub terminator: Terminator, 195 + } 196 + 197 + impl BasicBlock { 198 + pub fn new(block_id: BlockId) -> Self { 199 + Self { 200 + block_id, 201 + stmts: Vec::new(), 202 + terminator: Terminator::Return, 203 + } 204 + } 205 + } 206 + 207 + #[derive(Debug, PartialEq, Eq)] 208 + pub struct Local { 209 + pub id: LocalId, 210 + pub ty: Ty, 211 + } 212 + 213 + impl Local { 214 + pub fn new(local_id: LocalId, ty: Ty) -> Self { 215 + Self { id: local_id, ty } 216 + } 217 + } 218 + 219 + #[derive(Debug, PartialEq, Eq)] 220 + pub struct Function { 221 + pub name: String, 222 + pub blocks: Vec<BasicBlock>, 223 + pub parameters: usize, 224 + pub return_ty: Ty, 225 + pub locals: Vec<Local>, 226 + } 227 + 228 + impl IntoIterator for Function { 229 + type Item = BasicBlock; 230 + type IntoIter = std::vec::IntoIter<BasicBlock>; 231 + 232 + // TOOD: make this a proper CFG traversal 233 + fn into_iter(self) -> Self::IntoIter { 234 + self.blocks.into_iter() 235 + } 236 + } 237 + 238 + #[derive(Debug, PartialEq, Eq)] 239 + pub struct Extern { 240 + pub name: String, 241 + pub params: Vec<Ty>, 242 + pub return_ty: Ty, 243 + } 244 + 245 + #[derive(Debug, PartialEq, Eq)] 246 + pub struct Module { 247 + pub functions: Vec<Function>, 248 + pub constants: Vec<RValue>, 249 + pub externs: Vec<Extern>, 250 + }
+13 -245
frontend/src/mir/mod.rs
··· 1 - #![allow(unused)] 2 - 3 - pub mod visualize; 4 1 mod graph; 2 + pub mod ir; 5 3 mod visit; 6 - 7 - use std::fmt::Display; 8 - 9 - use crate::lex::BinOp; 10 - 11 - // corresponds to locals in the defining function 12 - pub type LocalId = usize; 13 - pub type BlockId = usize; 14 - 15 - #[derive(Debug, PartialEq, Eq, Clone)] 16 - pub enum Ty { 17 - Bool, 18 - Char, 19 - Int, 20 - Unit, 21 - Array(Box<Ty>, usize), 22 - DynArray(Box<Ty>), 23 - Tuple(Vec<Ty>), 24 - Ptr(Box<Ty>), 25 - TaggedUnion(Vec<(u8, Ty)>), 26 - Record(Vec<Ty>), 27 - } 28 - 29 - impl Ty { 30 - pub fn bytes(&self) -> usize { 31 - match self { 32 - Ty::Bool => 1, 33 - Ty::Char => 1, 34 - Ty::Int => 4, 35 - Ty::Unit => 0, 36 - Ty::Array(ty, len) => ty.bytes() * *len, 37 - Ty::DynArray(ty) => ty.bytes(), 38 - Ty::Tuple(tys) => tys.iter().map(|t| t.bytes()).sum(), 39 - Ty::Ptr(_) => 4, 40 - // this just returns the size of the largest field, not including the tag byte 41 - Ty::TaggedUnion(tags_tys) => tags_tys.iter().map(|(tag, ty)| ty.bytes()).max().unwrap(), 42 - Ty::Record(tys) => tys.iter().map(|t| t.bytes()).sum(), 43 - } 44 - } 45 - } 46 - 47 - impl Display for Ty { 48 - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 49 - match self { 50 - Ty::Bool => write!(f, "bool"), 51 - Ty::Char => write!(f, "char"), 52 - Ty::Int => write!(f, "int"), 53 - Ty::Unit => write!(f, "unit"), 54 - Ty::Array(ty, len) => write!(f, "[{}]{{ {} }}", ty, len), 55 - Ty::DynArray(ty) => write!(f, "dyn{{ {} }}", ty), 56 - Ty::Tuple(tys) => write!( 57 - f, 58 - "({})", 59 - tys.iter() 60 - .map(|t| t.to_string()) 61 - .collect::<Vec<String>>() 62 - .join(", ") 63 - ), 64 - Ty::Ptr(ty) => write!(f, "^{}", ty), 65 - Ty::TaggedUnion(tags_tys) => { 66 - let tags_tys_str = tags_tys 67 - .iter() 68 - .map(|(tag, ty)| format!("{}:<{}>", tag, ty)) 69 - .collect::<Vec<String>>() 70 - .join("|"); 71 - 72 - write!(f, "{}", tags_tys_str) 73 - } 74 - 75 - Ty::Record(tys) => write!( 76 - f, 77 - "{{ {} }}", 78 - tys.iter() 79 - .map(|t| t.to_string()) 80 - .collect::<Vec<String>>() 81 - .join(", ") 82 - ), 83 - } 84 - } 85 - } 86 - 87 - #[derive(Debug, PartialEq, Eq, Clone)] 88 - pub enum PlaceKind { 89 - /// array index operations 90 - Index(LocalId), 91 - /// field idx, and a type for validation 92 - Field(usize, Ty), 93 - Deref, 94 - } 95 - 96 - #[derive(Debug, PartialEq, Eq, Clone)] 97 - // places are memory locations 98 - pub struct Place { 99 - pub local: LocalId, 100 - // we use the place kind to determine where we are looking in the local 101 - // for example, if we are looking at a field, we need to know the field index 102 - // if we are looking at an array index, we need to know the array index 103 - pub kind: PlaceKind, 104 - } 105 - 106 - impl Place { 107 - pub fn new(local: LocalId, kind: PlaceKind) -> Self { 108 - Self { local, kind } 109 - } 110 - } 111 - 112 - #[derive(Debug, PartialEq, Eq)] 113 - pub enum Operand { 114 - Constant(i32), 115 - Copy(Place), 116 - } 117 - 118 - #[derive(Debug, PartialEq, Eq, Clone)] 119 - pub enum AllocKind { 120 - Array(Ty), 121 - DynArray(Ty), 122 - Tuple(Vec<Ty>), 123 - Record(Vec<Ty>), 124 - Variant(u8, Ty), 125 - } 126 - 127 - impl Display for AllocKind { 128 - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 129 - match self { 130 - AllocKind::Array(ty) => write!(f, "array<{}>", ty), 131 - AllocKind::Tuple(tys) => { 132 - let ty_str = tys 133 - .iter() 134 - .map(|t| t.to_string()) 135 - .collect::<Vec<String>>() 136 - .join(" * "); 4 + pub mod visualize; 5 + mod domtree; 137 6 138 - write!(f, "{}", ty_str) 139 - } 140 - AllocKind::DynArray(ty) => write!(f, "dyn_array<{}>", ty), 141 - AllocKind::Record(tys) => { 142 - let ty_str = tys 143 - .iter() 144 - .map(|t| t.to_string()) 145 - .collect::<Vec<String>>() 146 - .join(","); 7 + pub use ir::*; 147 8 148 - write!(f, "{{{}}}", ty_str) 149 - } 150 - AllocKind::Variant(tag, ty) => write!(f, "Variant({})<{}>", tag, ty), 151 - } 152 - } 153 - } 9 + #[allow(unused)] 10 + trait MirPass {} 154 11 155 - #[derive(Debug, PartialEq, Eq)] 156 - pub enum RValue { 157 - Use(Operand), 158 - BinOp(BinOp, Operand, Operand), 159 - Alloc(AllocKind, Vec<Operand>), 160 - } 12 + pub fn run_passes(module: Module, output_mir: bool) -> Module { 13 + let mut cfg = graph::FlowGraph::new(); 161 14 162 - impl RValue { 163 - pub fn place(&self) -> Option<Place> { 164 - match self { 165 - RValue::Use(op) => match op { 166 - Operand::Constant(_) => None, 167 - Operand::Copy(p) => Some(p.clone()), 168 - }, 169 - _ => None, 170 - } 171 - } 172 - } 173 - 174 - #[derive(Debug, PartialEq, Eq)] 175 - pub enum Statement { 176 - // This is in SSA form, so assigning defines a new local 177 - Assign(LocalId, RValue), 178 - Phi(LocalId, Vec<LocalId>), 179 - } 15 + for func in &module.functions { 16 + cfg.compute(func); 180 17 181 - #[derive(Debug, PartialEq, Eq)] 182 - pub enum Terminator { 183 - Return, 184 - Br(BlockId), 185 - // usize is index of the local for the condition 186 - BrIf(usize, BlockId, BlockId), 187 - Call { 188 - function_name: String, 189 - args: Vec<RValue>, 190 - destination: Option<LocalId>, 191 - target: BlockId, 192 - }, 193 - } 194 - 195 - #[derive(Debug, PartialEq, Eq)] 196 - pub struct BasicBlock { 197 - pub block_id: BlockId, 198 - pub stmts: Vec<Statement>, 199 - // TODO: should this possibly be an optional at some points 200 - pub terminator: Terminator, 201 - } 202 - 203 - impl BasicBlock { 204 - pub fn new(block_id: BlockId) -> Self { 205 - Self { 206 - block_id, 207 - stmts: Vec::new(), 208 - terminator: Terminator::Return, 18 + if output_mir { 19 + println!("{}", cfg.to_dot_string()); 209 20 } 210 21 } 211 - } 212 - 213 - #[derive(Debug, PartialEq, Eq)] 214 - pub struct Local { 215 - pub id: LocalId, 216 - pub ty: Ty, 217 - } 218 - 219 - impl Local { 220 - pub fn new(local_id: LocalId, ty: Ty) -> Self { 221 - Self { id: local_id, ty } 222 - } 223 - } 224 22 225 - #[derive(Debug, PartialEq, Eq)] 226 - pub struct Function { 227 - pub name: String, 228 - pub blocks: Vec<BasicBlock>, 229 - pub parameters: usize, 230 - pub return_ty: Ty, 231 - pub locals: Vec<Local>, 232 - } 233 - 234 - impl IntoIterator for Function { 235 - type Item = BasicBlock; 236 - type IntoIter = std::vec::IntoIter<BasicBlock>; 237 - 238 - // TOOD: make this a proper CFG traversal 239 - fn into_iter(self) -> Self::IntoIter { 240 - self.blocks.into_iter() 241 - } 242 - } 243 - 244 - #[derive(Debug, PartialEq, Eq)] 245 - pub struct Extern { 246 - pub name: String, 247 - pub params: Vec<Ty>, 248 - pub return_ty: Ty, 249 - } 250 - 251 - #[derive(Debug, PartialEq, Eq)] 252 - pub struct Module { 253 - pub functions: Vec<Function>, 254 - pub constants: Vec<RValue>, 255 - pub externs: Vec<Extern>, 23 + module 256 24 }
+17
frontend/src/mir/visit.rs
··· 1 + use crate::mir::*; 2 + 3 + pub fn visit_block_succs<F: FnMut(BlockId)>(_func: &Function, block: &BasicBlock, mut visit: F) { 4 + match &block.terminator { 5 + Terminator::Return => {} 6 + Terminator::Br(block_id) => { 7 + visit(*block_id); 8 + } 9 + Terminator::BrIf(_, then_, else_) => { 10 + visit(*then_); 11 + visit(*else_); 12 + } 13 + Terminator::Call { target, .. } => { 14 + visit(*target); 15 + } 16 + } 17 + }