├── .gdbinit ├── .gitignore ├── .vscode ├── launch.json └── settings.json ├── Cargo.lock ├── Cargo.toml ├── README ├── samples ├── 1.c ├── include │ ├── assert.h │ ├── ctype.h │ ├── errno.h │ ├── inttypes.h │ ├── limits.h │ ├── math.h │ ├── posix │ │ ├── _impl_pwd_systypes.h │ │ ├── _impl_unistd_systypes.h │ │ ├── pwd.h │ │ ├── regex.h │ │ ├── sys │ │ │ └── types.h │ │ └── unistd.h │ ├── setjmp.h │ ├── signal.h │ ├── stdarg.h │ ├── stdbool.h │ ├── stddef.h │ ├── stdint.h │ ├── stdio.h │ ├── stdlib.h │ └── string.h └── while_loop.c ├── src ├── ast │ ├── mod.rs │ └── pretty_print.rs ├── codegen.rs ├── codegen │ ├── cranelift.rs │ └── mrustc_mmir.rs ├── main.rs ├── parse │ ├── expr.rs │ ├── mod.rs │ ├── parsing.rs │ └── types.rs ├── preproc │ ├── lex.rs │ ├── mod.rs │ └── token.rs ├── typecheck.rs └── types.rs └── tools └── mmir_opt ├── Cargo.toml └── src ├── dump.rs ├── helper_types.rs ├── helper_types ├── bitmap.rs └── trie.rs ├── lexer.rs ├── logger.rs ├── main.rs ├── mir.rs ├── modtree.rs ├── optimise.rs ├── optimise └── const_propagate.rs ├── parser.rs ├── types.rs └── validate.rs /.gdbinit: -------------------------------------------------------------------------------- 1 | break rust_fail 2 | run 3 | 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | *.swo 3 | *.log 4 | /target/ 5 | /output/ 6 | /*.sh 7 | /[0-9].* -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "(gdb) Launch", 9 | "type": "cppdbg", 10 | "request": "launch", 11 | "program": "${workspaceRoot}/target/debug/rcc", 12 | "args": [ 13 | "samples/20230824.c", 14 | "-I", "samples/include/", 15 | "-o", "1.bin" 16 | ], 17 | "stopAtEntry": false, 18 | "cwd": "${workspaceRoot}", 19 | "environment": [ 20 | { "name": "RUST_LOG", "value": "rust_cc=debug" } 21 | ], 22 | "externalConsole": false, 23 | "MIMode": "gdb", 24 | "setupCommands": [ 25 | { 26 | "description": "Enable pretty-printing for gdb", 27 | "text": "-enable-pretty-printing", 28 | "ignoreFailures": true 29 | }, 30 | { 31 | "description": "Set Disassembly Flavor to Intel", 32 | "text": "-gdb-set disassembly-flavor intel", 33 | "ignoreFailures": true 34 | } 35 | ] 36 | } 37 | ] 38 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "rust-analyzer.linkedProjects": [ 3 | "./tools/mmir_opt/Cargo.toml" 4 | ] 5 | } -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "aho-corasick" 7 | version = "1.1.3" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" 10 | dependencies = [ 11 | "memchr", 12 | ] 13 | 14 | [[package]] 15 | name = "allocator-api2" 16 | version = "0.2.21" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" 19 | 20 | [[package]] 21 | name = "ansi_term" 22 | version = "0.12.1" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" 25 | dependencies = [ 26 | "winapi", 27 | ] 28 | 29 | [[package]] 30 | name = "anyhow" 31 | version = "1.0.95" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" 34 | 35 | [[package]] 36 | name = "arbitrary" 37 | version = "1.4.1" 38 | source = "registry+https://github.com/rust-lang/crates.io-index" 39 | checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" 40 | 41 | [[package]] 42 | name = "atty" 43 | version = "0.2.14" 44 | source = "registry+https://github.com/rust-lang/crates.io-index" 45 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 46 | dependencies = [ 47 | "hermit-abi", 48 | "libc", 49 | "winapi", 50 | ] 51 | 52 | [[package]] 53 | name = "bitflags" 54 | version = "1.3.2" 55 | source = "registry+https://github.com/rust-lang/crates.io-index" 56 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 57 | 58 | [[package]] 59 | name = "bumpalo" 60 | version = "3.16.0" 61 | source = "registry+https://github.com/rust-lang/crates.io-index" 62 | checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" 63 | dependencies = [ 64 | "allocator-api2", 65 | ] 66 | 67 | [[package]] 68 | name = "cfg-if" 69 | version = "1.0.0" 70 | source = "registry+https://github.com/rust-lang/crates.io-index" 71 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 72 | 73 | [[package]] 74 | name = "clap" 75 | version = "2.34.0" 76 | source = "registry+https://github.com/rust-lang/crates.io-index" 77 | checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" 78 | dependencies = [ 79 | "ansi_term", 80 | "atty", 81 | "bitflags", 82 | "strsim", 83 | "textwrap", 84 | "unicode-width", 85 | "vec_map", 86 | ] 87 | 88 | [[package]] 89 | name = "cranelift-bforest" 90 | version = "0.115.1" 91 | source = "registry+https://github.com/rust-lang/crates.io-index" 92 | checksum = "88c1d02b72b6c411c0a2e92b25ed791ad5d071184193c08a34aa0fdcdf000b72" 93 | dependencies = [ 94 | "cranelift-entity", 95 | ] 96 | 97 | [[package]] 98 | name = "cranelift-bitset" 99 | version = "0.115.1" 100 | source = "registry+https://github.com/rust-lang/crates.io-index" 101 | checksum = "720b93bd86ebbb23ebfb2db1ed44d54b2ecbdbb2d034d485bc64aa605ee787ab" 102 | 103 | [[package]] 104 | name = "cranelift-codegen" 105 | version = "0.115.1" 106 | source = "registry+https://github.com/rust-lang/crates.io-index" 107 | checksum = "aed3d2d9914d30b460eedd7fd507720203023997bef71452ce84873f9c93537c" 108 | dependencies = [ 109 | "bumpalo", 110 | "cranelift-bforest", 111 | "cranelift-bitset", 112 | "cranelift-codegen-meta", 113 | "cranelift-codegen-shared", 114 | "cranelift-control", 115 | "cranelift-entity", 116 | "cranelift-isle", 117 | "gimli", 118 | "hashbrown 0.14.5", 119 | "log", 120 | "regalloc2", 121 | "rustc-hash", 122 | "serde", 123 | "smallvec", 124 | "target-lexicon 0.12.16", 125 | ] 126 | 127 | [[package]] 128 | name = "cranelift-codegen-meta" 129 | version = "0.115.1" 130 | source = "registry+https://github.com/rust-lang/crates.io-index" 131 | checksum = "888c188d32263ec9e048873ff0b68c700933600d553f4412417916828be25f8e" 132 | dependencies = [ 133 | "cranelift-codegen-shared", 134 | ] 135 | 136 | [[package]] 137 | name = "cranelift-codegen-shared" 138 | version = "0.115.1" 139 | source = "registry+https://github.com/rust-lang/crates.io-index" 140 | checksum = "4ddd5f4114d04ce7e073dd74e2ad16541fc61970726fcc8b2d5644a154ee4127" 141 | 142 | [[package]] 143 | name = "cranelift-control" 144 | version = "0.115.1" 145 | source = "registry+https://github.com/rust-lang/crates.io-index" 146 | checksum = "92cc4c98d6a4256a1600d93ccd3536f3e77da9b4ca2c279de786ac22876e67d6" 147 | dependencies = [ 148 | "arbitrary", 149 | ] 150 | 151 | [[package]] 152 | name = "cranelift-entity" 153 | version = "0.115.1" 154 | source = "registry+https://github.com/rust-lang/crates.io-index" 155 | checksum = "760af4b5e051b5f82097a27274b917e3751736369fa73660513488248d27f23d" 156 | dependencies = [ 157 | "cranelift-bitset", 158 | ] 159 | 160 | [[package]] 161 | name = "cranelift-frontend" 162 | version = "0.115.1" 163 | source = "registry+https://github.com/rust-lang/crates.io-index" 164 | checksum = "c0bf77ec0f470621655ec7539860b5c620d4f91326654ab21b075b83900f8831" 165 | dependencies = [ 166 | "cranelift-codegen", 167 | "log", 168 | "smallvec", 169 | "target-lexicon 0.12.16", 170 | ] 171 | 172 | [[package]] 173 | name = "cranelift-isle" 174 | version = "0.115.1" 175 | source = "registry+https://github.com/rust-lang/crates.io-index" 176 | checksum = "4b665d0a6932c421620be184f9fc7f7adaf1b0bc2fa77bb7ac5177c49abf645b" 177 | 178 | [[package]] 179 | name = "cranelift-module" 180 | version = "0.115.1" 181 | source = "registry+https://github.com/rust-lang/crates.io-index" 182 | checksum = "e9490aa83e247b0bd222645ddb18912d7ae0bc95ab6fd34e559821d440f1b9e2" 183 | dependencies = [ 184 | "anyhow", 185 | "cranelift-codegen", 186 | "cranelift-control", 187 | ] 188 | 189 | [[package]] 190 | name = "cranelift-object" 191 | version = "0.115.1" 192 | source = "registry+https://github.com/rust-lang/crates.io-index" 193 | checksum = "1ba46c19c3fb773abfe73c18c78514837f198bd587456b223a843d27156495b8" 194 | dependencies = [ 195 | "anyhow", 196 | "cranelift-codegen", 197 | "cranelift-control", 198 | "cranelift-module", 199 | "log", 200 | "object", 201 | "target-lexicon 0.12.16", 202 | ] 203 | 204 | [[package]] 205 | name = "crc32fast" 206 | version = "1.4.2" 207 | source = "registry+https://github.com/rust-lang/crates.io-index" 208 | checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" 209 | dependencies = [ 210 | "cfg-if", 211 | ] 212 | 213 | [[package]] 214 | name = "env_logger" 215 | version = "0.5.13" 216 | source = "registry+https://github.com/rust-lang/crates.io-index" 217 | checksum = "15b0a4d2e39f8420210be8b27eeda28029729e2fd4291019455016c348240c38" 218 | dependencies = [ 219 | "atty", 220 | "humantime", 221 | "log", 222 | "regex", 223 | "termcolor", 224 | ] 225 | 226 | [[package]] 227 | name = "equivalent" 228 | version = "1.0.1" 229 | source = "registry+https://github.com/rust-lang/crates.io-index" 230 | checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" 231 | 232 | [[package]] 233 | name = "fallible-iterator" 234 | version = "0.3.0" 235 | source = "registry+https://github.com/rust-lang/crates.io-index" 236 | checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" 237 | 238 | [[package]] 239 | name = "foldhash" 240 | version = "0.1.4" 241 | source = "registry+https://github.com/rust-lang/crates.io-index" 242 | checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f" 243 | 244 | [[package]] 245 | name = "gimli" 246 | version = "0.31.1" 247 | source = "registry+https://github.com/rust-lang/crates.io-index" 248 | checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" 249 | dependencies = [ 250 | "fallible-iterator", 251 | "indexmap", 252 | "stable_deref_trait", 253 | ] 254 | 255 | [[package]] 256 | name = "hashbrown" 257 | version = "0.14.5" 258 | source = "registry+https://github.com/rust-lang/crates.io-index" 259 | checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" 260 | 261 | [[package]] 262 | name = "hashbrown" 263 | version = "0.15.2" 264 | source = "registry+https://github.com/rust-lang/crates.io-index" 265 | checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" 266 | dependencies = [ 267 | "foldhash", 268 | ] 269 | 270 | [[package]] 271 | name = "heck" 272 | version = "0.3.3" 273 | source = "registry+https://github.com/rust-lang/crates.io-index" 274 | checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" 275 | dependencies = [ 276 | "unicode-segmentation", 277 | ] 278 | 279 | [[package]] 280 | name = "hermit-abi" 281 | version = "0.1.19" 282 | source = "registry+https://github.com/rust-lang/crates.io-index" 283 | checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" 284 | dependencies = [ 285 | "libc", 286 | ] 287 | 288 | [[package]] 289 | name = "humantime" 290 | version = "1.3.0" 291 | source = "registry+https://github.com/rust-lang/crates.io-index" 292 | checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" 293 | dependencies = [ 294 | "quick-error", 295 | ] 296 | 297 | [[package]] 298 | name = "indexmap" 299 | version = "2.7.0" 300 | source = "registry+https://github.com/rust-lang/crates.io-index" 301 | checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" 302 | dependencies = [ 303 | "equivalent", 304 | "hashbrown 0.15.2", 305 | ] 306 | 307 | [[package]] 308 | name = "libc" 309 | version = "0.2.169" 310 | source = "registry+https://github.com/rust-lang/crates.io-index" 311 | checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" 312 | 313 | [[package]] 314 | name = "log" 315 | version = "0.4.25" 316 | source = "registry+https://github.com/rust-lang/crates.io-index" 317 | checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" 318 | 319 | [[package]] 320 | name = "memchr" 321 | version = "2.7.4" 322 | source = "registry+https://github.com/rust-lang/crates.io-index" 323 | checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" 324 | 325 | [[package]] 326 | name = "mmir_opt" 327 | version = "0.1.0" 328 | dependencies = [ 329 | "structopt", 330 | ] 331 | 332 | [[package]] 333 | name = "object" 334 | version = "0.36.7" 335 | source = "registry+https://github.com/rust-lang/crates.io-index" 336 | checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" 337 | dependencies = [ 338 | "crc32fast", 339 | "hashbrown 0.15.2", 340 | "indexmap", 341 | "memchr", 342 | ] 343 | 344 | [[package]] 345 | name = "proc-macro2" 346 | version = "0.4.30" 347 | source = "registry+https://github.com/rust-lang/crates.io-index" 348 | checksum = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" 349 | dependencies = [ 350 | "unicode-xid", 351 | ] 352 | 353 | [[package]] 354 | name = "proc-macro2" 355 | version = "1.0.93" 356 | source = "registry+https://github.com/rust-lang/crates.io-index" 357 | checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" 358 | dependencies = [ 359 | "unicode-ident", 360 | ] 361 | 362 | [[package]] 363 | name = "quick-error" 364 | version = "1.2.3" 365 | source = "registry+https://github.com/rust-lang/crates.io-index" 366 | checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" 367 | 368 | [[package]] 369 | name = "quote" 370 | version = "0.6.13" 371 | source = "registry+https://github.com/rust-lang/crates.io-index" 372 | checksum = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" 373 | dependencies = [ 374 | "proc-macro2 0.4.30", 375 | ] 376 | 377 | [[package]] 378 | name = "quote" 379 | version = "1.0.38" 380 | source = "registry+https://github.com/rust-lang/crates.io-index" 381 | checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" 382 | dependencies = [ 383 | "proc-macro2 1.0.93", 384 | ] 385 | 386 | [[package]] 387 | name = "rcc" 388 | version = "0.0.1" 389 | dependencies = [ 390 | "cranelift-codegen", 391 | "cranelift-frontend", 392 | "cranelift-module", 393 | "cranelift-object", 394 | "env_logger", 395 | "log", 396 | "structopt", 397 | "target-lexicon 0.11.2", 398 | "utf8reader", 399 | ] 400 | 401 | [[package]] 402 | name = "regalloc2" 403 | version = "0.11.1" 404 | source = "registry+https://github.com/rust-lang/crates.io-index" 405 | checksum = "145c1c267e14f20fb0f88aa76a1c5ffec42d592c1d28b3cd9148ae35916158d3" 406 | dependencies = [ 407 | "allocator-api2", 408 | "bumpalo", 409 | "hashbrown 0.15.2", 410 | "log", 411 | "rustc-hash", 412 | "smallvec", 413 | ] 414 | 415 | [[package]] 416 | name = "regex" 417 | version = "1.11.1" 418 | source = "registry+https://github.com/rust-lang/crates.io-index" 419 | checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" 420 | dependencies = [ 421 | "aho-corasick", 422 | "memchr", 423 | "regex-automata", 424 | "regex-syntax", 425 | ] 426 | 427 | [[package]] 428 | name = "regex-automata" 429 | version = "0.4.9" 430 | source = "registry+https://github.com/rust-lang/crates.io-index" 431 | checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" 432 | dependencies = [ 433 | "aho-corasick", 434 | "memchr", 435 | "regex-syntax", 436 | ] 437 | 438 | [[package]] 439 | name = "regex-syntax" 440 | version = "0.8.5" 441 | source = "registry+https://github.com/rust-lang/crates.io-index" 442 | checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" 443 | 444 | [[package]] 445 | name = "rustc-hash" 446 | version = "2.1.0" 447 | source = "registry+https://github.com/rust-lang/crates.io-index" 448 | checksum = "c7fb8039b3032c191086b10f11f319a6e99e1e82889c5cc6046f515c9db1d497" 449 | 450 | [[package]] 451 | name = "serde" 452 | version = "1.0.217" 453 | source = "registry+https://github.com/rust-lang/crates.io-index" 454 | checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" 455 | dependencies = [ 456 | "serde_derive", 457 | ] 458 | 459 | [[package]] 460 | name = "serde_derive" 461 | version = "1.0.217" 462 | source = "registry+https://github.com/rust-lang/crates.io-index" 463 | checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" 464 | dependencies = [ 465 | "proc-macro2 1.0.93", 466 | "quote 1.0.38", 467 | "syn 2.0.96", 468 | ] 469 | 470 | [[package]] 471 | name = "smallvec" 472 | version = "1.13.2" 473 | source = "registry+https://github.com/rust-lang/crates.io-index" 474 | checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" 475 | 476 | [[package]] 477 | name = "stable_deref_trait" 478 | version = "1.2.0" 479 | source = "registry+https://github.com/rust-lang/crates.io-index" 480 | checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" 481 | 482 | [[package]] 483 | name = "strsim" 484 | version = "0.8.0" 485 | source = "registry+https://github.com/rust-lang/crates.io-index" 486 | checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" 487 | 488 | [[package]] 489 | name = "structopt" 490 | version = "0.2.18" 491 | source = "registry+https://github.com/rust-lang/crates.io-index" 492 | checksum = "16c2cdbf9cc375f15d1b4141bc48aeef444806655cd0e904207edc8d68d86ed7" 493 | dependencies = [ 494 | "clap", 495 | "structopt-derive", 496 | ] 497 | 498 | [[package]] 499 | name = "structopt-derive" 500 | version = "0.2.18" 501 | source = "registry+https://github.com/rust-lang/crates.io-index" 502 | checksum = "53010261a84b37689f9ed7d395165029f9cc7abb9f56bbfe86bee2597ed25107" 503 | dependencies = [ 504 | "heck", 505 | "proc-macro2 0.4.30", 506 | "quote 0.6.13", 507 | "syn 0.15.44", 508 | ] 509 | 510 | [[package]] 511 | name = "syn" 512 | version = "0.15.44" 513 | source = "registry+https://github.com/rust-lang/crates.io-index" 514 | checksum = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" 515 | dependencies = [ 516 | "proc-macro2 0.4.30", 517 | "quote 0.6.13", 518 | "unicode-xid", 519 | ] 520 | 521 | [[package]] 522 | name = "syn" 523 | version = "2.0.96" 524 | source = "registry+https://github.com/rust-lang/crates.io-index" 525 | checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" 526 | dependencies = [ 527 | "proc-macro2 1.0.93", 528 | "quote 1.0.38", 529 | "unicode-ident", 530 | ] 531 | 532 | [[package]] 533 | name = "target-lexicon" 534 | version = "0.11.2" 535 | source = "registry+https://github.com/rust-lang/crates.io-index" 536 | checksum = "422045212ea98508ae3d28025bc5aaa2bd4a9cdaecd442a08da2ee620ee9ea95" 537 | 538 | [[package]] 539 | name = "target-lexicon" 540 | version = "0.12.16" 541 | source = "registry+https://github.com/rust-lang/crates.io-index" 542 | checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" 543 | 544 | [[package]] 545 | name = "termcolor" 546 | version = "1.4.1" 547 | source = "registry+https://github.com/rust-lang/crates.io-index" 548 | checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" 549 | dependencies = [ 550 | "winapi-util", 551 | ] 552 | 553 | [[package]] 554 | name = "textwrap" 555 | version = "0.11.0" 556 | source = "registry+https://github.com/rust-lang/crates.io-index" 557 | checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" 558 | dependencies = [ 559 | "unicode-width", 560 | ] 561 | 562 | [[package]] 563 | name = "unicode-ident" 564 | version = "1.0.14" 565 | source = "registry+https://github.com/rust-lang/crates.io-index" 566 | checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" 567 | 568 | [[package]] 569 | name = "unicode-segmentation" 570 | version = "1.12.0" 571 | source = "registry+https://github.com/rust-lang/crates.io-index" 572 | checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" 573 | 574 | [[package]] 575 | name = "unicode-width" 576 | version = "0.1.14" 577 | source = "registry+https://github.com/rust-lang/crates.io-index" 578 | checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" 579 | 580 | [[package]] 581 | name = "unicode-xid" 582 | version = "0.1.0" 583 | source = "registry+https://github.com/rust-lang/crates.io-index" 584 | checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" 585 | 586 | [[package]] 587 | name = "utf8reader" 588 | version = "0.1.0" 589 | source = "registry+https://github.com/rust-lang/crates.io-index" 590 | checksum = "31add7b376bd6ae962e31fcdd0e7b6ceea137c47edea6ef1eba7ae1ded67cf0c" 591 | 592 | [[package]] 593 | name = "vec_map" 594 | version = "0.8.2" 595 | source = "registry+https://github.com/rust-lang/crates.io-index" 596 | checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" 597 | 598 | [[package]] 599 | name = "winapi" 600 | version = "0.3.9" 601 | source = "registry+https://github.com/rust-lang/crates.io-index" 602 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 603 | dependencies = [ 604 | "winapi-i686-pc-windows-gnu", 605 | "winapi-x86_64-pc-windows-gnu", 606 | ] 607 | 608 | [[package]] 609 | name = "winapi-i686-pc-windows-gnu" 610 | version = "0.4.0" 611 | source = "registry+https://github.com/rust-lang/crates.io-index" 612 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 613 | 614 | [[package]] 615 | name = "winapi-util" 616 | version = "0.1.9" 617 | source = "registry+https://github.com/rust-lang/crates.io-index" 618 | checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" 619 | dependencies = [ 620 | "windows-sys", 621 | ] 622 | 623 | [[package]] 624 | name = "winapi-x86_64-pc-windows-gnu" 625 | version = "0.4.0" 626 | source = "registry+https://github.com/rust-lang/crates.io-index" 627 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 628 | 629 | [[package]] 630 | name = "windows-sys" 631 | version = "0.59.0" 632 | source = "registry+https://github.com/rust-lang/crates.io-index" 633 | checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" 634 | dependencies = [ 635 | "windows-targets", 636 | ] 637 | 638 | [[package]] 639 | name = "windows-targets" 640 | version = "0.52.6" 641 | source = "registry+https://github.com/rust-lang/crates.io-index" 642 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 643 | dependencies = [ 644 | "windows_aarch64_gnullvm", 645 | "windows_aarch64_msvc", 646 | "windows_i686_gnu", 647 | "windows_i686_gnullvm", 648 | "windows_i686_msvc", 649 | "windows_x86_64_gnu", 650 | "windows_x86_64_gnullvm", 651 | "windows_x86_64_msvc", 652 | ] 653 | 654 | [[package]] 655 | name = "windows_aarch64_gnullvm" 656 | version = "0.52.6" 657 | source = "registry+https://github.com/rust-lang/crates.io-index" 658 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 659 | 660 | [[package]] 661 | name = "windows_aarch64_msvc" 662 | version = "0.52.6" 663 | source = "registry+https://github.com/rust-lang/crates.io-index" 664 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 665 | 666 | [[package]] 667 | name = "windows_i686_gnu" 668 | version = "0.52.6" 669 | source = "registry+https://github.com/rust-lang/crates.io-index" 670 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 671 | 672 | [[package]] 673 | name = "windows_i686_gnullvm" 674 | version = "0.52.6" 675 | source = "registry+https://github.com/rust-lang/crates.io-index" 676 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 677 | 678 | [[package]] 679 | name = "windows_i686_msvc" 680 | version = "0.52.6" 681 | source = "registry+https://github.com/rust-lang/crates.io-index" 682 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 683 | 684 | [[package]] 685 | name = "windows_x86_64_gnu" 686 | version = "0.52.6" 687 | source = "registry+https://github.com/rust-lang/crates.io-index" 688 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 689 | 690 | [[package]] 691 | name = "windows_x86_64_gnullvm" 692 | version = "0.52.6" 693 | source = "registry+https://github.com/rust-lang/crates.io-index" 694 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 695 | 696 | [[package]] 697 | name = "windows_x86_64_msvc" 698 | version = "0.52.6" 699 | source = "registry+https://github.com/rust-lang/crates.io-index" 700 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 701 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | edition = "2015" 3 | name = "rcc" 4 | version = "0.0.1" 5 | authors = ["John Hodge "] 6 | 7 | [dependencies] 8 | log = "0.4" 9 | env_logger = "0.5" 10 | structopt = "0.2" 11 | utf8reader = "0.1" 12 | 13 | cranelift-frontend = "0.115" 14 | cranelift-codegen = "0.115" 15 | cranelift-module = "0.115" 16 | cranelift-object = "0.115" 17 | target-lexicon = "0.11" 18 | 19 | [workspace] 20 | members = ["tools/mmir_opt"] 21 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | A C parser witten in Rust, eventual idea is to be the parser of "UDI C" - an asynchronous C-like language for writing UDI drivers in (translated into callback-based C before distribution) 2 | -------------------------------------------------------------------------------- /samples/1.c: -------------------------------------------------------------------------------- 1 | // A helper that stress tests the compiler 2 | 3 | //#include 4 | #define/**/ foo(bar) baz 5 | #define baz 6 | foo(_) 7 | baz 8 | 9 | //extern int printf(const char*, ...); 10 | extern int printf(const char*, const char*); 11 | int main(int argc, const char* argv[]) { 12 | //printf("Hello, %s! float %f", "world", 1.23f); 13 | printf("Hello, %s!", "world"); 14 | 15 | for(int i = 0; i < 10; i ++) 16 | { 17 | (void)0; 18 | } 19 | 20 | return 0; 21 | } 22 | 23 | -------------------------------------------------------------------------------- /samples/include/assert.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define assert(expr) do { if(! (expr) ) { abort(); } } while(0) 4 | 5 | -------------------------------------------------------------------------------- /samples/include/ctype.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | 4 | extern int isalnum(int c); 5 | extern int isalpha(int c); 6 | extern int iscntrl(int c); 7 | extern int isdigit(int c); 8 | extern int isgraph(int c); 9 | extern int islower(int c); 10 | extern int isprint(int c); 11 | extern int ispunct(int c); 12 | extern int isspace(int c); 13 | extern int isupper(int c); 14 | extern int isxdigit(int c); 15 | 16 | extern int isascii(int c); 17 | extern int isblank(int c); 18 | 19 | 20 | extern int toupper(int c); 21 | extern int tolower(int c); 22 | -------------------------------------------------------------------------------- /samples/include/errno.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | extern int errno; 4 | 5 | -------------------------------------------------------------------------------- /samples/include/inttypes.h: -------------------------------------------------------------------------------- 1 | // 2 | // inttypes.h - Primarily printing macros 3 | // 4 | #pragma once 5 | 6 | #define PRIx64 "llx" 7 | -------------------------------------------------------------------------------- /samples/include/limits.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define SIZE_MAX 0xFFFFFFFFul 4 | #define UINT_MAX (~0u) 5 | #define ULONG_MAX (~0ul) 6 | #define ULLONG_MAX (~0ull) 7 | #define LLONG_MAX ((long long)(~0ull >> 1)) 8 | #define LLONG_MIN (-LLONG_MAX - 1) 9 | #define LONG_MAX (long)(~0ul >> 1) 10 | #define LONG_MIN ((-LONG_MAX - 1)) 11 | #define INT_MAX ((int)(~0u >> 1)) 12 | #define INT_MIN (-INT_MAX - 1) 13 | -------------------------------------------------------------------------------- /samples/include/math.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | extern double sqrt(double x); 4 | extern float sqrtf(float x); 5 | extern long double sqrtl(long double x); 6 | -------------------------------------------------------------------------------- /samples/include/posix/_impl_pwd_systypes.h: -------------------------------------------------------------------------------- 1 | #if defined(__INCLUDED_POSIX_PWD_H) && defined(__INCLUDED_POSIX_SYS_TYPES_H) 2 | #pragma once 3 | 4 | struct passwd { 5 | char *pw_name; /* username */ 6 | char *pw_passwd; /* user password */ 7 | uid_t pw_uid; /* user ID */ 8 | gid_t pw_gid; /* group ID */ 9 | char *pw_gecos; /* user information */ 10 | char *pw_dir; /* home directory */ 11 | char *pw_shell; /* shell program */ 12 | }; 13 | 14 | extern struct passwd *getpwnam(const char *name); 15 | extern struct passwd *getpwuid(uid_t uid); 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /samples/include/posix/_impl_unistd_systypes.h: -------------------------------------------------------------------------------- 1 | #if defined(__INCLUDED_POSIX_UNISTD_H) && defined(__INCLUDED_POSIX_SYS_TYPES_H) 2 | #pragma once 3 | 4 | extern uid_t getuid(void); 5 | extern uid_t geteuid(void); 6 | 7 | #endif 8 | -------------------------------------------------------------------------------- /samples/include/posix/pwd.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #define __INCLUDED_POSIX_PWD_H 3 | 4 | #include "_impl_pwd_systypes.h" 5 | -------------------------------------------------------------------------------- /samples/include/posix/regex.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | typedef long regoff_t; 4 | 5 | typedef struct _regex_s { 6 | size_t re_nsub; /* Number of parenthesized subexpressions. */ 7 | } regex_t; 8 | typedef struct _regmatch_s { 9 | regoff_t rm_so; /* Byte offset from start of string 10 | to start of substring */ 11 | regoff_t rm_eo; /* Byte offset from start of string of 12 | the first character after the end of 13 | substring */ 14 | } regmatch_t; 15 | 16 | #define REG_EXTENDED 1 17 | #define REG_ICASE 2 18 | #define REG_NOSUB 4 19 | #define REG_NEWLINE 8 20 | 21 | extern int regcomp(regex_t *preg, const char *regex, int cflags); 22 | extern int regexec(const regex_t *preg, const char *string, size_t nmatch, regmatch_t pmatch[], int eflags); 23 | extern size_t regerror(int errcode, const regex_t *preg, char *errbuf, size_t errbuf_size); 24 | extern void regfree(regex_t *preg); 25 | -------------------------------------------------------------------------------- /samples/include/posix/sys/types.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #define __INCLUDED_POSIX_SYS_TYPES_H 3 | 4 | typedef __magictype__("uid_t:u32") uid_t; 5 | typedef __magictype__("gid_t:u32") gid_t; 6 | 7 | #include "../_impl_unistd_systypes.h" 8 | #include "../_impl_pwd_systypes.h" 9 | -------------------------------------------------------------------------------- /samples/include/posix/unistd.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #define __INCLUDED_POSIX_UNISTD_H 3 | 4 | extern int close(int fd); 5 | 6 | #include "_impl_unistd_systypes.h" 7 | -------------------------------------------------------------------------------- /samples/include/setjmp.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | typedef __magictype__("jmp_buf:v64") jmp_buf; 4 | 5 | extern int setjmp(jmp_buf env); 6 | extern void longjmp(jmp_buf env, int val); 7 | // POSIX 2001 8 | //extern int sigsetjmp(sigjmp_buf env, int savesigs); 9 | //extern void siglongjmp(sigjmp_buf env, int val); 10 | -------------------------------------------------------------------------------- /samples/include/signal.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | typedef void (*sighandler_t)(int); 4 | 5 | extern sighandler_t signal(int signum, sighandler_t handler); 6 | 7 | enum { 8 | SIGINT = 2, 9 | }; 10 | 11 | -------------------------------------------------------------------------------- /samples/include/stdarg.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | //typedef __magictype__("va_list:void") va_list; 4 | typedef __gnuc_va_list va_list; 5 | 6 | #define va_start(__list, __first) __magiccall__("va_start" : __list, __first :) 7 | #define va_end(__list) __magiccall__("va_end" : __list :) 8 | #define va_arg(__list, __ty) __magiccall__("va_arg" : __list : __ty) 9 | #define va_copy(__dst, __src) __magiccall__("va_copy" : __dst, __src : ) 10 | 11 | #include 12 | 13 | extern int vprintf(const char *format, va_list ap); 14 | extern int vfprintf(FILE *stream, const char *format, va_list ap); 15 | extern int vdprintf(int fd, const char *format, va_list ap); 16 | extern int vsprintf(char *str, const char *format, va_list ap); 17 | extern int vsnprintf(char *str, size_t size, const char *format, va_list ap); 18 | 19 | -------------------------------------------------------------------------------- /samples/include/stdbool.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | typedef _Bool bool; 4 | #define true ((_Bool)1) 5 | #define false ((_Bool)0) 6 | -------------------------------------------------------------------------------- /samples/include/stddef.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef __RCC__ 4 | typedef __magictype__("size_t:uptr") size_t; 5 | typedef __magictype__("ssize_t:iptr") ssize_t; 6 | typedef __magictype__("wchar_t:u32") wchar_t; 7 | #else 8 | typedef unsigned long size_t; 9 | typedef unsigned long ssize_t; 10 | typedef unsigned long wchar_t; 11 | #endif 12 | 13 | #define NULL ((void*)0) 14 | -------------------------------------------------------------------------------- /samples/include/stdint.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | typedef __magictype__("int8_t:i8") int8_t; 4 | typedef __magictype__("int16_t:i16") int16_t; 5 | typedef __magictype__("int32_t:i32") int32_t; 6 | typedef __magictype__("int64_t:i64") int64_t; 7 | 8 | typedef __magictype__("uint8_t:u8") uint8_t; 9 | typedef __magictype__("uint16_t:u16") uint16_t; 10 | typedef __magictype__("uint32_t:u32") uint32_t; 11 | typedef __magictype__("uint64_t:u64") uint64_t; 12 | 13 | typedef __magictype__("intptr_t:iptr") intptr_t; 14 | typedef __magictype__("uintptr_t:uptr") uintptr_t; 15 | 16 | typedef __magictype__("intptr_t:u64") intptr_t; 17 | 18 | -------------------------------------------------------------------------------- /samples/include/stdio.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #define BUFSIZ 4096 6 | 7 | typedef struct FILE FILE; 8 | typedef unsigned long long fpos_t; 9 | typedef __magictype__("off_t:i64") off_t; // POSIX 10 | 11 | extern FILE* stdin; 12 | extern FILE* stdout; 13 | extern FILE* stderr; 14 | 15 | extern void perror(const char* s); 16 | 17 | extern int printf(const char *format, ...); 18 | extern int fprintf(FILE *stream, const char *format, ...); 19 | extern int dprintf(int fd, const char *format, ...); 20 | extern int sprintf(char *str, const char *format, ...); 21 | extern int snprintf(char *str, size_t size, const char *format, ...); 22 | 23 | extern int scanf(const char *format, ...); 24 | extern int fscanf(FILE *stream, const char *format, ...); 25 | extern int sscanf(const char *str, const char *format, ...); 26 | 27 | extern FILE *fopen(const char *pathname, const char *mode); 28 | //extern FILE *fdopen(int fd, const char *mode); 29 | extern FILE *freopen(const char *pathname, const char *mode, FILE *stream); 30 | 31 | enum { 32 | SEEK_END = -1, 33 | SEEK_CUR = 0, 34 | SEEK_SET = 1, 35 | }; 36 | extern int fseek(FILE *stream, long offset, int whence); 37 | extern long ftell(FILE *stream); 38 | extern void rewind(FILE *stream); 39 | extern int fgetpos(FILE *stream, fpos_t *pos); 40 | extern int fsetpos(FILE *stream, const fpos_t *pos); 41 | 42 | extern size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream); 43 | extern size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream); 44 | 45 | extern int fflush(FILE *stream); 46 | extern int fclose(FILE *stream); 47 | 48 | extern int fgetc(FILE *stream); 49 | extern char *fgets(char *s, int size, FILE *stream); 50 | extern int getc(FILE *stream); 51 | extern int getchar(void); 52 | extern int ungetc(int c, FILE *stream); 53 | 54 | extern int fputc(int c, FILE *stream); 55 | extern int fputs(const char *s, FILE *stream); 56 | extern int putc(int c, FILE *stream); 57 | extern int putchar(int c); 58 | extern int puts(const char *s); 59 | 60 | -------------------------------------------------------------------------------- /samples/include/stdlib.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "stddef.h" 4 | 5 | extern void abort(void); 6 | 7 | extern int atoi(const char *nptr); 8 | extern long atol(const char *nptr); 9 | extern long long atoll(const char *nptr); 10 | 11 | extern long strtol(const char *nptr, char **endptr, int base); 12 | extern long long strtoll(const char *nptr, char **endptr, int base); 13 | 14 | extern void *malloc(size_t size); 15 | extern void free(void *ptr); 16 | extern void *calloc(size_t nmemb, size_t size); 17 | extern void *realloc(void *ptr, size_t size); 18 | 19 | extern void exit(int status) __attribute__((noreturn)); 20 | 21 | 22 | static inline int abs(int j) { if(j < 0) { return -j; } else { return j; } } 23 | static inline long labs(long j) { if(j < 0) { return -j; } else { return j; } } 24 | static inline long long llabs(long long j) { if(j < 0) { return -j; } else { return j; } } 25 | 26 | #define __builtin_return_address(x) 0 27 | 28 | -------------------------------------------------------------------------------- /samples/include/string.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | extern int strcmp(const char *s1, const char *s2); 4 | extern int strncmp(const char *s1, const char *s2, size_t n); 5 | extern size_t strlen(const char *s); 6 | 7 | extern char *strcpy(char *dest, const char *src); 8 | extern char *strncpy(char *dest, const char *src, size_t n); 9 | 10 | extern void *memcpy(void *dest, const void *src, size_t n); 11 | extern void *memset(void *s, int c, size_t n); 12 | extern void *memmove(void *dest, const void *src, size_t n); 13 | extern int memcmp(const void *s1, const void *s2, size_t n); 14 | 15 | extern char *strdup(const char *s); 16 | // POSIX >=200809 17 | extern char *strndup(const char *s, size_t n); 18 | 19 | extern char *strchr(const char *s, int c); 20 | extern char *strrchr(const char *s, int c); 21 | 22 | extern char *strerror(int errnum); 23 | 24 | 25 | -------------------------------------------------------------------------------- /samples/while_loop.c: -------------------------------------------------------------------------------- 1 | void while_test(int* out) 2 | { 3 | int n = 16; 4 | while(n--) 5 | { 6 | out[n] = 0; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/ast/pretty_print.rs: -------------------------------------------------------------------------------- 1 | 2 | use super::ItemRef; 3 | 4 | pub fn write(mut sink: impl ::std::io::Write, prog: &super::Program) 5 | { 6 | PrettyPrinter { sink: &mut sink, prog: prog }.write_program(); 7 | } 8 | 9 | struct PrettyPrinter<'a, 'b> { 10 | sink: &'a mut dyn (::std::io::Write), 11 | prog: &'b super::Program, 12 | } 13 | 14 | impl<'a, 'b> PrettyPrinter<'a, 'b> 15 | { 16 | 17 | fn write_program(&mut self) 18 | { 19 | for item in &self.prog.item_order 20 | { 21 | match item 22 | { 23 | &ItemRef::ValueDecl(ref name) => { 24 | self.write_prototype(&self.prog.symbols[name]); 25 | }, 26 | &ItemRef::Value(ref name) => { 27 | self.write_value(&self.prog.symbols[name]); 28 | }, 29 | &ItemRef::Typedef(ref name) => { 30 | let td = &self.prog.typedefs[name]; 31 | self.write_str("typedef "); self.write_type_d(td, &mut |s| s.write_str(name), false); self.write_str(";\n"); 32 | }, 33 | &ItemRef::Struct(ref name) => { 34 | self.write_struct_def(&self.prog.structs[name].borrow(), true); 35 | self.write_str(";\n"); 36 | }, 37 | &ItemRef::Enum(ref name) => { 38 | self.write_enum_def(&self.prog.enums[name].borrow(), true); 39 | self.write_str(";\n"); 40 | }, 41 | &ItemRef::Union(ref name) => { 42 | self.write_union_def(&self.prog.unions[name].borrow(), true); 43 | self.write_str(";\n"); 44 | }, 45 | } 46 | } 47 | } 48 | 49 | fn find_typedef(&self, ty: &::types::TypeRef) -> Option<&'b str> 50 | { 51 | for (k,v) in &self.prog.typedefs { 52 | if v == ty || (ty.basetype == v.basetype && v.qualifiers.is_lesser_than(&ty.qualifiers)) { 53 | return Some(k) 54 | } 55 | } 56 | None 57 | } 58 | 59 | fn write_prototype(&mut self, sym: &::ast::Symbol) 60 | { 61 | self.write_type(&sym.symtype, |f| f.write_str(&sym.name)); 62 | self.write_str(";\n"); 63 | } 64 | fn write_value(&mut self, sym: &::ast::Symbol) 65 | { 66 | self.write_type(&sym.symtype, |f| f.write_str(&sym.name)); 67 | match sym.value 68 | { 69 | Some(::ast::SymbolValue::Code(ref fcn)) => { 70 | self.write_str("\n"); 71 | self.write_block(&fcn.borrow().code, 0); 72 | self.write_str("\n"); 73 | }, 74 | Some(::ast::SymbolValue::Value(ref v)) => { 75 | if let Some(init) = v.borrow().as_ref() { 76 | self.write_str(" = "); 77 | self.write_initialiser(init); 78 | } 79 | self.write_str(";\n"); 80 | }, 81 | None => panic!("Value with no value - {:?}", sym.name), 82 | } 83 | } 84 | 85 | 86 | fn write_struct_def(&mut self, s: &::types::Struct, use_newlines: bool) 87 | { 88 | let nl = if true || use_newlines { "\n" } else { " " }; 89 | let indent = if true || use_newlines { "\t" } else { "" }; 90 | self.write_str("struct "); self.write_str(&s.name); self.write_str(nl); 91 | if let Some(ref body) = s.get_items() 92 | { 93 | self.write_str("{"); self.write_str(nl); 94 | for &(ref ty, ref name) in &body.fields 95 | { 96 | self.write_str(indent); 97 | match ty 98 | { 99 | crate::types::StructFieldTy::Value(ref ty) => { 100 | self.write_type(ty, |s| s.write_str(name)); 101 | }, 102 | crate::types::StructFieldTy::Bitfield(ty, bits) => { 103 | self.write_type(ty, |s| s.write_str(name)); 104 | write!(self, ": {}", bits); 105 | }, 106 | } 107 | self.write_str(";"); self.write_str(nl); 108 | } 109 | self.write_str("}"); 110 | if body.attributes.gcc.len() > 0 111 | { 112 | self.write_str("__attribute__(("); 113 | for a in &body.attributes.gcc { 114 | self.write_str(&a.0); 115 | } 116 | self.write_str("))"); 117 | } 118 | } 119 | } 120 | fn write_enum_def(&mut self, s: &::types::Enum, use_newlines: bool) 121 | { 122 | let nl = if true || use_newlines { "\n" } else { " " }; 123 | let indent = if true || use_newlines { "\t" } else { "" }; 124 | self.write_str("enum "); self.write_str(&s.name); self.write_str(nl); 125 | if let Some(ref items) = s.get_items() 126 | { 127 | self.write_str("{"); self.write_str(nl); 128 | for &(val, ref name) in items.iter() 129 | { 130 | self.write_str(indent); 131 | write!(self, "{} = {},", name, val); 132 | self.write_str(nl); 133 | } 134 | self.write_str("}"); 135 | /*if body.attributes.gcc.len() > 0 136 | { 137 | self.write_str("__attribute__(("); 138 | for a in &body.attributes.gcc { 139 | self.write_str(&a.0); 140 | } 141 | self.write_str("))"); 142 | }*/ 143 | } 144 | } 145 | fn write_union_def(&mut self, s: &::types::Union, use_newlines: bool) 146 | { 147 | let nl = if true || use_newlines { "\n" } else { " " }; 148 | let indent = if true || use_newlines { "\t" } else { "" }; 149 | self.write_str("union "); self.write_str(&s.name); self.write_str(nl); 150 | if let Some(ref body) = s.get_items() 151 | { 152 | self.write_str("{"); self.write_str(nl); 153 | for &(ref ty, ref name) in body.iter() 154 | { 155 | self.write_str(indent); 156 | self.write_type(ty, |s| s.write_str(name)); 157 | self.write_str(";"); self.write_str(nl); 158 | } 159 | self.write_str("}"); 160 | } 161 | } 162 | 163 | fn write_initialiser(&mut self, init: &::ast::Initialiser) 164 | { 165 | match init 166 | { 167 | &::ast::Initialiser::Value(ref v) => { 168 | self.write_node(v, super::NodePrecedence::CommaOperator.up()); 169 | }, 170 | &::ast::Initialiser::ListLiteral(ref vs) => { 171 | self.write_str(" = {"); 172 | let mut is_first = true; 173 | for v in vs { 174 | if !is_first { 175 | self.write_str(", "); 176 | } 177 | else { 178 | self.write_str(" "); 179 | } 180 | is_first = false; 181 | self.write_initialiser(v); 182 | } 183 | self.write_str(" }"); 184 | }, 185 | &::ast::Initialiser::ArrayLiteral(_) => panic!("TODO: Pretty-print ArrayLiteral"), 186 | &::ast::Initialiser::StructLiteral(_) => panic!("TODO: Pretty-print StructLiteral"), 187 | } 188 | } 189 | 190 | 191 | //fn write_type(&mut self, ty: &::types::TypeRef, mut cb: impl FnOnce(&mut Self)) 192 | fn write_type(&mut self, ty: &::types::TypeRef, mut cb: impl FnMut(&mut Self)) 193 | { 194 | //self.write_type_d(ty, Box::new(cb)); 195 | self.write_type_d(ty, &mut cb, true); 196 | } 197 | //fn write_type_d<'b>(&mut self, ty: &::types::TypeRef, cb: Box<::std::boxed::FnBox(&mut Self)+'b>) 198 | fn write_type_d(&mut self, ty: &::types::TypeRef, cb: &mut dyn FnMut(&mut Self), use_typedef: bool) 199 | { 200 | use ::types::BaseType; 201 | match ty.basetype 202 | { 203 | BaseType::Void => { 204 | self.write_type_qualifiers(&ty.qualifiers); 205 | self.write_str("void "); 206 | cb(self); 207 | }, 208 | BaseType::Bool => { 209 | self.write_type_qualifiers(&ty.qualifiers); 210 | self.write_str("_Bool "); 211 | cb(self); 212 | }, 213 | BaseType::Struct(ref r) => { 214 | if use_typedef { 215 | //if let Some(t) = self.find_typedef(ty) { 216 | // write!(self, "{} ", t); 217 | // cb(self); 218 | //} 219 | } 220 | self.write_type_qualifiers(&ty.qualifiers); 221 | if r.borrow().name != "" { 222 | write!(self, "struct {} ", r.borrow().name); 223 | } 224 | else { 225 | let print_body = if ! use_typedef { 226 | true 227 | } 228 | else if let Some(t) = self.find_typedef(ty) { 229 | write!(self, "{} ", t); 230 | false 231 | } 232 | else { 233 | true 234 | }; 235 | if print_body 236 | { 237 | self.write_struct_def(&r.borrow(), false); 238 | self.write_str(" "); 239 | } 240 | } 241 | cb(self); 242 | }, 243 | BaseType::Enum(ref r) => { 244 | self.write_type_qualifiers(&ty.qualifiers); 245 | if r.borrow().name != "" { 246 | write!(self, "enum {} ", r.borrow().name); 247 | } 248 | else { 249 | self.write_str("enum { ... } "); 250 | } 251 | cb(self); 252 | }, 253 | BaseType::Union(ref r) => { 254 | self.write_type_qualifiers(&ty.qualifiers); 255 | if r.borrow().name != "" { 256 | write!(self, "union {} ", r.borrow().name); 257 | } 258 | else { 259 | self.write_str("union { ... } "); 260 | } 261 | cb(self); 262 | }, 263 | BaseType::Integer(ref r) => { 264 | self.write_type_qualifiers(&ty.qualifiers); 265 | use ::types::IntClass; 266 | use ::types::Signedness::*; 267 | match r 268 | { 269 | //IntClass::Bits(s,b) => write!(self, "{}int{}_t ", if s.is_unsigned() { "u" } else { "" }, b), 270 | IntClass::Char(s) => write!(self, "{}char ", match s { None => "", Some(Signed) => "signed ", Some(Unsigned) => "unsigned " }), 271 | IntClass::Short(s) => write!(self, "{}short ", if s.is_unsigned() { "unsigned " } else { "" }), 272 | IntClass::Int(s) => write!(self, "{}int ", if s.is_unsigned() { "unsigned " } else { "" }), 273 | IntClass::Long(s) => write!(self, "{}long ", if s.is_unsigned() { "unsigned " } else { "" }), 274 | IntClass::LongLong(s) => write!(self, "{}long long ", if s.is_unsigned() { "unsigned " } else { "" }), 275 | } 276 | cb(self); 277 | }, 278 | BaseType::Float(ref r) => { 279 | self.write_type_qualifiers(&ty.qualifiers); 280 | use ::types::FloatClass; 281 | match r 282 | { 283 | FloatClass::Float => self.write_str("float "), 284 | FloatClass::Double => self.write_str("double "), 285 | FloatClass::LongDouble => self.write_str("long double "), 286 | } 287 | cb(self); 288 | }, 289 | 290 | BaseType::MagicType(ref m) => { 291 | self.write_type_qualifiers(&ty.qualifiers); 292 | use ::types::MagicType; 293 | match *m 294 | { 295 | MagicType::Named(ref n, ref _repr) => { self.write_str(n); self.write_str(" "); }, 296 | MagicType::VaList => self.write_str("va_list "), 297 | } 298 | cb(self); 299 | }, 300 | 301 | BaseType::Pointer(ref ity) => { 302 | self.write_type(ity, |s| { 303 | s.write_str("*"); 304 | s.write_type_qualifiers(&ty.qualifiers); 305 | (*cb)(s); 306 | }); 307 | }, 308 | BaseType::Array(ref ity, ref size) => { 309 | self.write_type(ity, |s| { 310 | cb(s); 311 | s.write_str("["); 312 | match size 313 | { 314 | &::types::ArraySize::None => {}, 315 | &::types::ArraySize::Fixed(v) => { write!(s, "{}", v); }, 316 | &::types::ArraySize::Expr(ref v) => { 317 | v.with_node(|node| s.write_node(node, super::NodePrecedence::CommaOperator.up())); 318 | }, 319 | } 320 | s.write_str("]"); 321 | }); 322 | }, 323 | BaseType::Function(ref info) => { 324 | self.write_type(&info.ret, |_| {}); 325 | (*cb)(self); 326 | self.write_str("("); 327 | let mut b = false; 328 | for (aty, aname) in &info.args { 329 | if b { 330 | self.write_str(", "); 331 | } 332 | b = true; 333 | self.write_type(aty, |f| f.write_str(aname)); 334 | } 335 | if info.is_variadic { 336 | self.write_str(", "); 337 | self.write_str("..."); 338 | } 339 | self.write_str(")"); 340 | self.write_type_qualifiers(&ty.qualifiers); 341 | if !info.attributes.gcc.is_empty() { 342 | self.write_str(" __attribute__(("); 343 | b = false; 344 | for (name,args) in &info.attributes.gcc { 345 | if b { 346 | self.write_str(", "); 347 | } 348 | b = true; 349 | self.write_str(name); 350 | if !args.is_empty() { 351 | self.write_str("("); 352 | let mut b = false; 353 | for a in args { 354 | if b { self.write_str(",") }; 355 | b = true; 356 | self.write_str(a); 357 | } 358 | self.write_str(")"); 359 | } 360 | } 361 | self.write_str("))"); 362 | } 363 | }, 364 | BaseType::TypeOf(ref inner) => { 365 | self.write_str("typeof("); 366 | self.write_node(&inner.node(), super::NodePrecedence::CommaOperator.up()); 367 | self.write_str(")"); 368 | }, 369 | } 370 | } 371 | fn write_type_qualifiers(&mut self, qualifiers: &::types::Qualifiers) 372 | { 373 | if qualifiers.is_const() { 374 | self.write_str("const "); 375 | } 376 | if qualifiers.is_volatile() { 377 | self.write_str("volatile "); 378 | } 379 | if qualifiers.is_restrict() { 380 | self.write_str("restrict "); 381 | } 382 | } 383 | 384 | fn write_block(&mut self, block: &super::Block, indent: usize) 385 | { 386 | for _ in 0 .. indent { 387 | self.write_str("\t"); 388 | } 389 | self.write_str("{\n"); 390 | for sn in block 391 | { 392 | for _ in 0 .. indent { 393 | self.write_str("\t"); 394 | } 395 | // Only indent again if the statement is NOT a label 396 | match sn 397 | { 398 | &super::Statement::Label(..) => { }, 399 | &super::Statement::CaseDefault => { }, 400 | &super::Statement::CaseSingle(..) => { }, 401 | &super::Statement::CaseRange(..) => { }, 402 | _ => self.write_str("\t"), 403 | } 404 | if self.write_stmt(sn, indent) { 405 | // No semicolon+newline needed 406 | } 407 | else { 408 | self.write_str(";\n"); 409 | } 410 | } 411 | for _ in 0 .. indent { 412 | self.write_str("\t"); 413 | } 414 | self.write_str("}\n"); 415 | } 416 | fn write_stmt(&mut self, stmt: &super::Statement, indent: usize) -> bool 417 | { 418 | use super::Statement; 419 | match stmt 420 | { 421 | &Statement::Empty => { false }, 422 | &Statement::VarDef(ref vd) => { self.write_vardef(vd); false }, 423 | &Statement::Expr(ref e) => { self.write_node(e, super::NodePrecedence::Lowest); false }, 424 | &Statement::Block(ref b) => { self.write_block(b, indent+1); true }, 425 | &Statement::IfStatement { ref cond, ref true_arm, ref else_arm } => { 426 | self.write_str("if( "); 427 | self.write_defexpr(cond); 428 | self.write_str(" )\n"); 429 | self.write_block(true_arm, indent+1); 430 | if let &Some(ref ea) = else_arm { 431 | for _ in 0 .. indent+1 { 432 | self.write_str("\t"); 433 | } 434 | self.write_str("else\n"); 435 | self.write_block(ea, indent+1); 436 | } 437 | true 438 | }, 439 | &Statement::WhileLoop { ref cond, ref body } => { 440 | self.write_str("while( "); 441 | self.write_defexpr(cond); 442 | self.write_str(" )\n"); 443 | self.write_block(body, indent+1); 444 | true 445 | }, 446 | &Statement::DoWhileLoop { ref body, ref cond } => { 447 | self.write_str("do\n"); 448 | self.write_block(body, indent+1); 449 | for _ in 0 .. indent+1 { 450 | self.write_str("\t"); 451 | } 452 | self.write_str("while( "); 453 | self.write_node(cond, super::NodePrecedence::Lowest); 454 | self.write_str(" )"); 455 | false 456 | }, 457 | &Statement::ForLoop { ref init, ref cond, ref inc, ref body } => { 458 | self.write_str("for( "); 459 | if let Some(init) = init { 460 | self.write_defexpr(init); 461 | } 462 | self.write_str("; "); 463 | if let Some(cond) = cond { 464 | self.write_node(cond, super::NodePrecedence::Lowest); 465 | } 466 | self.write_str("; "); 467 | if let Some(inc) = inc { 468 | self.write_node(inc, super::NodePrecedence::Lowest); 469 | } 470 | self.write_str(" )\n"); 471 | self.write_block(body, indent+1); 472 | true 473 | }, 474 | &Statement::Continue => { self.write_str("continue"); false } 475 | &Statement::Break => { self.write_str("continue"); false } 476 | &Statement::Return(ref v) => { 477 | self.write_str("return"); 478 | if let Some(ref e) = v { 479 | self.write_str(" "); 480 | self.write_node(e, super::NodePrecedence::Lowest); 481 | } 482 | false 483 | }, 484 | &Statement::Goto(ref n) => { write!(self, "goto {}", n); false }, 485 | 486 | &Statement::Switch(ref v, ref stmts) => { 487 | self.write_str("switch "); self.write_node(v, super::NodePrecedence::CommaOperator.up()); self.write_str("\n"); 488 | self.write_block(stmts, indent+1); 489 | true 490 | }, 491 | 492 | // NOTE: Labels have negative indentation (handled by caller) 493 | &Statement::Label(ref n) => { write!(self, "{}:\n", n); true }, 494 | &Statement::CaseDefault => { self.write_str("default:\n"); true }, 495 | &Statement::CaseSingle(v) => { write!(self, "case {}:\n", v); true }, 496 | &Statement::CaseRange(v1, v2) => { write!(self, "case {} ... {}:\n", v1, v2); true }, 497 | } 498 | } 499 | fn write_vardef(&mut self, defs: &super::VarDefList) 500 | { 501 | // TODO: Ideally this would re-create the original definition line (with commas) 502 | let mut first = true; 503 | for def in defs 504 | { 505 | if !first { 506 | // TODO: THIS IS WRONG. It'll break for multiple definitions in a for loop header 507 | self.write_str("; "); 508 | } 509 | self.write_type(&def.ty, |self_| self_.write_str(&def.name)); 510 | if let Some(init) = &def.value { 511 | self.write_str(" = "); 512 | self.write_initialiser(init); // TODO: Indent level? 513 | } 514 | first = false; 515 | } 516 | } 517 | fn write_defexpr(&mut self, node: &super::ExprOrDef) 518 | { 519 | match node 520 | { 521 | &super::ExprOrDef::Expr(ref v) => self.write_node(v, super::NodePrecedence::CommaOperator.up()), 522 | &super::ExprOrDef::Definition(ref d) => self.write_vardef(d), 523 | } 524 | } 525 | fn write_node(&mut self, node: &super::Node, max_p: super::NodePrecedence) 526 | { 527 | let node_p = node.get_precedence(); 528 | // Parenthesis around values if precence is lower than a specified minimum 529 | if node_p < max_p { 530 | self.write_str("("); 531 | } 532 | use super::NodeKind; 533 | use super::{UniOp,BinOp}; 534 | match node.kind 535 | { 536 | NodeKind::StmtList(ref subnodes) => { 537 | self.write_node(&subnodes[0], node_p.down()); 538 | for sn in &subnodes[1..] { 539 | self.write_str(", "); 540 | self.write_node(sn, node_p.down()); 541 | } 542 | }, 543 | 544 | NodeKind::Identifier(ref n, _) => self.write_str(n), 545 | NodeKind::String(ref s) => write!(self, "{:?}", s), 546 | NodeKind::Integer(v, ty) => write!(self, "{} /*{:?}*/", v, ty), 547 | NodeKind::Float(v, ty) => write!(self, "{} /*{:?}*/", v, ty), 548 | 549 | NodeKind::FcnCall(ref fcn, ref values) => { 550 | self.write_node(fcn, node_p); 551 | self.write_str("("); 552 | if values.len() > 0 { 553 | self.write_node(&values[0], super::NodePrecedence::CommaOperator.up()); 554 | for sn in &values[1..] { 555 | self.write_str(", "); 556 | self.write_node(sn, super::NodePrecedence::CommaOperator.up()); 557 | } 558 | } 559 | self.write_str(")"); 560 | }, 561 | 562 | NodeKind::Assign(ref dst, ref v) => { 563 | self.write_node(dst, node_p); 564 | self.write_str(" = "); 565 | self.write_node(v, node_p); 566 | }, 567 | NodeKind::AssignOp(ref op, ref dst, ref v) => { 568 | self.write_node(dst, node_p); 569 | match op 570 | { 571 | BinOp::LogicAnd => self.write_str(" &&= "), 572 | BinOp::LogicOr => self.write_str(" ||= "), 573 | 574 | BinOp::BitAnd => self.write_str(" &= "), 575 | BinOp::BitOr => self.write_str(" |= "), 576 | BinOp::BitXor => self.write_str(" ^= "), 577 | 578 | BinOp::ShiftLeft => self.write_str(" <<= "), 579 | BinOp::ShiftRight => self.write_str(" >>= "), 580 | 581 | BinOp::CmpEqu 582 | | BinOp::CmpNEqu 583 | | BinOp::CmpLt 584 | | BinOp::CmpLtE 585 | | BinOp::CmpGt 586 | | BinOp::CmpGtE 587 | => panic!(""), 588 | 589 | BinOp::Add => self.write_str(" += "), 590 | BinOp::Sub => self.write_str(" -= "), 591 | 592 | BinOp::Mul => self.write_str(" *= "), 593 | BinOp::Div => self.write_str(" /= "), 594 | BinOp::Mod => self.write_str(" %= "), 595 | } 596 | self.write_node(v, node_p); 597 | }, 598 | 599 | NodeKind::ImplicitCast(ref ty, ref v) => { 600 | self.write_node(v, node_p); 601 | self.write_str("/*: "); 602 | self.write_type(ty, |_|{}); 603 | self.write_str("*/"); 604 | }, 605 | NodeKind::Cast(ref ty, ref v) => { 606 | self.write_str("("); 607 | self.write_type(ty, |_|{}); 608 | self.write_str(")"); 609 | self.write_node(v, node_p); 610 | }, 611 | NodeKind::SizeofType(ref ty) => { 612 | self.write_str("sizeof "); 613 | self.write_type(ty, |_|{}); 614 | }, 615 | NodeKind::SizeofExpr(ref v) => { 616 | self.write_str("sizeof("); 617 | self.write_node(v, super::NodePrecedence::CommaOperator.up()); 618 | self.write_str(")"); 619 | }, 620 | NodeKind::Intrinsic(ref name, ref tys, ref vals) => { 621 | self.write_str("__magiccall__("); 622 | write!(self, "{:?} : ", name); 623 | for v in vals { 624 | self.write_node(v, super::NodePrecedence::CommaOperator.up()); 625 | self.write_str(","); 626 | } 627 | self.write_str(" : "); 628 | for ty in tys { 629 | self.write_type(ty, |_|{}); 630 | self.write_str(","); 631 | } 632 | self.write_str(")"); 633 | }, 634 | 635 | NodeKind::Ternary(ref c, ref t, ref f) => { 636 | self.write_node(c, node_p.down()); 637 | self.write_str("?"); 638 | self.write_node(t, node_p.down()); 639 | self.write_str(":"); 640 | self.write_node(f, node_p); 641 | }, 642 | NodeKind::UniOp(ref op, ref v) => { 643 | match op 644 | { 645 | &UniOp::Neg => self.write_str("- "), 646 | &UniOp::BitNot => self.write_str("~"), 647 | &UniOp::LogicNot => self.write_str("!"), 648 | &UniOp::PreInc => self.write_str("++"), 649 | &UniOp::PreDec => self.write_str("--"), 650 | &UniOp::PostInc => { self.write_node(v, node_p); self.write_str("++"); return }, 651 | &UniOp::PostDec => { self.write_node(v, node_p); self.write_str("--"); return }, 652 | &UniOp::Address => self.write_str("&"), 653 | &UniOp::Deref => self.write_str("*"), 654 | } 655 | self.write_node(v, node_p); 656 | }, 657 | NodeKind::BinOp(ref op, ref l, ref r) => { 658 | self.write_node(l, node_p); 659 | match op 660 | { 661 | &BinOp::LogicAnd => self.write_str(" && "), 662 | &BinOp::LogicOr => self.write_str(" || "), 663 | 664 | &BinOp::BitAnd => self.write_str(" & "), 665 | &BinOp::BitOr => self.write_str(" | "), 666 | &BinOp::BitXor => self.write_str(" ^ "), 667 | 668 | &BinOp::ShiftLeft => self.write_str(" << "), 669 | &BinOp::ShiftRight => self.write_str(" >> "), 670 | 671 | &BinOp::CmpEqu => self.write_str(" == "), 672 | &BinOp::CmpNEqu => self.write_str(" != "), 673 | &BinOp::CmpLt => self.write_str(" < "), 674 | &BinOp::CmpLtE => self.write_str(" <= "), 675 | &BinOp::CmpGt => self.write_str(" > "), 676 | &BinOp::CmpGtE => self.write_str(" >= "), 677 | 678 | &BinOp::Add => self.write_str(" + "), 679 | &BinOp::Sub => self.write_str(" - "), 680 | 681 | &BinOp::Mul => self.write_str(" * "), 682 | &BinOp::Div => self.write_str(" / "), 683 | &BinOp::Mod => self.write_str(" % "), 684 | } 685 | self.write_node(r, node_p); 686 | }, 687 | NodeKind::Index(ref v, ref i) => { 688 | self.write_node(v, node_p); 689 | self.write_str("["); 690 | self.write_node(i, super::NodePrecedence::CommaOperator.up()); 691 | self.write_str("]"); 692 | }, 693 | NodeKind::DerefMember(ref v, ref n) => { 694 | self.write_node(v, node_p); 695 | self.write_str("->"); 696 | self.write_str(n); 697 | }, 698 | NodeKind::Member(ref v, ref n) => { 699 | self.write_node(v, node_p); 700 | self.write_str("."); 701 | self.write_str(n); 702 | }, 703 | } 704 | if node.get_precedence() < max_p { 705 | self.write_str(")"); 706 | } 707 | } 708 | 709 | fn write_str(&mut self, s: &str) { 710 | let _ = self.sink.write_all(s.as_bytes()); 711 | } 712 | fn write_fmt(&mut self, f: ::std::fmt::Arguments) { 713 | let _ = self.sink.write_fmt(f); 714 | } 715 | } 716 | 717 | -------------------------------------------------------------------------------- /src/codegen.rs: -------------------------------------------------------------------------------- 1 | 2 | mod cranelift; 3 | mod mrustc_mmir; 4 | 5 | pub struct Context 6 | { 7 | inner: Inner, 8 | } 9 | pub enum BackendName { 10 | Cranelift, 11 | MrustcMmir, 12 | } 13 | enum Inner { 14 | Cranelift(self::cranelift::Context), 15 | Mmir(self::mrustc_mmir::Context), 16 | } 17 | impl Context 18 | { 19 | pub fn new(backend: BackendName) -> Self 20 | { 21 | Context { 22 | inner: match backend 23 | { 24 | BackendName::Cranelift => Inner::Cranelift(self::cranelift::Context::new()), 25 | BackendName::MrustcMmir => Inner::Mmir(self::mrustc_mmir::Context::new()), 26 | } 27 | } 28 | } 29 | pub fn finish(self, sink: impl ::std::io::Write) -> Result<(), Box> 30 | { 31 | self.inner.finish(sink) 32 | } 33 | 34 | pub fn set_symbol_static(&mut self, name: &crate::ast::Ident) { 35 | self.inner.set_symbol_static(name); 36 | } 37 | /// Declare the existance of a function 38 | pub fn declare_function(&mut self, name: &crate::ast::Ident, ty: &crate::types::FunctionType) 39 | { 40 | self.inner.declare_function(name, ty) 41 | } 42 | pub fn declare_value(&mut self, name: &crate::ast::Ident, ty: &crate::types::TypeRef) 43 | { 44 | self.inner.declare_value(name, ty) 45 | } 46 | pub fn lower_value(&mut self, name: &crate::ast::Ident, ty: &crate::types::TypeRef, val: Option<&crate::ast::Initialiser>) 47 | { 48 | self.inner.lower_value(name, ty, val) 49 | } 50 | pub fn lower_function(&mut self, name: &crate::ast::Ident, ty: &crate::types::FunctionType, body: &crate::ast::FunctionBody) 51 | { 52 | self.inner.lower_function(name, ty, body) 53 | } 54 | } 55 | 56 | impl Inner { 57 | pub fn finish(self, sink: impl ::std::io::Write) -> Result<(), Box> 58 | { 59 | match self { 60 | Inner::Cranelift(i) => i.finish(sink), 61 | Inner::Mmir(i) => i.finish(sink), 62 | } 63 | } 64 | 65 | pub fn set_symbol_static(&mut self, name: &crate::ast::Ident) 66 | { 67 | match self { 68 | Inner::Cranelift(_i) => {}, 69 | Inner::Mmir(i) => i.set_symbol_static(name), 70 | } 71 | } 72 | pub fn declare_function(&mut self, name: &crate::ast::Ident, ty: &crate::types::FunctionType) 73 | { 74 | match self { 75 | Inner::Cranelift(i) => i.declare_function(name, ty), 76 | Inner::Mmir(i) => i.declare_function(name, ty), 77 | } 78 | } 79 | pub fn declare_value(&mut self, name: &crate::ast::Ident, ty: &crate::types::TypeRef) 80 | { 81 | match self { 82 | Inner::Cranelift(i) => i.declare_value(name, ty), 83 | Inner::Mmir(i) => i.declare_value(name, ty), 84 | } 85 | } 86 | pub fn lower_value(&mut self, name: &crate::ast::Ident, ty: &crate::types::TypeRef, val: Option<&crate::ast::Initialiser>) 87 | { 88 | match self { 89 | Inner::Cranelift(i) => i.lower_value(name, ty, val), 90 | Inner::Mmir(i) => i.lower_value(name, ty, val), 91 | } 92 | } 93 | pub fn lower_function(&mut self, name: &crate::ast::Ident, ty: &crate::types::FunctionType, body: &crate::ast::FunctionBody) 94 | { 95 | match self { 96 | Inner::Cranelift(i) => i.lower_function(name, ty, body), 97 | Inner::Mmir(i) => i.lower_function(name, ty, body), 98 | } 99 | } 100 | 101 | } -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | /*! 2 | * C parser (and eventual) compiler 3 | */ 4 | #[macro_use] 5 | extern crate log; 6 | extern crate env_logger; 7 | 8 | extern crate utf8reader; 9 | #[macro_use] 10 | extern crate structopt; 11 | 12 | extern crate cranelift_codegen; 13 | extern crate cranelift_frontend; 14 | extern crate cranelift_module; 15 | extern crate cranelift_object; 16 | 17 | mod preproc; 18 | mod parse; 19 | mod types; 20 | mod ast; 21 | 22 | mod codegen; 23 | mod typecheck; 24 | 25 | #[derive(StructOpt)] 26 | struct Options 27 | { 28 | #[structopt(parse(from_os_str))] 29 | input: ::std::path::PathBuf, 30 | 31 | #[structopt(short="I",parse(from_os_str))] 32 | include_dirs: Vec<::std::path::PathBuf>, 33 | 34 | /// `-D FOO=bar` 35 | #[structopt(short="D")] 36 | defines: Vec, 37 | 38 | #[structopt(short,long)] 39 | output: Option<::std::path::PathBuf>, 40 | 41 | #[structopt(long)] 42 | dump_ast: Option<::std::path::PathBuf>, 43 | 44 | #[structopt(long="emit-mmir")] 45 | emit_mmir: bool, 46 | } 47 | 48 | fn main() 49 | { 50 | env_logger::init(); 51 | 52 | // 1. Parse command line arguments 53 | let args: Options = ::structopt::StructOpt::from_args(); 54 | 55 | let mut program = ::ast::Program::new(); 56 | 57 | // - Parse into the AST 58 | match ::parse::parse(&mut program, &args.input, args.include_dirs, &args.defines) 59 | { 60 | Err(e) => { 61 | panic!("Error parsing file - {}", e); 62 | }, 63 | Ok(_) => {} 64 | } 65 | 66 | if false 67 | { 68 | let ofp = ::std::fs::File::create("DUMP_parsed.c").unwrap(); 69 | ::ast::pretty_print::write(ofp, &program); 70 | } 71 | 72 | // TODO: Type check/annotate? 73 | // - Need to annotate types and implicit conversion positions 74 | // > All node types 75 | // > Add promotions/demotions/conversions 76 | // > Annotate ternary with if it's in LValue position 77 | // > Collate variables and determine if they're addressed 78 | for (name,ty,sym) in program.iter_symbols() 79 | { 80 | match sym 81 | { 82 | ast::SymbolValue::Code(ref fcn) => { 83 | let fcn_ty = match ty.basetype 84 | { 85 | crate::types::BaseType::Function(ref fcn_ty) => fcn_ty, 86 | _ => panic!("ERROR: Code without a function type"), 87 | }; 88 | typecheck::handle_function(&program, name, fcn_ty, &mut fcn.borrow_mut()) 89 | }, 90 | ast::SymbolValue::Value(ref init) => { 91 | typecheck::handle_global(&program, name, ty, init.borrow_mut().as_mut()); 92 | }, 93 | } 94 | } 95 | 96 | // Convert to cranelift? 97 | if let Some(output_path) = args.output 98 | { 99 | let mut c = codegen::Context::new(if args.emit_mmir { codegen::BackendName::MrustcMmir } else { codegen::BackendName::Cranelift }); 100 | for (vis, name,ty,sym) in program.iter_symbols_with_prototypes() 101 | { 102 | if let ast::Visibility::Static = vis { 103 | c.set_symbol_static(name); 104 | } 105 | match sym 106 | { 107 | None => match ty.basetype 108 | { 109 | crate::types::BaseType::Function(ref fcn_ty) => c.declare_function(name, fcn_ty), 110 | _ => c.declare_value(name, ty), 111 | }, 112 | Some(ast::SymbolValue::Code(ref fcn)) => { 113 | let fcn_ty = match ty.basetype 114 | { 115 | crate::types::BaseType::Function(ref fcn_ty) => fcn_ty, 116 | _ => panic!("ERROR: Code without a function type"), 117 | }; 118 | c.lower_function(name, fcn_ty, &fcn.borrow()); 119 | }, 120 | Some(ast::SymbolValue::Value(ref init)) => { 121 | c.lower_value(name, ty, init.borrow().as_ref()); 122 | }, 123 | } 124 | } 125 | c.finish(get_output(&output_path)).unwrap(); 126 | } 127 | 128 | // Dump AST 129 | if let Some(output_path) = args.dump_ast 130 | { 131 | ::ast::pretty_print::write(get_output(&output_path), &program); 132 | } 133 | } 134 | 135 | fn get_output(output_path: &::std::path::Path) -> Box { 136 | if output_path == ::std::path::Path::new("-") { 137 | Box::new(::std::io::stdout().lock()) 138 | } 139 | else { 140 | match std::fs::File::create(output_path) 141 | { 142 | Ok(v) => Box::new(v), 143 | Err(e) => panic!("Unable to open `{}` for writing: {}", output_path.display(),e), 144 | } 145 | } 146 | } 147 | 148 | // vim: ft=rust 149 | -------------------------------------------------------------------------------- /src/parse/expr.rs: -------------------------------------------------------------------------------- 1 | //! Expression parsing 2 | //! `ParseState::parse_expr` - Returns an AST expression node, parsed from the token stream 3 | use parse::Token; 4 | use parse::ParseResult; 5 | 6 | /// Shortcut for creating a new node 7 | macro_rules! node { 8 | ( $span:expr, $ty:ident $($args:tt)* ) => { 9 | crate::ast::Node::new( $span, crate::ast::NodeKind::$ty $($args)* ) 10 | }; 11 | } 12 | 13 | // Parse, left associative 14 | macro_rules! parse_left_assoc 15 | { 16 | ( $(#[$a:meta])* $_self:ident, $name:ident, $next:ident, $sp:ident, $rv:ident, { $($patterns:pat => $vals:expr),*, }) => { 17 | $(#[$a])* 18 | fn $name(&mut $_self) -> ParseResult<::ast::Node> { 19 | let $sp = $_self.lex.point_span(); 20 | let mut $rv = $_self.$next()?; 21 | loop 22 | { 23 | $rv = match $_self.lex.get_token()? 24 | { 25 | $($patterns => { let $sp = $sp.clone(); $vals }),*, 26 | t @ _ => { 27 | $_self.lex.put_back(t); 28 | break; 29 | } 30 | }; 31 | } 32 | Ok($rv) 33 | } 34 | } 35 | } 36 | 37 | impl<'ast> super::ParseState<'ast> 38 | { 39 | // ---------------------------------------------------------------- 40 | // Expressions! 41 | // ---------------------------------------------------------------- 42 | /// Parse a single expression 43 | pub fn parse_expr(&mut self) -> ParseResult<::ast::Node> 44 | { 45 | return self.parse_expr_0(); 46 | } 47 | /// Parse a sequence of expressions (comma-separated) 48 | pub(super) fn parse_expr_list(&mut self) -> ParseResult<::ast::Node> 49 | { 50 | let sp = self.lex.point_span(); 51 | let exp = self.parse_expr()?; 52 | if peek_token_nc!(self.lex, Token::Comma) 53 | { 54 | let mut exprs = vec![exp]; 55 | while peek_token!(self.lex, Token::Comma) 56 | { 57 | exprs.push( self.parse_expr()? ); 58 | } 59 | Ok(node!( sp, StmtList(exprs) )) 60 | } 61 | else 62 | { 63 | Ok(exp) 64 | } 65 | } 66 | 67 | /// Parse #0 : Assignment 68 | fn parse_expr_0(&mut self) -> ParseResult<::ast::Node> 69 | { 70 | let sp = self.lex.point_span(); 71 | let rv = self.parse_expr_1()?; 72 | Ok( match self.lex.get_token()? 73 | { 74 | Token::Assign => node!( sp, Assign(Box::new(rv), Box::new(self.parse_expr_0()?)) ), 75 | Token::AssignBitAnd => node!( sp, AssignOp(::ast::BinOp::BitAnd, Box::new(rv), Box::new(self.parse_expr_0()?)) ), 76 | Token::AssignBitOr => node!( sp, AssignOp(::ast::BinOp::BitOr, Box::new(rv), Box::new(self.parse_expr_0()?)) ), 77 | Token::AssignBitXor => node!( sp, AssignOp(::ast::BinOp::BitXor, Box::new(rv), Box::new(self.parse_expr_0()?)) ), 78 | Token::AssignAdd => node!( sp, AssignOp(::ast::BinOp::Add, Box::new(rv), Box::new(self.parse_expr_0()?)) ), 79 | Token::AssignSub => node!( sp, AssignOp(::ast::BinOp::Sub, Box::new(rv), Box::new(self.parse_expr_0()?)) ), 80 | Token::AssignMul => node!( sp, AssignOp(::ast::BinOp::Mul, Box::new(rv), Box::new(self.parse_expr_0()?)) ), 81 | Token::AssignDiv => node!( sp, AssignOp(::ast::BinOp::Div, Box::new(rv), Box::new(self.parse_expr_0()?)) ), 82 | Token::AssignMod => node!( sp, AssignOp(::ast::BinOp::Mod, Box::new(rv), Box::new(self.parse_expr_0()?)) ), 83 | Token::AssignShiftLeft => node!( sp, AssignOp(::ast::BinOp::ShiftLeft , Box::new(rv), Box::new(self.parse_expr_0()?)) ), 84 | Token::AssignShiftRight => node!( sp, AssignOp(::ast::BinOp::ShiftRight, Box::new(rv), Box::new(self.parse_expr_0()?)) ), 85 | t @ _ => { 86 | self.lex.put_back(t); 87 | rv 88 | } 89 | }) 90 | } 91 | 92 | /// Expression #1 - Ternary 93 | fn parse_expr_1(&mut self) -> ParseResult<::ast::Node> 94 | { 95 | let sp = self.lex.point_span(); 96 | let rv = self.parse_expr_2()?; 97 | 98 | Ok( match self.lex.get_token()? 99 | { 100 | Token::QuestionMark => { 101 | debug!("Ternary, rv (cnd) = {:?}", rv); 102 | let tv = Box::new(self.parse_expr_1()?); 103 | debug!("Ternary - tv = {:?}", tv); 104 | syntax_assert!(self.lex.get_token()?, Token::Colon); 105 | let fv = Box::new(self.parse_expr_1()?); 106 | debug!("Ternary - fv = {:?}", fv); 107 | node!( sp, Ternary(Box::new(rv), tv, fv) ) 108 | } 109 | t @ _ => { 110 | self.lex.put_back(t); 111 | rv 112 | } 113 | }) 114 | } 115 | 116 | parse_left_assoc!{ 117 | /// Expression #2 - Boolean AND/OR 118 | self, parse_expr_2, parse_expr_3, sp, rv, { 119 | Token::DoublePipe => node!( sp, BinOp(::ast::BinOp::LogicOr, Box::new(rv), Box::new(self.parse_expr_3()?)) ), 120 | Token::DoubleAmpersand => node!( sp, BinOp(::ast::BinOp::LogicAnd, Box::new(rv), Box::new(self.parse_expr_3()?)) ), 121 | }} 122 | 123 | parse_left_assoc!{ 124 | /// Expresission #3 - Bitwise 125 | self, parse_expr_3, parse_expr_4, sp, rv, { 126 | Token::Pipe => node!( sp, BinOp(::ast::BinOp::BitOr , Box::new(rv), Box::new(self.parse_expr_4()?)) ), 127 | Token::Ampersand => node!( sp, BinOp(::ast::BinOp::BitAnd, Box::new(rv), Box::new(self.parse_expr_4()?)) ), 128 | Token::Caret => node!( sp, BinOp(::ast::BinOp::BitXor, Box::new(rv), Box::new(self.parse_expr_4()?)) ), 129 | }} 130 | 131 | parse_left_assoc!{ 132 | /// Expression #4 - Comparison Operators 133 | self, parse_expr_4, parse_expr_5, sp, rv, { 134 | Token::Equality => node!( sp, BinOp(::ast::BinOp::CmpEqu , Box::new(rv), Box::new(self.parse_expr_5()?)) ), 135 | Token::NotEquals => node!( sp, BinOp(::ast::BinOp::CmpNEqu, Box::new(rv), Box::new(self.parse_expr_5()?)) ), 136 | Token::Lt => node!( sp, BinOp(::ast::BinOp::CmpLt, Box::new(rv), Box::new(self.parse_expr_5()?)) ), 137 | Token::LtE => node!( sp, BinOp(::ast::BinOp::CmpLtE, Box::new(rv), Box::new(self.parse_expr_5()?)) ), 138 | Token::Gt => node!( sp, BinOp(::ast::BinOp::CmpGt, Box::new(rv), Box::new(self.parse_expr_5()?)) ), 139 | Token::GtE => node!( sp, BinOp(::ast::BinOp::CmpGtE, Box::new(rv), Box::new(self.parse_expr_5()?)) ), 140 | }} 141 | 142 | parse_left_assoc!{ 143 | /// Expression #5 - Bit Shifts 144 | self, parse_expr_5, parse_expr_6, sp, rv, { 145 | Token::ShiftLeft => node!( sp, BinOp(::ast::BinOp::ShiftLeft, Box::new(rv), Box::new(self.parse_expr_6()?)) ), 146 | Token::ShiftRight => node!( sp, BinOp(::ast::BinOp::ShiftRight, Box::new(rv), Box::new(self.parse_expr_6()?)) ), 147 | }} 148 | 149 | parse_left_assoc!{ 150 | /// Expresion #6 - Arithmatic 151 | self, parse_expr_6, parse_expr_7, sp, rv, { 152 | Token::Plus => node!( sp, BinOp(::ast::BinOp::Add, Box::new(rv), Box::new(self.parse_expr_7()?)) ), 153 | Token::Minus => node!( sp, BinOp(::ast::BinOp::Sub, Box::new(rv), Box::new(self.parse_expr_7()?)) ), 154 | }} 155 | 156 | parse_left_assoc!{ 157 | /// Expression #7 - Multiply/Divide 158 | self, parse_expr_7, parse_expr_8, sp, rv, { 159 | Token::Star => node!( sp, BinOp(::ast::BinOp::Mul, Box::new(rv), Box::new(self.parse_expr_8()?)) ), 160 | Token::Slash => node!( sp, BinOp(::ast::BinOp::Div, Box::new(rv), Box::new(self.parse_expr_8()?)) ), 161 | Token::Percent => node!( sp, BinOp(::ast::BinOp::Mod, Box::new(rv), Box::new(self.parse_expr_8()?)) ), 162 | }} 163 | 164 | //parse_left_assoc!{ 165 | // /// Expression #8 - Unary Righthand 166 | // self, parse_expr_8, parse_expr_9, sp, rv, { 167 | //}} 168 | 169 | /// Expression #9 - Unary left 170 | fn parse_expr_8(&mut self) -> ParseResult<::ast::Node> 171 | { 172 | let sp = self.lex.point_span(); 173 | Ok( match self.lex.get_token()? 174 | { 175 | Token::Minus => node!( sp, UniOp(::ast::UniOp::Neg, Box::new(self.parse_expr_8()?)) ), 176 | Token::Tilde => node!( sp, UniOp(::ast::UniOp::BitNot, Box::new(self.parse_expr_8()?)) ), 177 | Token::Exclamation => node!( sp, UniOp(::ast::UniOp::LogicNot, Box::new(self.parse_expr_8()?)) ), 178 | Token::DoublePlus => node!( sp, UniOp(::ast::UniOp::PreInc, Box::new(self.parse_expr_8()?)) ), 179 | Token::DoubleMinus => node!( sp, UniOp(::ast::UniOp::PreDec, Box::new(self.parse_expr_8()?)) ), 180 | Token::Star => node!( sp, UniOp(::ast::UniOp::Deref, Box::new(self.parse_expr_8()?)) ), 181 | Token::Ampersand => node!( sp, UniOp(::ast::UniOp::Address, Box::new(self.parse_expr_member()?)) ), // different, as double addr is inval 182 | t @ _ => { 183 | self.lex.put_back(t); 184 | let rv = self.parse_expr_member()?; 185 | match self.lex.get_token()? 186 | { 187 | Token::DoublePlus => node!( sp, UniOp(::ast::UniOp::PostInc, Box::new(rv)) ), 188 | Token::DoubleMinus => node!( sp, UniOp(::ast::UniOp::PostDec, Box::new(rv)) ), 189 | t => { 190 | self.lex.put_back(t); 191 | rv 192 | } 193 | } 194 | }, 195 | }) 196 | } 197 | 198 | 199 | parse_left_assoc!{ 200 | /// Expression - Member access 201 | self, parse_expr_member, parse_expr_p, sp, rv, { 202 | Token::DerefMember => node!( sp, DerefMember(Box::new(rv), syntax_assert!(self.lex => Token::Ident(i) @ i)) ), 203 | Token::Period => node!( sp, Member( Box::new(rv), syntax_assert!(self.lex => Token::Ident(i) @ i)) ), 204 | Token::SquareOpen => { 205 | let idx = self.parse_expr()?; 206 | syntax_assert!(self.lex => Token::SquareClose); 207 | node!( sp, Index(Box::new(rv), Box::new(idx)) ) 208 | }, 209 | Token::ParenOpen => { 210 | let mut args = Vec::new(); 211 | if ! peek_token!(self.lex, Token::ParenClose) 212 | { 213 | loop 214 | { 215 | args.push( self.parse_expr()? ); 216 | if peek_token!(self.lex, Token::ParenClose) { 217 | break; 218 | } 219 | syntax_assert!(self.lex => Token::Comma); 220 | } 221 | } 222 | node!( sp, FcnCall(Box::new(rv), args) ) 223 | }, 224 | }} 225 | 226 | /// Expression - Parens 227 | fn parse_expr_p(&mut self) -> ParseResult<::ast::Node> 228 | { 229 | let sp = self.lex.point_span(); 230 | Ok(match self.lex.get_token()? 231 | { 232 | // - Either a cast, or a grouped expression 233 | Token::ParenOpen => match self.get_base_type_opt()? 234 | { 235 | Some((_sc, basetype)) => { 236 | let (fulltype, ident) = self.get_full_type(basetype)?; 237 | if ident != "" { 238 | syntax_error!("Unexpected identifier in cast"); 239 | } 240 | syntax_assert!(self.lex => Token::ParenClose); 241 | // Handle `(struct Foo){...}` expressions 242 | if peek_token_nc!(self.lex, Token::BraceOpen) { 243 | self.lex.point_span().todo(format_args!("Handle inline init syntax `(struct Foo){{ ... }}`")); 244 | } 245 | node!( sp, Cast(fulltype, Box::new(self.parse_expr_8()?)) ) 246 | }, 247 | None => { 248 | let rv = self.parse_expr_list()?; 249 | syntax_assert!(self.lex => Token::ParenClose); 250 | rv 251 | }, 252 | }, 253 | t @ _ => { 254 | self.lex.put_back(t); 255 | self.parse_expr_z()? 256 | } 257 | }) 258 | } 259 | /// Expression - Leaf nodes 260 | fn parse_expr_z(&mut self) -> ParseResult<::ast::Node> 261 | { 262 | let sp = self.lex.point_span(); 263 | Ok(match self.lex.get_token()? 264 | { 265 | Token::Ident(ref id) if id == "__magiccall__" => { 266 | syntax_assert!(self.lex => Token::ParenOpen); 267 | let name = syntax_assert!(self.lex => Token::String(s) @ s); 268 | let mut vals = Vec::new(); 269 | if peek_token!(self.lex, Token::Colon) 270 | { 271 | loop 272 | { 273 | if peek_token_nc!(self.lex, Token::ParenClose) || peek_token_nc!(self.lex, Token::Colon) { 274 | break; 275 | } 276 | 277 | let v = Box::new(self.parse_expr_0()?); 278 | vals.push(v); 279 | 280 | if !peek_token!(self.lex, Token::Comma) { 281 | break; 282 | } 283 | } 284 | } 285 | let mut tys = Vec::new(); 286 | if peek_token!(self.lex, Token::Colon) 287 | { 288 | loop 289 | { 290 | if peek_token_nc!(self.lex, Token::ParenClose) { 291 | break; 292 | } 293 | 294 | let (_sc, base_ty) = self.get_base_type()?; 295 | let (ty, name) = self.get_full_type( base_ty )?; 296 | if ! name.is_empty() { 297 | syntax_error!("Unexpected name in magic call"); 298 | } 299 | tys.push(ty); 300 | 301 | if !peek_token!(self.lex, Token::Comma) { 302 | break; 303 | } 304 | } 305 | } 306 | syntax_assert!(self.lex => Token::ParenClose); 307 | node!( sp, Intrinsic(name, tys, vals) ) 308 | }, 309 | Token::Ident(ref id) if id == "__func__" => { 310 | node!( sp, String(format!("dunno")) ) 311 | }, 312 | Token::Ident(id) => { 313 | use crate::ast::IdentRef; 314 | // TODO: Look up the name here? (It's where it should be done...) 315 | let b = if let Some( (e,i) ) = self.ast.find_enum_var(&id) { 316 | Some(IdentRef::Enum(e, i)) 317 | } 318 | else if let Some(v) = self.ast.get_symbol(&id) { 319 | if let crate::types::BaseType::Function(_) = v.symtype.basetype { 320 | // Special type for functions, as they have strange typecheck decay rules 321 | Some(IdentRef::Function) 322 | } 323 | else { 324 | Some(IdentRef::StaticItem) 325 | } 326 | } 327 | else { 328 | None 329 | }; 330 | node!( sp, Identifier(id, b) ) 331 | }, 332 | Token::String(s) => { 333 | let mut val = s; 334 | loop 335 | { 336 | match self.lex.get_token()? { 337 | Token::String(s) => { val.push_str(&s); }, 338 | t @ _ => { self.lex.put_back(t); break; } 339 | } 340 | } 341 | node!( sp, String(val) ) 342 | }, 343 | Token::Integer(v,cls,_) => node!( sp, Integer(v, cls) ), 344 | Token::Character(v) => node!( sp, Integer(v, crate::types::IntClass::char()) ), 345 | Token::Float(v,cls,_) => node!( sp, Float(v, cls) ), 346 | Token::Rword_sizeof => { 347 | let expect_paren = peek_token!(self.lex, Token::ParenOpen); 348 | let rv = match self.get_base_type_opt()? 349 | { 350 | Some((_sc,t)) => { 351 | let (tr, name) = self.get_full_type(t)?; 352 | if ! name.is_empty() { 353 | syntax_error!("Unexpected name in sizeof"); 354 | } 355 | node!( sp, SizeofType(tr) ) 356 | }, 357 | None => { 358 | let val = if expect_paren { self.parse_expr_0()? } else { self.parse_expr_p()? }; 359 | node!( sp, SizeofExpr(Box::new(val)) ) 360 | }, 361 | }; 362 | if expect_paren { 363 | syntax_assert!(self.lex => Token::ParenClose); 364 | } 365 | debug!("{:?}, lex={}", rv, self.lex); 366 | rv 367 | }, 368 | t @ _ => syntax_error!("Unexpected {:?}, expected value", t), 369 | }) 370 | } 371 | } 372 | -------------------------------------------------------------------------------- /src/parse/mod.rs: -------------------------------------------------------------------------------- 1 | /*! 2 | * C Parser, containing the lexer, pre-processor, and AST generation 3 | * 4 | * See `self::parse` for the top-level parser function 5 | */ 6 | 7 | // Root parsing function 8 | pub use crate::preproc::Token; 9 | 10 | macro_rules! syntax_error 11 | { 12 | ($msg:expr) => ({ return Err(::parse::Error::SyntaxError(format!("{}",$msg))) }); 13 | ($fmt:expr, $($arg:tt)*) => ({ return Err(::parse::Error::SyntaxError(format!($fmt, $($arg)*))) }); 14 | } 15 | macro_rules! syntax_assert 16 | { 17 | ($lex:expr => Token::$exp:ident) => (syntax_assert!($lex.get_token()?, Token::$exp)); 18 | ($lex:expr => Token::$exp:ident($($a:ident),+) @ $v:expr) => (syntax_assert!($lex.get_token()?, Token::$exp($($a),+) => $v)); 19 | ($tok:expr, Token::$exp:ident) => (match $tok { 20 | Token::$exp => {}, 21 | tok @ _ => syntax_error!("Unexpected token {:?}, expected {:?}", tok, stringify!($exp)), 22 | }); 23 | ($tok:expr, Token::$exp:ident($($a:ident),+) => $v:expr) => (match $tok { 24 | Token::$exp($($a),+) => $v, 25 | tok @ _ => syntax_error!("Unexpected token {:?}, expected {:?}", tok, stringify!($exp)), 26 | }) 27 | } 28 | macro_rules! peek_token_nc 29 | { 30 | ($lex:expr, $tok:pat) => ({ 31 | let lex = &mut $lex; 32 | match lex.get_token()? { 33 | tok @ $tok => { lex.put_back(tok); true }, 34 | tok @ _ => { lex.put_back(tok); false } 35 | } 36 | }) 37 | } 38 | macro_rules! peek_token 39 | { 40 | ($lex:expr, $tok:pat) => ({ 41 | let lex = &mut $lex; 42 | match lex.get_token()? { 43 | $tok => true, 44 | tok @ _ => { lex.put_back(tok); false } 45 | } 46 | }); 47 | ($lex:expr, $tok:pat if $cond:expr) => ({ 48 | let lex = &mut $lex; 49 | match lex.get_token()? { 50 | $tok if $cond => true, 51 | tok @ _ => { lex.put_back(tok); false } 52 | } 53 | }); 54 | } 55 | macro_rules! parse_todo 56 | { 57 | ($str:expr) => (return Err(::parse::Error::Todo($str))) 58 | } 59 | 60 | mod parsing; 61 | mod expr; 62 | mod types; 63 | 64 | #[derive(Debug)] 65 | pub enum Error 66 | { 67 | Todo(&'static str), 68 | EOF, 69 | IOError(::std::io::Error), 70 | BadCharacter(char), 71 | SyntaxError(String), 72 | } 73 | impl From<::preproc::Error> for Error 74 | { 75 | fn from(v: ::preproc::Error) -> Self 76 | { 77 | match v 78 | { 79 | ::preproc::Error::EOF => Error::EOF, 80 | ::preproc::Error::IoError(e) => Error::IOError(e), 81 | ::preproc::Error::UnexpectedEof => Error::SyntaxError(format!("Unexpected EOF in preprocessor")), 82 | ::preproc::Error::MalformedLiteral(v) => Error::SyntaxError(format!("Malformed literal: {}", v)), 83 | ::preproc::Error::BadCharacter(c) => Error::BadCharacter(c), 84 | } 85 | } 86 | } 87 | impl ::std::fmt::Display for Error 88 | { 89 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 90 | match self { 91 | Error::Todo(msg) => write!(f, "TODO: {}", msg), 92 | Error::EOF => f.write_str("Unexpected EOF"), 93 | Error::IOError(error) => write!(f, "IO Error: {}", error), 94 | Error::BadCharacter(ch) => write!(f, "Unexpected character: {:?}", ch), 95 | Error::SyntaxError(msg) => write!(f, "Syntax Error: {}", msg), 96 | } 97 | } 98 | } 99 | 100 | pub type ParseResult = Result; 101 | 102 | struct ParseState<'ast> 103 | { 104 | ast: &'ast mut ::ast::Program, 105 | lex: ::preproc::Preproc, 106 | } 107 | 108 | /// Parse a file into the passed AST program representation 109 | pub fn parse(ast: &mut ::ast::Program, filename: &::std::path::Path, include_paths: Vec<::std::path::PathBuf>, defines: &[String]) -> ParseResult<()> 110 | { 111 | let pp_opts = { 112 | let mut pp_opts = super::preproc::Options::default(); 113 | pp_opts.include_paths = include_paths; 114 | pp_opts 115 | }; 116 | 117 | let mut self_ = ParseState { 118 | ast: ast, 119 | lex: ::preproc::Preproc::new( Some(filename), pp_opts )?, 120 | }; 121 | 122 | // Process command-line `-D` arguments 123 | self_.lex.parse_define_str("__RCC__").unwrap(); 124 | for d in defines 125 | { 126 | self_.lex.parse_define_str(d)?; 127 | } 128 | 129 | match self_.parseroot() 130 | { 131 | Ok(o) => Ok(o), 132 | Err(e) => { 133 | error!("Parse error {:?} at {}", e, self_.lex); 134 | match e 135 | { 136 | ::parse::Error::SyntaxError(s) => Err( ::parse::Error::SyntaxError(format!("{} {}", self_.lex, s)) ), 137 | _ => Err( e ), 138 | } 139 | } 140 | } 141 | } 142 | 143 | // vim: ft=rust 144 | 145 | -------------------------------------------------------------------------------- /src/parse/parsing.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Top-level parser 3 | */ 4 | use parse::Token; 5 | use parse::ParseResult; 6 | 7 | impl<'ast> super::ParseState<'ast> 8 | { 9 | pub(super) fn parseroot(&mut self) -> ParseResult<()> 10 | { 11 | loop 12 | { 13 | let tok = self.lex.get_token()?; 14 | match tok 15 | { 16 | Token::EOF => { 17 | break; 18 | }, 19 | Token::Semicolon => {}, 20 | Token::Rword_typedef => { 21 | let (_sc, basetype) = self.get_base_type()?; 22 | debug!("do_typedef: basetype={:?}", basetype); 23 | let (typeid, name) = self.get_full_type(basetype)?; 24 | self.ast.set_typedef(name, typeid); 25 | syntax_assert!( self.lex.get_token()?, Token::Semicolon ); 26 | }, 27 | _ => { 28 | self.lex.put_back(tok); 29 | self.do_definition()? 30 | }, 31 | } 32 | debug!("--- {}", self.lex); 33 | } 34 | debug!("=== {}", self.lex); 35 | 36 | Ok( () ) 37 | } 38 | 39 | /// Parse the body of a GCC `__attribute__(...)` 40 | pub(super) fn parse_gcc_attributes(&mut self, mut cb: impl FnMut(&mut Self, String, Vec)->ParseResult<()>) -> ParseResult<()> 41 | { 42 | syntax_assert!( self.lex.get_token()?, Token::ParenOpen ); 43 | let is_double_wrapped = peek_token!(self.lex, Token::ParenOpen); 44 | loop { 45 | let name = syntax_assert!( self.lex.get_token()?, Token::Ident(n) => n ); 46 | let opts = if peek_token!(self.lex, Token::ParenOpen) { 47 | let mut toks = vec![]; 48 | let mut depth = 0; 49 | loop 50 | { 51 | let t = self.lex.get_token()?; 52 | match t 53 | { 54 | Token::ParenOpen => depth += 1, 55 | Token::ParenClose if depth == 0 => break toks, 56 | Token::ParenClose => depth -= 1, 57 | _ => {}, 58 | } 59 | toks.push(t); 60 | } 61 | } 62 | else { 63 | vec![] 64 | }; 65 | cb(self, name, opts)?; 66 | if ! peek_token!(self.lex, Token::Comma) { 67 | break; 68 | } 69 | } 70 | if is_double_wrapped { 71 | syntax_assert!( self.lex.get_token()?, Token::ParenClose ); 72 | } 73 | syntax_assert!( self.lex.get_token()?, Token::ParenClose ); 74 | Ok( () ) 75 | } 76 | 77 | fn do_definition(&mut self) -> ParseResult<()> 78 | { 79 | let span = self.lex.point_span(); 80 | // 1. Get base type 81 | let (extra, basetype) = self.get_base_type()?; 82 | debug!("do_definition: basetype={:?}", basetype); 83 | // 2. Get extended type and identifier 84 | let (typeid, ident) = self.get_full_type(basetype.clone())?; 85 | // - if the ident is non-empty, parse the variable definition. 86 | if ident != "" 87 | { 88 | // 3. Check for a: Semicolon, Comma, Open Brace, or Assignment 89 | match self.lex.get_token()? 90 | { 91 | // NOTE: The following would need changes for C++11 array literals 92 | Token::BraceOpen => return self.parse_function(span, extra, typeid, ident), 93 | tok @ _ => { 94 | self.lex.put_back(tok); 95 | 96 | self.parse_variable_def(span, extra.clone(), typeid, ident)?; 97 | return self.parse_variable_list(extra, basetype) 98 | } 99 | } 100 | } 101 | else 102 | { 103 | syntax_assert!( self.lex.get_token()?, Token::Semicolon ); 104 | return Ok( () ); 105 | } 106 | } 107 | } 108 | 109 | // --- 110 | // Function bodies 111 | // --- 112 | impl<'ast> super::ParseState<'ast> 113 | { 114 | fn parse_function(&mut self, span: crate::ast::Span, extra: crate::parse::types::BaseTypeExtra, typeid: ::types::TypeRef, ident: String) -> ParseResult<()> 115 | { 116 | // TODO: Store function name for `__func__` 117 | let code = self.parse_block()?; 118 | debug!("parse_function: code = {:?}", code); 119 | self.ast.define_function(span, extra.is_inline, extra.storage_class, typeid, ident, Some(code)); 120 | Ok( () ) 121 | } 122 | 123 | fn parse_opt_block(&mut self) -> ParseResult<::ast::Block> 124 | { 125 | let exprs = self.parse_block_line()?; 126 | if let ::ast::Statement::Block(b) = exprs { 127 | Ok( b ) 128 | } 129 | else { 130 | Ok( vec![ exprs ] ) 131 | } 132 | } 133 | 134 | fn parse_block(&mut self) -> ParseResult<::ast::Block> 135 | { 136 | // Opening brace has been eaten 137 | let mut statements = Vec::new(); 138 | 139 | while !peek_token!(self.lex, Token::BraceClose) 140 | { 141 | let s = self.parse_block_line()?; 142 | debug!("> {:?}", s); 143 | statements.push( s ); 144 | } 145 | 146 | Ok( statements ) 147 | } 148 | 149 | fn try_parse_local_var(&mut self) -> ParseResult>> 150 | { 151 | let sp = self.lex.point_span(); 152 | Ok(match self.get_base_type_opt()? 153 | { 154 | Some((extra, basetype)) => { 155 | debug!("try_parse_local_var - basetype={:?}", basetype); 156 | // Definition! 157 | let (typeid, ident) = self.get_full_type(basetype.clone())?; 158 | let rv = if ident != "" 159 | { 160 | // 3. Check for a: Semicolon, Comma, Open Brace, or Assignment 161 | if peek_token!(self.lex, Token::BraceOpen) 162 | { 163 | // NOTE: The following would need changes for C++11 struct initialisation 164 | self.parse_function(sp, extra, typeid, ident)?; 165 | return Ok(Some(vec![])); 166 | //parse_todo!("Nested functions"); 167 | } 168 | // If the storage class is specified as `static`, it's a "global" 169 | else if let Some(crate::types::StorageClass::Static|crate::types::StorageClass::Extern) = extra.storage_class 170 | { 171 | self.parse_variable_def(sp, extra.clone(), typeid, ident)?; 172 | self.parse_variable_list(extra, basetype)?; 173 | return Ok(Some(vec![])); 174 | } 175 | else 176 | { 177 | let init = self.parse_var_init()?; 178 | let mut rv = vec![ ::ast::VariableDefinition { span: sp.clone(), ty: typeid, name: ident, index: None, value: init } ]; 179 | while peek_token!(self.lex, Token::Comma) 180 | { 181 | let (typeid, ident) = self.get_full_type(basetype.clone())?; 182 | let init = self.parse_var_init()?; 183 | 184 | rv.push( ::ast::VariableDefinition { span: sp.clone(), ty: typeid, name: ident, index: None, value: init } ); 185 | } 186 | Some(rv) 187 | } 188 | } 189 | else 190 | { 191 | Some(vec![]) 192 | }; 193 | rv 194 | }, 195 | None => None 196 | }) 197 | } 198 | fn parse_var_init(&mut self) -> ParseResult> 199 | { 200 | Ok(if !peek_token!(self.lex, Token::Assign) { 201 | // No assignment 202 | None 203 | } else if !peek_token!(self.lex, Token::BraceOpen) { 204 | // `=` ... 205 | Some( ::ast::Initialiser::Value( self.parse_expr()? ) ) 206 | } 207 | else { 208 | // `=` `{` 209 | Some( self.parse_composite_lit()? ) 210 | }) 211 | } 212 | 213 | /// Parse a single line in a block 214 | fn parse_block_line(&mut self) -> ParseResult<::ast::Statement> 215 | { 216 | debug!(">>> {}", self.lex); 217 | // Attempt to get a type, returns None if no type was present 218 | Ok(match self.try_parse_local_var()? 219 | { 220 | Some(n) => { 221 | if !n.is_empty() { 222 | syntax_assert!(self.lex.get_token()?, Token::Semicolon); 223 | } 224 | ::ast::Statement::VarDef(n) 225 | }, 226 | None => match self.lex.get_token()? 227 | { 228 | Token::Semicolon => ::ast::Statement::Empty, 229 | Token::BraceOpen => ::ast::Statement::Block( self.parse_block()? ), 230 | Token::Rword_return => if peek_token!(self.lex, Token::Semicolon) { 231 | ::ast::Statement::Return( None ) 232 | } else { 233 | let rv = ::ast::Statement::Return( Some(self.parse_expr_list()?) ); 234 | syntax_assert!(self.lex.get_token()?, Token::Semicolon); 235 | rv 236 | }, 237 | Token::Rword_break => { 238 | syntax_assert!(self.lex.get_token()?, Token::Semicolon); 239 | ::ast::Statement::Break 240 | }, 241 | Token::Rword_continue => { 242 | syntax_assert!(self.lex.get_token()?, Token::Semicolon); 243 | ::ast::Statement::Continue 244 | }, 245 | Token::Rword_goto => { 246 | let dest = syntax_assert!(self.lex => Token::Ident(s) @ s); 247 | syntax_assert!(self.lex => Token::Semicolon); 248 | ::ast::Statement::Goto(dest) 249 | }, 250 | Token::Rword_while => { 251 | syntax_assert!(self.lex => Token::ParenOpen); 252 | let cnd = self.parse_expr_list()?; 253 | syntax_assert!(self.lex => Token::ParenClose); 254 | let code = self.parse_opt_block()?; 255 | ::ast::Statement::WhileLoop { 256 | cond: ::ast::ExprOrDef::Expr(cnd), 257 | body: code, 258 | } 259 | }, 260 | Token::Rword_do => { 261 | let code = self.parse_opt_block()?; 262 | syntax_assert!(self.lex => Token::Rword_while); 263 | syntax_assert!(self.lex => Token::ParenOpen); 264 | let cnd = self.parse_expr_list()?; 265 | syntax_assert!(self.lex => Token::ParenClose); 266 | syntax_assert!(self.lex => Token::Semicolon); 267 | ::ast::Statement::DoWhileLoop { 268 | body: code, 269 | cond: cnd, 270 | } 271 | }, 272 | Token::Rword_for => self.parse_for_loop()?, 273 | Token::Rword_if => { 274 | syntax_assert!(self.lex => Token::ParenOpen); 275 | let cnd = self.parse_expr()?; 276 | syntax_assert!(self.lex => Token::ParenClose); 277 | let tcode = self.parse_opt_block()?; 278 | let fcode = if peek_token!(self.lex, Token::Rword_else) { 279 | Some( self.parse_opt_block()? ) 280 | } else { 281 | None 282 | }; 283 | debug!("{}IF: {:?} {:?} {:?}", self.lex, cnd, tcode, fcode); 284 | ::ast::Statement::IfStatement { 285 | cond: ::ast::ExprOrDef::Expr(cnd), 286 | true_arm: tcode, 287 | else_arm: fcode, 288 | } 289 | }, 290 | Token::Rword_switch => self.parse_switch_statement()?, 291 | 292 | t @ Token::Ident(_) => { 293 | self.lex.put_back(t); 294 | let sp = self.lex.point_span(); 295 | let rv = self.parse_expr_list()?; 296 | // If the expression was just an ident, AND it's followed by a colon: It's a label 297 | if let ::ast::NodeKind::Identifier(i,_) = rv.kind 298 | { 299 | if peek_token!(self.lex, Token::Colon) { 300 | ::ast::Statement::Label(i) 301 | } 302 | else { 303 | // Otherwise, check for the semicolon and return a bare ident 304 | syntax_assert!(self.lex.get_token()?, Token::Semicolon); 305 | ::ast::Statement::Expr( ::ast::Node::new( sp, ::ast::NodeKind::Identifier(i, None) ) ) 306 | } 307 | } 308 | else { 309 | syntax_assert!(self.lex.get_token()?, Token::Semicolon); 310 | ::ast::Statement::Expr(rv) 311 | } 312 | }, 313 | 314 | // Expression 315 | t @ _ => { 316 | self.lex.put_back(t); 317 | let rv = self.parse_expr_list()?; 318 | syntax_assert!(self.lex.get_token()?, Token::Semicolon); 319 | ::ast::Statement::Expr(rv) 320 | } 321 | }, 322 | }) 323 | } 324 | 325 | /// Parse a for loop 326 | // ('for' has been eaten) 327 | fn parse_for_loop(&mut self) -> ParseResult<::ast::Statement> 328 | { 329 | syntax_assert!(self.lex => Token::ParenOpen); 330 | debug!("parse_for_loop"); 331 | let init = if peek_token_nc!(self.lex, Token::Semicolon) { 332 | None 333 | } else { 334 | match self.try_parse_local_var()? 335 | { 336 | Some(n) => Some(::ast::ExprOrDef::Definition(n)), 337 | None => Some(::ast::ExprOrDef::Expr( self.parse_expr_list()? )), 338 | } 339 | }; 340 | syntax_assert!(self.lex => Token::Semicolon); 341 | debug!("parse_for_loop: init = {:?}", init); 342 | let cnd = if peek_token_nc!(self.lex, Token::Semicolon) { 343 | None 344 | } else { 345 | Some(self.parse_expr_list()?) 346 | }; 347 | syntax_assert!(self.lex => Token::Semicolon); 348 | debug!("parse_for_loop: cnd = {:?}", cnd); 349 | let inc = if peek_token_nc!(self.lex, Token::ParenClose) { 350 | None 351 | } else { 352 | Some(self.parse_expr_list()?) 353 | }; 354 | syntax_assert!(self.lex => Token::ParenClose); 355 | debug!("parse_for_loop: inc = {:?}", inc); 356 | let code = self.parse_opt_block()?; 357 | Ok( ::ast::Statement::ForLoop { 358 | init, 359 | cond: cnd, 360 | inc, 361 | body: code, 362 | } ) 363 | } 364 | 365 | fn parse_switch_statement(&mut self) -> ParseResult<::ast::Statement> 366 | { 367 | let cnd = self.parse_expr()?; 368 | let mut code = Vec::new(); 369 | syntax_assert!(self.lex => Token::BraceOpen); 370 | loop 371 | { 372 | match self.lex.get_token()? 373 | { 374 | Token::BraceClose => break, 375 | Token::Rword_default => { 376 | code.push( ::ast::Statement::CaseDefault ); 377 | syntax_assert!(self.lex => Token::Colon); 378 | }, 379 | Token::Rword_case => { 380 | // TODO: Allow named constants (from #define)? 381 | let n = self.parse_expr()?; 382 | let first = match n.const_eval_opt() 383 | { 384 | crate::ast::ConstVal::Integer(i) => i, 385 | _ => syntax_error!("Case value is not constant - {:?}", n), 386 | }; 387 | if peek_token!(self.lex, Token::Vargs) { 388 | let n = self.parse_expr()?; 389 | let last = match n.const_eval_opt() 390 | { 391 | crate::ast::ConstVal::Integer(i) => i, 392 | _ => syntax_error!("Case value is not constant - {:?}", n), 393 | }; 394 | code.push( ::ast::Statement::CaseRange(first, last) ); 395 | } 396 | else { 397 | code.push( ::ast::Statement::CaseSingle(first) ); 398 | } 399 | syntax_assert!(self.lex => Token::Colon); 400 | }, 401 | t @ _ => { 402 | self.lex.put_back(t); 403 | code.push( self.parse_block_line()? ); 404 | } 405 | } 406 | } 407 | Ok( ::ast::Statement::Switch(cnd, code) ) 408 | } 409 | 410 | fn parse_variable_list(&mut self, extra: super::types::BaseTypeExtra, basetype: ::types::TypeRef) -> ParseResult<()> 411 | { 412 | loop 413 | { 414 | match self.lex.get_token()? 415 | { 416 | Token::Comma => {}, 417 | Token::Semicolon => break, 418 | t @ _ => { 419 | error!("{}: Unexpected token {:?}", self.lex, t); 420 | return Err( ::parse::Error::SyntaxError(format!("Unexpected token {:?} in parse_variable_list, expected comma or semicolon", t)) ); 421 | } 422 | } 423 | 424 | let span = self.lex.point_span(); 425 | let (typeid, ident) = self.get_full_type(basetype.clone())?; 426 | self.parse_variable_def(span, extra.clone(), typeid, ident)?; 427 | 428 | } 429 | 430 | Ok( () ) 431 | } 432 | 433 | fn parse_variable_def(&mut self, span: crate::ast::Span, extra: super::types::BaseTypeExtra, typeid: ::types::TypeRef, ident: String) -> ParseResult<()> 434 | { 435 | if peek_token!(self.lex, Token::Ident(ref n) if n == "__attribute__") 436 | { 437 | self.parse_gcc_attributes(|self_, name, _args| { 438 | match &name[..] 439 | { 440 | "unused" => Ok( () ), 441 | "section" => { 442 | // TODO: Handle `section("FOO")` properly. 443 | Ok( () ) 444 | }, 445 | _ => panic!("{} TODO: __attribute__({} {:?}) on variable definition (before value)", self_.lex, name, _args), 446 | } 447 | })?; 448 | } 449 | 450 | let init = self.parse_var_init()?; 451 | match init { 452 | None => { 453 | self.ast.define_variable(span, extra.is_inline, extra.storage_class, typeid, ident, None); 454 | } 455 | Some(init) => { 456 | let mut typeid = typeid; 457 | // If the type is an array with no size, get the size from the initialiser 458 | if let ::types::BaseType::Array(ref ty, ::types::ArraySize::None) = typeid.basetype { 459 | let len = match init 460 | { 461 | ::ast::Initialiser::ListLiteral(ref elems) => elems.len(), 462 | ::ast::Initialiser::Value(::ast::Node { kind: ::ast::NodeKind::String(ref val), .. }) => val.len() + 1, 463 | _ => todo!("Get array size from {:?}", init), 464 | }; 465 | typeid = ::types::Type::new_ref(::types::BaseType::Array(ty.clone(), ::types::ArraySize::Fixed(len as u64)), typeid.qualifiers.clone()); 466 | } 467 | self.ast.define_variable(span, extra.is_inline, extra.storage_class, typeid, ident, Some(init)); 468 | } 469 | } 470 | 471 | //if peek_token!(self.lex, Token::Ident(ref n) if n == "__attribute__") 472 | //{ 473 | // self.parse_gcc_attributes(|self_, name, _args| { 474 | // match &name[..] 475 | // { 476 | // _ => panic!("{} TODO: __attribute__({}) on variable definition (after value)", self_.lex, name), 477 | // } 478 | // })?; 479 | //} 480 | 481 | Ok( () ) 482 | } 483 | } 484 | 485 | // --- 486 | // Composite literals 487 | // --- 488 | impl<'ast> super::ParseState<'ast> 489 | { 490 | fn parse_composite_lit(&mut self) -> ParseResult<::ast::Initialiser> 491 | { 492 | Ok(match self.lex.get_token()? 493 | { 494 | Token::BraceClose => { 495 | ::ast::Initialiser::ListLiteral(vec![]) 496 | }, 497 | t @ Token::Period => { 498 | self.lex.put_back(t); 499 | ::ast::Initialiser::StructLiteral( self.parse_struct_lit()? ) 500 | }, 501 | t @ Token::SquareOpen => { 502 | self.lex.put_back(t); 503 | ::ast::Initialiser::ArrayLiteral( self.parse_array_lit()? ) 504 | }, 505 | t @ _ => { 506 | self.lex.put_back(t); 507 | ::ast::Initialiser::ListLiteral( self.parse_list_lit()? ) 508 | }, 509 | }) 510 | } 511 | fn parse_lit_inner(&mut self) -> ParseResult<::ast::Initialiser> { 512 | if !peek_token!(self.lex, Token::BraceOpen) { 513 | // `=` ... 514 | Ok( ::ast::Initialiser::Value( self.parse_expr()? ) ) 515 | } 516 | else { 517 | // `=` `{` 518 | self.parse_composite_lit() 519 | } 520 | } 521 | 522 | fn parse_list_lit(&mut self) -> ParseResult> 523 | { 524 | let mut items = Vec::new(); 525 | loop 526 | { 527 | if peek_token_nc!(self.lex, Token::BraceClose) { 528 | break; 529 | } 530 | let val = self.parse_lit_inner()?; 531 | items.push(val); 532 | if ! peek_token!(self.lex, Token::Comma) { 533 | break; 534 | } 535 | } 536 | syntax_assert!(self.lex => Token::BraceClose); 537 | 538 | 539 | Ok(items) 540 | } 541 | 542 | fn parse_array_lit(&mut self) -> ParseResult> 543 | { 544 | let mut items = Vec::new(); 545 | loop 546 | { 547 | if peek_token_nc!(self.lex, Token::BraceClose) { 548 | break; 549 | } 550 | syntax_assert!(self.lex => Token::SquareOpen); 551 | let idx_expr = self.parse_expr()?; 552 | syntax_assert!(self.lex => Token::SquareClose); 553 | syntax_assert!(self.lex => Token::Assign); 554 | let val = self.parse_lit_inner()?; 555 | 556 | items.push( (idx_expr,val) ); 557 | 558 | if ! peek_token!(self.lex, Token::Comma) { 559 | break; 560 | } 561 | } 562 | syntax_assert!(self.lex => Token::BraceClose); 563 | 564 | Ok(items) 565 | } 566 | 567 | fn parse_struct_lit(&mut self) -> ParseResult> 568 | { 569 | let mut items = Vec::new(); 570 | loop 571 | { 572 | if peek_token_nc!(self.lex, Token::BraceClose) { 573 | break; 574 | } 575 | syntax_assert!(self.lex => Token::Period); 576 | let name = syntax_assert!(self.lex => Token::Ident(i) @ i); 577 | syntax_assert!(self.lex => Token::Assign); 578 | let val = self.parse_lit_inner()?; 579 | 580 | items.push( (name,val) ); 581 | 582 | if ! peek_token!(self.lex, Token::Comma) { 583 | break; 584 | } 585 | } 586 | syntax_assert!(self.lex => Token::BraceClose); 587 | 588 | Ok(items) 589 | } 590 | } 591 | 592 | // vim: ft=rust ts=4 sw=4 593 | -------------------------------------------------------------------------------- /src/preproc/lex.rs: -------------------------------------------------------------------------------- 1 | /*! 2 | * Converts a source file into a stream of tokens 3 | */ 4 | use super::token::Token; 5 | use super::Error; 6 | 7 | pub type LexerInput<'a> = Box< dyn (::std::iter::Iterator>) + 'a >; 8 | 9 | pub struct Lexer<'a> 10 | { 11 | instream: LexerInput<'a>, 12 | lastchar: Option, 13 | 14 | /// String currently being captured (for floats/intergers) 15 | capture: Option, 16 | } 17 | struct CaptureHandle; 18 | impl Drop for CaptureHandle { 19 | fn drop(&mut self) { panic!("BUG: CaptureHandle not consumed"); } 20 | } 21 | 22 | macro_rules! try_eof { 23 | ($fcn:expr, $eofval:expr) => ( 24 | match $fcn { 25 | Ok(v) => v, 26 | Err(Error::EOF) => return Ok($eofval), 27 | Err(e) => return Err(e), 28 | } 29 | ); 30 | } 31 | 32 | macro_rules! match_ch { 33 | ($_self:ident, $def:expr, $($p:pat => $v:expr),* ) => ( 34 | match try_eof!($_self.getc(),$def) { 35 | $($p => $v),*, 36 | ch @ _ => { $_self.ungetc(ch); $def } 37 | } 38 | ); 39 | ($_self:ident, $def:expr, $($p:pat => $v:expr),*, ) => (match_ch!($_self,$def,$($p=>$v),*)); 40 | } 41 | 42 | impl<'a> Lexer<'a> 43 | { 44 | pub fn new(instream: LexerInput<'a>) -> Lexer<'a> { 45 | Lexer { 46 | instream: instream, 47 | lastchar: None, 48 | capture: None, 49 | } 50 | } 51 | 52 | fn getc(&mut self) -> super::Result 53 | { 54 | let ch = if let Some(ch) = self.lastchar.take() 55 | { 56 | ch 57 | } 58 | else 59 | { 60 | match self.instream.next() 61 | { 62 | Some(Ok(ch)) => ch, 63 | Some(Err(e)) => return Err(Error::IoError(e)), 64 | None => return Err(Error::EOF), 65 | } 66 | }; 67 | if let Some(cap) = self.capture.as_mut() 68 | { 69 | cap.push(ch) 70 | } 71 | Ok(ch) 72 | } 73 | fn ungetc(&mut self, ch: char) { 74 | self.lastchar = Some(ch); 75 | if let Some(cap) = self.capture.as_mut() 76 | { 77 | cap.pop(); 78 | } 79 | } 80 | 81 | fn start_capture(&mut self, ch: char) -> CaptureHandle { 82 | self.capture = Some(::std::iter::once(ch).collect()); 83 | CaptureHandle 84 | } 85 | fn end_capture(&mut self, handle: CaptureHandle) -> String { 86 | ::std::mem::forget(handle); 87 | self.capture.take().unwrap() 88 | } 89 | 90 | // Eat as many spaces as possible, returns errors verbatim 91 | fn eat_whitespace(&mut self) -> super::Result 92 | { 93 | let mut n = 0; 94 | loop 95 | { 96 | let ch = self.getc()?; 97 | if !ch.is_ascii_whitespace() || ch == '\n' { 98 | self.ungetc(ch); 99 | break; 100 | } 101 | n += 1; 102 | } 103 | Ok(n) 104 | } 105 | // Read and return the rest of the line 106 | // - Eof is converted to return value 107 | fn read_to_eol(&mut self) -> super::Result 108 | { 109 | let mut ret = String::new(); 110 | loop 111 | { 112 | let ch = try_eof!(self.getc(), ret); 113 | if ch == '\n' { break; } 114 | ret.push( ch ); 115 | } 116 | self.ungetc('\n'); 117 | return Ok(ret); 118 | } 119 | // Read and return a sequence of "identifier" characters 120 | fn read_ident(&mut self) -> super::Result 121 | { 122 | let mut name = String::new(); 123 | loop 124 | { 125 | let ch = try_eof!(self.getc(), name); 126 | if !(ch.is_alphanumeric() || ch == '_' || ch == '$') { 127 | self.ungetc(ch); 128 | break; 129 | } 130 | name.push( ch ); 131 | } 132 | return Ok(name); 133 | } 134 | // Read a number from the input stream 135 | fn read_number_with_len(&mut self, base: u32) -> super::Result<(u64,usize)> 136 | { 137 | let mut val = 0; 138 | let mut len = 0; 139 | loop 140 | { 141 | let ch = try_eof!( self.getc(), (val,len) ); 142 | match ch.to_digit(base) { 143 | Some(d) => { 144 | val *= base as u64; 145 | val += d as u64; 146 | len += 1; 147 | }, 148 | None => { 149 | self.ungetc(ch); 150 | return Ok( (val, len) ); 151 | } 152 | } 153 | } 154 | } 155 | fn read_number(&mut self, base: u32) -> super::Result 156 | { 157 | Ok( self.read_number_with_len(base)?.0 ) 158 | } 159 | 160 | fn read_escape(&mut self) -> super::Result> 161 | { 162 | Ok(match self.getc()? 163 | { 164 | '\\' => Some('\\'), 165 | '"' => Some('"'), 166 | 'n' => Some('\n'), 167 | 'r' => Some('\r'), 168 | 't' => Some(9u8 as char), 169 | 'v' => Some(11u8 as char), 170 | '0' => Some('\0'), 171 | 'x' => { 172 | let c1 = char::to_digit(self.getc()?, 16).expect("Unexpected character in escape code"); 173 | let c2 = char::to_digit(self.getc()?, 16).expect("Unexpected character in escape code"); 174 | Some( (c1 *16 + c2) as u8 as char ) 175 | }, 176 | '\n' => None, 177 | c @ _ => panic!("Unexpected escape code in string '\\{}'", c) 178 | }) 179 | } 180 | 181 | // Read a double-quoted string 182 | // - NOTE: has no EOF processing, as an EOF in a double-quoted string is invalid 183 | fn read_string(&mut self) -> super::Result 184 | { 185 | let mut ret = String::new(); 186 | loop 187 | { 188 | let ch = self.getc()?; 189 | if ch == '\"' { 190 | break; 191 | } 192 | if ch == '\\' { 193 | match self.read_escape()? 194 | { 195 | Some(c) => ret.push(c), 196 | None => {}, 197 | } 198 | } 199 | else { 200 | ret.push( ch ); 201 | } 202 | } 203 | return Ok(ret); 204 | } 205 | // Read a single-quoted character constant 206 | fn read_charconst(&mut self) -> super::Result 207 | { 208 | let mut ret = String::new(); 209 | loop 210 | { 211 | let ch = self.getc()?; 212 | if ch == '\'' { 213 | break; 214 | } 215 | if ch == '\\' { 216 | match self.read_escape()? 217 | { 218 | Some(c) => ret.push(c), 219 | None => {}, 220 | } 221 | } 222 | else { 223 | ret.push( ch ); 224 | } 225 | } 226 | match ret.len() 227 | { 228 | 0 => Err( Error::MalformedLiteral("Empty chracter constant") ), 229 | 1 => Ok( ret.chars().next().unwrap() as u64 ), 230 | _ => Err( Error::MalformedLiteral("Over-long character constant") ), 231 | } 232 | } 233 | 234 | pub fn get_token_includestr(&mut self) -> super::Result> 235 | { 236 | try_eof!(self.eat_whitespace(), None); 237 | 238 | let mut ch = try_eof!(self.getc(), None); 239 | if ch == '<' 240 | { 241 | let mut rv = String::new(); 242 | loop 243 | { 244 | ch = self.getc()?; 245 | if ch == '>' { 246 | break ; 247 | } 248 | rv.push(ch); 249 | } 250 | Ok( Some(rv) ) 251 | } 252 | else 253 | { 254 | self.ungetc(ch); 255 | Ok( None ) 256 | } 257 | } 258 | // Read a single token from the stream 259 | pub fn get_token(&mut self) -> super::Result 260 | { 261 | if try_eof!(self.eat_whitespace(), Token::EOF) > 0 { 262 | return Ok(Token::Whitespace); 263 | } 264 | 265 | let mut ch = try_eof!(self.getc(), Token::EOF); 266 | let ret = match ch 267 | { 268 | '\n' => Token::Newline, 269 | '\\' => 270 | match try_eof!(self.getc(), Token::EOF) 271 | { 272 | '\n' => Token::EscapedNewline, 273 | ch => { self.ungetc(ch); Token::Backslash }, 274 | }, 275 | '#' => Token::Hash, 276 | '~' => Token::Tilde, 277 | '!' => match_ch!(self, Token::Exclamation, 278 | '=' => Token::NotEquals, 279 | ), 280 | ';' => Token::Semicolon, 281 | ',' => Token::Comma, 282 | '?' => Token::QuestionMark, 283 | ':' => Token::Colon, 284 | '^' => match_ch!(self, Token::Caret, 285 | '=' => Token::AssignBitXor, 286 | ), 287 | '.' => match_ch!(self, Token::Period, 288 | '.' => { 289 | if try_eof!(self.getc(), Token::Period) != '.' { 290 | error!("Lex error '..' hit"); 291 | return Err( Error::BadCharacter('.') ); 292 | } 293 | Token::Vargs 294 | }, 295 | ), 296 | '=' => match_ch!(self, Token::Assign, 297 | '=' => Token::Equality, 298 | ), 299 | '+' => match_ch!(self, Token::Plus, 300 | '+' => Token::DoublePlus, 301 | '=' => Token::AssignAdd, 302 | ), 303 | '-' => match_ch!(self, Token::Minus, 304 | '-' => Token::DoubleMinus, 305 | '=' => Token::AssignSub, 306 | '>' => Token::DerefMember, 307 | ), 308 | '>' => match_ch!(self, Token::Gt, 309 | '>' => match_ch!(self, Token::ShiftRight, 310 | '=' => Token::AssignShiftRight, 311 | ), 312 | '=' => Token::GtE, 313 | ), 314 | '<' => match_ch!(self, Token::Lt, 315 | '<' => match_ch!(self, Token::ShiftLeft, 316 | '=' => Token::AssignShiftLeft, 317 | ), 318 | '=' => Token::LtE, 319 | ), 320 | '|' => match_ch!(self, Token::Pipe, 321 | '|' => match_ch!(self, Token::DoublePipe, 322 | '=' => Token::AssignLogicOr, 323 | ), 324 | '=' => Token::AssignBitOr, 325 | ), 326 | '&' => match_ch!(self, Token::Ampersand, 327 | '&' => match_ch!(self, Token::DoubleAmpersand, 328 | '=' => Token::AssignLogicAnd, 329 | ), 330 | '=' => Token::AssignBitAnd, 331 | ), 332 | '(' => Token::ParenOpen, ')' => Token::ParenClose, 333 | '{' => Token::BraceOpen, '}' => Token::BraceClose, 334 | '[' => Token::SquareOpen, ']' => Token::SquareClose, 335 | '%' => match_ch!(self, Token::Percent, 336 | '=' => Token::AssignMod, 337 | ), 338 | '*' => match_ch!(self, Token::Star, 339 | '=' => Token::AssignMul, 340 | ), 341 | '/' => match_ch!(self, Token::Slash, 342 | '/' => Token::LineComment(self.read_to_eol()?), 343 | '=' => Token::AssignDiv, 344 | '*' => { 345 | let mut comment = String::new(); 346 | loop { 347 | match try_eof!(self.getc(), Token::BlockComment(comment)) { 348 | '*' => { 349 | match try_eof!(self.getc(), Token::BlockComment(comment)) { 350 | '/' => break, 351 | '*' => self.ungetc('*'), // Handles '**/' 352 | c @ _ => comment.push(c) 353 | } 354 | }, 355 | c @ _ => comment.push(c) 356 | } 357 | } 358 | Token::BlockComment(comment) 359 | }, 360 | ), 361 | 362 | '"' => Token::String( self.read_string()? ), 363 | '\'' => Token::Character( self.read_charconst()? ), 364 | 365 | '0' ..= '9' => { 366 | let caph = self.start_capture(ch); 367 | let (base, whole) = if ch == '0' { 368 | let ch2 = try_eof!(self.getc(), Token::Integer(0, ::types::IntClass::int(), self.end_capture(caph))); 369 | match ch2 { 370 | '0' ..= '7' => { 371 | self.ungetc(ch2); 372 | (8, self.read_number( 8)?) 373 | }, 374 | 'x' => (16, self.read_number(16)?), 375 | 'b' => ( 2, self.read_number( 2)?), 376 | _ => { 377 | self.ungetc(ch2); 378 | (10, 0) 379 | } 380 | } 381 | } 382 | else { 383 | self.ungetc(ch); 384 | (10, self.read_number(10)?) 385 | }; 386 | // Check for a decimal point 387 | let intret = |s: &mut Self, c| Token::Integer(whole, ::types::IntClass::int(), s.end_capture(c)); 388 | ch = try_eof!(self.getc(), intret(self,caph)); 389 | if ch == '.' || (base == 10 && (ch == 'e' || ch == 'E')) || (base == 16 && (ch == 'p' || ch == 'P')) 390 | { 391 | // Floating point 392 | if base != 10 && base != 16 { 393 | return Err(super::Error::MalformedLiteral("Only hex and decimal floats are supported")); 394 | } 395 | let (frac_value, frac_len) = self.read_number_with_len(base)?; 396 | let (exp_is_neg,exponent) = if match self.getc() 397 | { 398 | Ok('e') | Ok('E') if base != 16 => true, 399 | Ok('p') | Ok('P') if base == 16 => true, 400 | Ok(ch) => { self.ungetc(ch); false }, 401 | Err(Error::EOF) => false, 402 | Err(e) => return Err(e), 403 | } 404 | { 405 | // e, E, p or P were seen, parse the exponent (in base 10) 406 | let is_neg = match self.getc() 407 | { 408 | Ok('-') => true, 409 | Ok('+') => false, 410 | Ok(ch) => { self.ungetc(ch); false }, 411 | Err(Error::EOF) => false, 412 | Err(e) => return Err(e), 413 | }; 414 | let (ev, el) = self.read_number_with_len(10)?; 415 | if el == 0 { return Err(super::Error::MalformedLiteral("Exponent has no digits")); } 416 | (is_neg, ev) 417 | } 418 | else 419 | { 420 | (false, 0) 421 | }; 422 | 423 | let float_val = make_float(base, whole, frac_len, frac_value, exp_is_neg, exponent); 424 | let ty = match self.getc() 425 | { 426 | Ok('f') | Ok('F') => ::types::FloatClass::Float, 427 | Ok('l') | Ok('L') => ::types::FloatClass::LongDouble, 428 | Ok(_) | Err(Error::EOF) => ::types::FloatClass::Double, 429 | Err(e) => return Err(e), 430 | }; 431 | Token::Float( float_val, ty, self.end_capture(caph) ) 432 | } 433 | else 434 | { 435 | // Integer 436 | let is_unsigned = ::types::Signedness::from_bool_signed(if ch=='u'||ch=='U' { ch = try_eof!(self.getc(), intret(self,caph)); false } else { true }); 437 | let is_long = if ch=='l'||ch=='L' { ch = try_eof!(self.getc(), intret(self,caph)); true } else { false }; 438 | let is_longlong = if ch=='l'||ch=='L' { ch = try_eof!(self.getc(), intret(self,caph)); true } else { false }; 439 | self.ungetc(ch); 440 | Token::Integer( whole, match (is_long,is_longlong) { 441 | (false,false) => ::types::IntClass::Int(is_unsigned), 442 | (true, false) => ::types::IntClass::Long(is_unsigned), 443 | (true, true ) => ::types::IntClass::LongLong(is_unsigned), 444 | (false, true) => panic!("BUGCHECK: LongLong set, but Long unset") 445 | }, self.end_capture(caph) ) 446 | } 447 | }, 448 | 'a'..='z'|'A'..='Z'|'_'|'$' => { 449 | self.ungetc(ch); 450 | Token::Ident( self.read_ident()? ) 451 | }, 452 | _ => { 453 | error!("Bad character #{} hit", ch as u32); 454 | return Err(Error::BadCharacter(ch)) 455 | } 456 | }; 457 | trace!("get_token: {:?}", ret); 458 | Ok(ret) 459 | } 460 | } 461 | 462 | pub(super) fn map_keywords(tok: Token) -> Token 463 | { 464 | match tok 465 | { 466 | Token::Ident(ident) => match &ident[..] 467 | { 468 | "typedef" => Token::Rword_typedef, 469 | "static" => Token::Rword_static, 470 | "extern" => Token::Rword_extern, 471 | "inline" => Token::Rword_inline, 472 | 473 | "const" => Token::Rword_const, 474 | "volatile" => Token::Rword_volatile, 475 | "restrict" => Token::Rword_restrict, 476 | 477 | "auto" => Token::Rword_auto, 478 | "register" => Token::Rword_register, 479 | "signed" => Token::Rword_signed, 480 | "unsigned" => Token::Rword_unsigned, 481 | "void" => Token::Rword_void, 482 | "_Bool" => Token::Rword_Bool, 483 | "char" => Token::Rword_char, 484 | "short" => Token::Rword_short, 485 | "int" => Token::Rword_int, 486 | "long" => Token::Rword_long, 487 | "float" => Token::Rword_float, 488 | "double" => Token::Rword_double, 489 | 490 | "sizeof" => Token::Rword_sizeof, 491 | "enum" => Token::Rword_enum, 492 | "union" => Token::Rword_union, 493 | "struct" => Token::Rword_struct, 494 | 495 | "if" => Token::Rword_if, 496 | "else" => Token::Rword_else, 497 | "do" => Token::Rword_do, 498 | "while" => Token::Rword_while, 499 | "for" => Token::Rword_for, 500 | "switch" => Token::Rword_switch, 501 | 502 | "case" => Token::Rword_case, 503 | "default" => Token::Rword_default, 504 | "return" => Token::Rword_return, 505 | "break" => Token::Rword_break, 506 | "continue" => Token::Rword_continue, 507 | "goto" => Token::Rword_goto, 508 | 509 | _ => Token::Ident(ident) 510 | }, 511 | t => t, 512 | } 513 | } 514 | 515 | fn make_float(base: u32, whole: u64, frac_len: usize, frac_value: u64, exp_is_neg: bool, exponent: u64) -> f64 //Float32_128 516 | { 517 | if frac_value == 0 && exponent == 0 { 518 | whole as f64 519 | } 520 | else { 521 | // 1. Convert the fraction value into an ecoded fraction. (5 => 0x5 => 0x8, 123 => 0x7B => 0x0214D) 522 | panic!("TODO: Create floating point values (base={}, whole={},frac={}:{},exponent={}{}", base, whole, frac_len, frac_value, if exp_is_neg { "-" } else { "" }, exponent); 523 | } 524 | } 525 | 526 | // vim: ft=rust 527 | -------------------------------------------------------------------------------- /src/preproc/token.rs: -------------------------------------------------------------------------------- 1 | #[allow(non_camel_case_types)] 2 | #[derive(Debug,PartialEq,Clone)] 3 | /// Token type (result of lexing/pre-processor) 4 | pub enum Token 5 | { 6 | EOF, 7 | 8 | // Ignored Tokens 9 | Whitespace, 10 | LineComment(String), 11 | BlockComment(String), 12 | Newline, 13 | EscapedNewline, 14 | 15 | // Pre-preocessor forwarding (when confiured to do so) 16 | Preprocessor(Preprocessor), 17 | 18 | // -- Expression leaves 19 | Integer(u64, ::types::IntClass, String), 20 | Float(f64, ::types::FloatClass, String), 21 | Character(u64), 22 | String(String), 23 | Ident(String), 24 | 25 | // -- Symbols 26 | Hash, 27 | Tilde, 28 | Exclamation, 29 | Period, 30 | DerefMember, 31 | Comma, 32 | Semicolon, 33 | Star, 34 | Slash, 35 | Backslash, 36 | Vargs, 37 | QuestionMark, 38 | Colon, 39 | 40 | Assign, 41 | AssignAdd, 42 | AssignSub, 43 | AssignMul, 44 | AssignDiv, 45 | AssignMod, 46 | AssignLogicOr, 47 | AssignLogicAnd, 48 | AssignBitOr, 49 | AssignBitAnd, 50 | AssignBitXor, 51 | AssignShiftLeft, 52 | AssignShiftRight, 53 | 54 | ShiftRight, 55 | ShiftLeft, 56 | 57 | Equality, 58 | NotEquals, 59 | Lt, 60 | Gt, 61 | LtE, 62 | GtE, 63 | 64 | Percent, 65 | Plus, 66 | Minus, 67 | DoublePlus, // ungood (bad joke, sorry) 68 | DoubleMinus, 69 | 70 | Ampersand, 71 | Pipe, 72 | Caret, 73 | DoubleAmpersand, 74 | DoublePipe, 75 | 76 | // -- Brackets 77 | BraceOpen, 78 | BraceClose, 79 | ParenOpen, 80 | ParenClose, 81 | SquareOpen, 82 | SquareClose, 83 | 84 | // -- Reserved Words 85 | // - Storage classes 86 | Rword_typedef, 87 | Rword_auto, 88 | Rword_extern, 89 | Rword_static, 90 | Rword_register, 91 | Rword_inline, 92 | // - Qualifiers 93 | Rword_const, 94 | Rword_volatile, 95 | Rword_restrict, 96 | // - Types 97 | Rword_void, 98 | Rword_Bool, 99 | Rword_signed, 100 | Rword_unsigned, 101 | Rword_char, 102 | Rword_short, 103 | Rword_int, 104 | Rword_long, 105 | Rword_float, 106 | Rword_double, 107 | // - Composites 108 | Rword_enum, 109 | Rword_union, 110 | Rword_struct, 111 | // - Blocks 112 | Rword_if, 113 | Rword_else, 114 | Rword_while, 115 | Rword_do, 116 | Rword_for, 117 | Rword_switch, 118 | // - Flow 119 | Rword_goto, 120 | Rword_continue, 121 | Rword_break, 122 | Rword_return, 123 | Rword_case, 124 | Rword_default, 125 | // - Meta 126 | Rword_sizeof, 127 | } 128 | impl From for Token { 129 | fn from(v: Preprocessor) -> Token { 130 | Token::Preprocessor(v) 131 | } 132 | } 133 | 134 | #[derive(Debug,PartialEq,Clone)] 135 | pub enum Preprocessor 136 | { 137 | /// #include 138 | Include { 139 | angle_brackets: bool, 140 | path: String, 141 | }, 142 | /// EOF in an included file 143 | EndOfInclude, 144 | 145 | IfDef { 146 | is_not_defined: bool, 147 | ident: String, 148 | }, 149 | If { 150 | tokens: Vec, 151 | }, 152 | ElseIf { 153 | tokens: Vec, 154 | }, 155 | Else, 156 | 157 | /// A macro definition 158 | MacroDefine { 159 | name: String, 160 | arg_names: Option< Vec >, 161 | expansion: Vec, 162 | }, 163 | /// Macro un-definition 164 | MacroUndefine { 165 | name: String, 166 | }, 167 | /// A macro invocation/expansion 168 | /// NOTE: This only gets emitted if macros are being handled by the pre-processor 169 | MacroInvocaton { 170 | /// Input tokens (with whitespace stripped) 171 | input: Vec, 172 | /// Output tokens 173 | output: Vec, 174 | }, 175 | } 176 | 177 | -------------------------------------------------------------------------------- /tools/mmir_opt/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "mmir_opt" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | structopt = "0.2" 10 | -------------------------------------------------------------------------------- /tools/mmir_opt/src/dump.rs: -------------------------------------------------------------------------------- 1 | use ::std::io::Write; 2 | 3 | pub fn dump_tree(dst: &::std::path::Path, tree: &crate::modtree::Root) -> ::std::io::Result<()> 4 | { 5 | let mut fp = ::std::io::BufWriter::new( ::std::fs::File::create(dst)?); 6 | for (name, ty) in &tree.types { 7 | if ty.align == usize::MAX { 8 | writeln!(fp, "type {};", name)?; 9 | } 10 | else { 11 | writeln!(fp, "type {name} {{")?; 12 | writeln!(fp, "\tSIZE {}, ALIGN {};", ty.size, ty.align)?; 13 | for f in &ty.fields { 14 | write!(fp, "\t{} = {};", f.offset, Ty(&f.ty,0))?; 15 | if let Some(c) = &f.comment { 16 | write!(fp, "\t /*{c}*/")?; 17 | } 18 | writeln!(fp, "")?; 19 | } 20 | writeln!(fp, "}}")?; 21 | } 22 | } 23 | 24 | for (name, def) in &tree.statics { 25 | if let Some(v) = &def.link_name { 26 | writeln!(fp, "static {}: {} = @{:?};", name, Ty(&def.ty,0), v)?; 27 | } 28 | if let Some(v) = &def.value { 29 | write!(fp, "static {}: {} = {}", name, Ty(&def.ty,0), Bytes(&v.data))?; 30 | if ! v.reloc.is_empty() { 31 | write!(fp, "{{")?; 32 | for r in &v.reloc { 33 | write!(fp, "@{}+{}=", r.ofs, r.size)?; 34 | match &r.value { 35 | crate::modtree::RelocVal::Symbol(s) => write!(fp, "{s}")?, 36 | crate::modtree::RelocVal::Data(d) => write!(fp, "{}", Bytes(d))?, 37 | } 38 | write!(fp, ", ")?; 39 | } 40 | write!(fp, "}}")?; 41 | } 42 | writeln!(fp, ";")?; 43 | } 44 | } 45 | 46 | for (name, def) in &tree.functions { 47 | write!(fp, "fn {name}(")?; 48 | for (name,ty) in Iterator::zip( def.arg_names.iter(), def.sig.args.iter() ) { 49 | write!(fp, "{name}: {}, ", Ty(ty,0))?; 50 | } 51 | if def.sig.is_variadic { 52 | write!(fp, "...")?; 53 | } 54 | write!(fp, ")")?; 55 | if !def.sig.ret.is_unit() { 56 | write!(fp, " -> {}", Ty(&def.sig.ret,0))?; 57 | } 58 | if let Some(link_name) = &def.link_name { 59 | write!(fp, " = {link_name:?}:\"\"")?; 60 | } 61 | if let Some(b) = &def.body { 62 | dump_function_body(&mut fp, b, Some(def))?; 63 | } 64 | else { 65 | writeln!(fp, ";")?; 66 | } 67 | } 68 | 69 | Ok( () ) 70 | } 71 | 72 | pub fn dump_function_body(fp: &mut dyn ::std::io::Write, fcn: &crate::mir::Function, def: Option<&crate::modtree::Function>) -> ::std::io::Result<()> 73 | { 74 | let b = fcn; 75 | writeln!(fp, " {{")?; 76 | for (name,ty) in &b.locals { 77 | writeln!(fp, "\tlet {name}: {ty};", ty=Ty(ty,0))?; 78 | } 79 | for (name,val) in &b.drop_flags { 80 | writeln!(fp, "\tlet {name} = {val};", val=if *val { "1" } else { "0" })?; 81 | } 82 | for (i, blk) in b.blocks.iter().enumerate() { 83 | writeln!(fp, "\t{i}: {{")?; 84 | for stmt in &blk.statements { 85 | write!(fp, "\t\t")?; 86 | match stmt { 87 | crate::mir::Statement::SpanComment(c) => { 88 | writeln!(fp, "/*{}*/", c)?; 89 | continue 90 | }, 91 | crate::mir::Statement::Assign(dst, rval) => { 92 | write!(fp, "ASSIGN {} = ", Val(dst,def))?; 93 | match rval { 94 | crate::mir::Value::Constant(c) => write!(fp, "{}", C(c))?, 95 | crate::mir::Value::Use(v) => write!(fp, "={}", Val(v,def))?, 96 | crate::mir::Value::Borrow(crate::types::Mutability::Shared, slot) 97 | => write!(fp, "& {}", Val(slot,def))?, 98 | crate::mir::Value::Borrow(mutability, slot) 99 | => write!(fp, "&{} {}", mutability.to_str(), Val(slot,def))?, 100 | crate::mir::Value::BinOp(l,o,r) => 101 | write!(fp, "BINOP {l} {o} {r}", 102 | l=Val(l,def), 103 | r=Val(r,def), 104 | o=match o 105 | { 106 | crate::mir::BinOp::Add => "+", 107 | crate::mir::BinOp::Sub => "-", 108 | crate::mir::BinOp::Div => "/", 109 | crate::mir::BinOp::Mul => "*", 110 | crate::mir::BinOp::Rem => "%", 111 | crate::mir::BinOp::Shr => ">>", 112 | crate::mir::BinOp::Shl => "<<", 113 | crate::mir::BinOp::BitAnd => "&", 114 | crate::mir::BinOp::BitOr => "|", 115 | crate::mir::BinOp::BitXor => "^", 116 | crate::mir::BinOp::Less => "<", 117 | crate::mir::BinOp::Greater => ">", 118 | crate::mir::BinOp::LessEqual => "<=", 119 | crate::mir::BinOp::GreaterEqual => ">=", 120 | crate::mir::BinOp::Equals => "==", 121 | crate::mir::BinOp::NotEquals => "!=", 122 | } 123 | )?, 124 | crate::mir::Value::UniOp(op, val) 125 | => write!(fp, "UNIOP {op} {v}", 126 | v=Val(val, def), 127 | op=match op { 128 | crate::mir::UniOp::Inv => "!", 129 | crate::mir::UniOp::Neg => "-", 130 | }, 131 | )?, 132 | crate::mir::Value::Cast(val, ty) => write!(fp, "CAST {} as {}", Val(val,def), Ty(ty,0))?, 133 | crate::mir::Value::DstPtr(v) => write!(fp, "DSTPTR {}", Val(v,def))?, 134 | crate::mir::Value::DstMeta(_) => todo!(), 135 | crate::mir::Value::Tuple(ents) => { 136 | write!(fp, "(")?; 137 | for e in ents { 138 | write!(fp, "{}, ", Val(e, def))?; 139 | } 140 | write!(fp, ")")?; 141 | }, 142 | crate::mir::Value::Array(_) => todo!(), 143 | crate::mir::Value::Struct(_, _) => todo!(), 144 | crate::mir::Value::UnionVariant(_, _, _) => todo!(), 145 | crate::mir::Value::EnumVariant(_, _, _) => todo!(), 146 | } 147 | }, 148 | } 149 | writeln!(fp, ";")?; 150 | } 151 | write!(fp, "\t\t")?; 152 | match &blk.terminator { 153 | crate::mir::Terminator::Removed => panic!(), 154 | crate::mir::Terminator::Invalid => writeln!(fp, "INCOMPLETE")?, 155 | crate::mir::Terminator::Return => writeln!(fp, "RETURN")?, 156 | crate::mir::Terminator::Diverge => writeln!(fp, "DIVERGE")?, 157 | crate::mir::Terminator::Goto(idx) => writeln!(fp, "GOTO {}", idx)?, 158 | crate::mir::Terminator::Call(call) => { 159 | write!(fp, "CALL {} = ", Val(&call.dst,def))?; 160 | match &call.target { 161 | crate::mir::CallTarget::Path(p) => write!(fp, "{}", p)?, 162 | crate::mir::CallTarget::Intrinsic(name, tys) => { 163 | write!(fp, "{:?}<", name)?; 164 | for t in tys { 165 | write!(fp, "{},", Ty(t,0))?; 166 | } 167 | write!(fp, ">")?; 168 | }, 169 | crate::mir::CallTarget::Value(v) => write!(fp, "({})", Val(v, def))?, 170 | } 171 | write!(fp, "(")?; 172 | for v in &call.args { 173 | write!(fp, "{},", Val(v, def))?; 174 | } 175 | writeln!(fp, ") goto {} else {}", call.bb_ret, call.bb_panic)?; 176 | }, 177 | crate::mir::Terminator::If(v, bb_true, bb_false) 178 | => writeln!(fp, "IF {} goto {} else {}", Val(v, def), bb_true, bb_false)?, 179 | crate::mir::Terminator::SwitchValue(v, vals, targets, bb_default) => { 180 | writeln!(fp, "SWITCHVALUE {} {{", Val(v, def))?; 181 | match vals { 182 | crate::mir::SwitchValues::Signed(vals) => { 183 | for (v,t) in Iterator::zip(vals.iter(), targets.iter()) { 184 | writeln!(fp, "\t\t{:+} = {},", v, t)?; 185 | } 186 | } 187 | crate::mir::SwitchValues::Unsigned(vals) => { 188 | for (v,t) in Iterator::zip(vals.iter(), targets.iter()) { 189 | writeln!(fp, "\t\t{} = {},", v, t)?; 190 | } 191 | } 192 | crate::mir::SwitchValues::Float(vals) => { 193 | for (v,t) in Iterator::zip(vals.iter(), targets.iter()) { 194 | writeln!(fp, "\t\t{:+} = {},", v, t)?; 195 | } 196 | } 197 | crate::mir::SwitchValues::String(vals) => { 198 | for (v,t) in Iterator::zip(vals.iter(), targets.iter()) { 199 | writeln!(fp, "\t\t{} = {},", Bytes(v), t)?; 200 | } 201 | } 202 | } 203 | writeln!(fp, "\t\t_ = {}", bb_default)?; 204 | writeln!(fp, "\t\t}}")?; 205 | }, 206 | } 207 | writeln!(fp, "\t}}")?; 208 | } 209 | writeln!(fp, "}}")?; 210 | Ok( () ) 211 | } 212 | 213 | struct Bytes<'a>(&'a [u8]); 214 | impl ::std::fmt::Display for Bytes<'_> { 215 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 216 | f.write_str("\"")?; 217 | for &b in self.0 { 218 | match b { 219 | 0 => f.write_str("\\0")?, 220 | b'\\' => f.write_str("\\\\")?, 221 | b'"' => f.write_str("\\\"")?, 222 | b' ' ..= 0x7F => (b as char).fmt(f)?, 223 | b => write!(f, "\\x{:02x}", b)?, 224 | } 225 | } 226 | f.write_str("\"")?; 227 | Ok( () ) 228 | } 229 | } 230 | 231 | struct Ty<'a>(&'a crate::types::TypeRef, usize); 232 | impl<'a> Ty<'a> { 233 | fn inner(&self) -> Ty<'a> { 234 | assert!(self.1 < self.0.wrappers.len()); 235 | Ty(self.0, self.1+1) 236 | } 237 | } 238 | impl ::std::fmt::Display for Ty<'_> { 239 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 240 | if self.1 < self.0.wrappers.len() { 241 | let w = &self.0.wrappers[ self.0.wrappers.len() - 1 - self.1 ]; 242 | match w { 243 | crate::types::Wrapper::Slice => write!(f, "[{}]", self.inner()), 244 | crate::types::Wrapper::Array(c) => write!(f, "[{}; {c}]", self.inner()), 245 | crate::types::Wrapper::Pointer(m) => write!(f, "*{} {}", m.to_str(), self.inner()), 246 | crate::types::Wrapper::Borrow(crate::types::Mutability::Shared) => write!(f, "&{}", self.inner()), 247 | crate::types::Wrapper::Borrow(m) => write!(f, "&{} {}", m.to_str(), self.inner()), 248 | } 249 | } 250 | else { 251 | match &self.0.root { 252 | crate::types::Root::Diverge => f.write_str("!"), 253 | crate::types::Root::Str => f.write_str("str"), 254 | crate::types::Root::Unsigned(bits) => write!(f, "u{bits}"), 255 | crate::types::Root::Signed(bits) => write!(f, "i{bits}"), 256 | crate::types::Root::Float(bits) => write!(f, "f{bits}"), 257 | crate::types::Root::Named(name) => f.write_str(name), 258 | crate::types::Root::Tuple(ents) => { 259 | f.write_str("(")?; 260 | for t in ents { 261 | Ty(t,0).fmt(f)?; 262 | f.write_str(",")?; 263 | } 264 | f.write_str(")") 265 | }, 266 | crate::types::Root::Function(fcn) => { 267 | if fcn.abi != "" { 268 | write!(f, "extern {:?}", fcn.abi)?; 269 | } 270 | f.write_str("fn(")?; 271 | for t in &fcn.args { 272 | Ty(t,0).fmt(f)?; 273 | f.write_str(",")?; 274 | } 275 | f.write_str(")")?; 276 | if ! fcn.ret.is_unit() { 277 | f.write_str("->")?; 278 | Ty(&fcn.ret,0).fmt(f)?; 279 | } 280 | Ok( () ) 281 | }, 282 | } 283 | } 284 | } 285 | } 286 | 287 | struct Val<'a, T>(&'a T, Option<&'a crate::modtree::Function>); 288 | impl<'a,T> Val<'a, T> { 289 | pub fn get_arg(&self, i: usize) -> ::std::borrow::Cow { 290 | if let Some(d) = self.1 { 291 | d.arg_names[i][..].into() 292 | } 293 | else { 294 | format!("a{}", i).into() 295 | } 296 | } 297 | pub fn get_local(&self, i: usize) -> ::std::borrow::Cow { 298 | if let Some(d) = self.1 { 299 | d.body.as_ref().unwrap().locals[i].0[..].into() 300 | } 301 | else { 302 | format!("_{}", i).into() 303 | } 304 | } 305 | } 306 | impl<'a> ::std::fmt::Display for Val<'a, crate::mir::Slot> { 307 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 308 | for w in &self.0.wrappers { 309 | match w { 310 | crate::mir::SlotWrapper::Deref => f.write_str("(*")?, 311 | _ => {}, 312 | } 313 | } 314 | match self.0.root { 315 | crate::mir::SlotRoot::Named(ref name) => f.write_str(name)?, 316 | crate::mir::SlotRoot::Argument(i) => f.write_str(&self.get_arg(i))?, 317 | crate::mir::SlotRoot::Local(i) => f.write_str(&self.get_local(i))?, 318 | crate::mir::SlotRoot::Return => f.write_str("RETURN")?, 319 | } 320 | for w in &self.0.wrappers { 321 | match w { 322 | //crate::mir::SlotWrapper::Deref => f.write_str(".*")?, 323 | crate::mir::SlotWrapper::Deref => f.write_str(")")?, 324 | crate::mir::SlotWrapper::Index(i) => write!(f, "[{}]", self.get_local(*i))?, 325 | crate::mir::SlotWrapper::Field(i) => write!(f, " .{}", i)?, 326 | crate::mir::SlotWrapper::Downcast(i) => write!(f, "#{}", i)?, 327 | } 328 | } 329 | Ok( () ) 330 | } 331 | } 332 | impl<'a> ::std::fmt::Display for Val<'a, crate::mir::Param> { 333 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 334 | match self.0 { 335 | crate::mir::Param::Const(c) => C(c).fmt(f), 336 | crate::mir::Param::Slot(s) => Val(s,self.1).fmt(f), 337 | } 338 | } 339 | } 340 | 341 | struct C<'a>(&'a crate::mir::Const); 342 | impl ::std::fmt::Display for C<'_> { 343 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 344 | match self.0 { 345 | crate::mir::Const::Boolean(true) => f.write_str("true"), 346 | crate::mir::Const::Boolean(false) => f.write_str("false"), 347 | crate::mir::Const::Unsigned(val, bits) => write!(f, "{} u{}", val, bits), 348 | crate::mir::Const::Signed(val, bits) => write!(f, "{:+} i{}", val, bits), 349 | crate::mir::Const::Float(_, _) => todo!(), 350 | crate::mir::Const::String(v) => write!(f, "{}", Bytes(&v.as_bytes())), 351 | crate::mir::Const::ItemAddr(v) => write!(f, "ADDROF {}", v), 352 | } 353 | } 354 | } -------------------------------------------------------------------------------- /tools/mmir_opt/src/helper_types.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | 3 | pub use self::trie::Trie; 4 | pub mod trie; 5 | pub use self::bitmap::Bitmap; 6 | pub mod bitmap; -------------------------------------------------------------------------------- /tools/mmir_opt/src/helper_types/bitmap.rs: -------------------------------------------------------------------------------- 1 | #[derive(Clone)] 2 | pub struct Bitmap(Vec); 3 | impl Bitmap { 4 | pub fn new(size: usize) -> Self { 5 | Bitmap(vec![0; (size + 32-1) / 32]) 6 | } 7 | pub fn get(&self, idx: usize) -> bool { 8 | self.0[idx / 32] & (1 << (idx%32)) != 0 9 | } 10 | pub fn set(&mut self, idx: usize) { 11 | self.0[idx / 32] |= 1 << (idx%32); 12 | } 13 | } -------------------------------------------------------------------------------- /tools/mmir_opt/src/helper_types/trie.rs: -------------------------------------------------------------------------------- 1 | 2 | pub struct Trie(Option>); 3 | impl Trie 4 | where 5 | T: Ord + Eq + Copy 6 | { 7 | pub fn new() -> Self { 8 | Trie(None) 9 | } 10 | pub fn push(&mut self, p: &[T]) { 11 | match self.0 12 | { 13 | None => self.0 = Some(TrieNode { prefix: p.to_owned(), children: Vec::new(), leaf: true }), 14 | Some(ref mut v) => v.push(p), 15 | } 16 | } 17 | pub fn iter(&self) -> impl Iterator> { 18 | [].into_iter() 19 | } 20 | pub fn dump(&self) 21 | where 22 | T: ::std::fmt::Debug, 23 | { 24 | if let Some(v) = &self.0 { 25 | v.dump(0); 26 | } 27 | else { 28 | println!("[]"); 29 | } 30 | } 31 | } 32 | 33 | struct TrieNode { 34 | prefix: Vec, 35 | children: Vec<(T, TrieNode)>, 36 | leaf: bool, 37 | } 38 | impl TrieNode 39 | where 40 | T: Eq + Ord + Copy, 41 | { 42 | fn push(&mut self, vals: &[T]) 43 | { 44 | if self.prefix.len() <= vals.len() && self.prefix == &vals[..self.prefix.len()] { 45 | if vals.len() == self.prefix.len() { 46 | self.leaf = true; 47 | } 48 | else { 49 | let v = vals[self.prefix.len()]; 50 | let tail = &vals[self.prefix.len()+1..]; 51 | match self.children.binary_search_by_key(&v, |v| v.0) { 52 | Ok(i) => self.children[i].1.push(tail), 53 | Err(i) => self.children.insert(i, (v, TrieNode { prefix: tail.to_owned(), children: Vec::new(), leaf: true })), 54 | } 55 | } 56 | } 57 | else { 58 | // Determine the leading match 59 | let match_len = Iterator::zip(vals.iter(), self.prefix.iter()).take_while(|(a,b)| a==b).count(); 60 | if match_len == 0 { 61 | } 62 | else { 63 | let new_exist = TrieNode { 64 | prefix: self.prefix[match_len+1..].to_owned(), 65 | children: ::std::mem::take(&mut self.children), 66 | leaf: self.leaf 67 | }; 68 | let new_incoming = TrieNode { 69 | prefix: vals[match_len+1..].to_owned(), 70 | children: Vec::new(), 71 | leaf: true 72 | }; 73 | self.children = vec![ 74 | (self.prefix[match_len], new_exist), 75 | (vals[match_len], new_incoming), 76 | ]; 77 | self.children.sort_unstable_by_key(|v| v.0); 78 | self.prefix = vals[..match_len].to_owned(); 79 | self.leaf = false; 80 | } 81 | } 82 | } 83 | fn dump(&self, indent: usize) 84 | where 85 | T: ::std::fmt::Debug, 86 | { 87 | for _ in 0..indent { print!(" ") } 88 | println!("{:?} = {}", self.prefix, self.leaf); 89 | for (val,child) in &self.children { 90 | for _ in 0..indent { print!(" ") } 91 | println!("=> {:?}", val); 92 | child.dump(indent+1); 93 | } 94 | } 95 | } -------------------------------------------------------------------------------- /tools/mmir_opt/src/lexer.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug)] 2 | pub enum Token<'a> 3 | { 4 | Ident(&'a str), 5 | String(StringLit<'a>), 6 | Integer(u128), 7 | Float(f64), 8 | Sym(&'a str), 9 | } 10 | #[derive(Debug)] 11 | pub struct StringLit<'a>(&'a str); 12 | impl<'a> StringLit<'a> { 13 | pub fn parse_string(&self) -> String { 14 | match String::from_utf8(self.parse_bytes()) { 15 | Err(e) => panic!("Malformed String : \"{}\" - {:?}", self.0, e), 16 | Ok(rv) => rv, 17 | } 18 | } 19 | pub fn parse_bytes(&self) -> Vec { 20 | let mut rv = Vec::new(); 21 | let mut it = self.0.split('\\'); 22 | let c = it.next().unwrap(); 23 | rv.extend(c.as_bytes().iter().copied()); 24 | while let Some(c) = it.next() { 25 | let (ch,next) = match c.as_bytes() { 26 | [] => (b'\\',"".as_bytes()), 27 | &[b'n', ref rest @ ..] => (b'\n', rest), 28 | &[b'0', ref rest @ ..] => (b'\0', rest), 29 | &[b'x', b1, b2, ref rest @ ..] => { 30 | let v1 = (b1 as char).to_digit(16).unwrap() as u8; 31 | let v2 = (b2 as char).to_digit(16).unwrap() as u8; 32 | (v1 * 16 + v2, rest) 33 | }, 34 | &[b'u', ref rest @ ..] => { 35 | assert!(rest.starts_with(b"{")); 36 | let i = rest[1..].iter().position(|v| *v == b'}').expect(""); 37 | let (value,rest) = rest[1..].split_at(i); 38 | let rest = &rest[1..]; // Chop off the closing `}` 39 | let mut codepoint = 0; 40 | for b in value { 41 | codepoint *= 16; 42 | codepoint += (*b as char).to_digit(16).unwrap(); 43 | } 44 | let mut buf = [0; 4]; 45 | let v = char::from_u32(codepoint).unwrap() 46 | .encode_utf8(&mut buf) 47 | .as_bytes(); 48 | let mut bytes = v.iter().copied(); 49 | rv.extend(bytes.by_ref().take(v.len() - 1)); 50 | (bytes.next().unwrap(), rest) 51 | }, 52 | &[b't', ref rest @ ..] => (b'\t', rest), 53 | &[b, ..] => todo!("\\{}", b as char), 54 | }; 55 | rv.push(ch); 56 | rv.extend(next.iter().copied()); 57 | } 58 | rv 59 | } 60 | } 61 | 62 | #[derive(Clone)] 63 | pub struct Lexer<'p,'a> { 64 | path: &'p ::std::path::Path, 65 | rem: &'a str, 66 | init: &'a str, 67 | line: usize 68 | } 69 | impl<'p, 'a> Lexer<'p, 'a> { 70 | pub fn new(path: &'p ::std::path::Path, v: &'a str) -> Self { 71 | Lexer { path, rem: v, init: v, line: 1 } 72 | } 73 | 74 | pub fn get_tok(&mut self) -> Option> { 75 | while let Some(_) = self.consume_comment() { 76 | } 77 | if false 78 | { 79 | let line_count = { 80 | let len = self.rem.as_ptr() as usize - self.init.as_ptr() as usize; 81 | 1 + self.init[..len].bytes().filter(|&b| b == b'\n').count() 82 | }; 83 | debug_assert!(line_count == self.line, "{}: Line count diverge: {} != {}", self, line_count, self.line); 84 | } 85 | Some(match self.rem.as_bytes() 86 | { 87 | [] => return None, 88 | [b'0', b'x', ..] => { 89 | self.consume(2); 90 | let len = self.rem.as_bytes().iter().position(|&b| !b.is_ascii_hexdigit()).unwrap_or(self.rem.len()); 91 | if len == 0 { 92 | panic!("{self}: Bare `0x`"); 93 | } 94 | let s = self.consume(len); 95 | Token::Integer(u128::from_str_radix(s, 16).unwrap()) 96 | }, 97 | [b'0', b'b', ..] => todo!("{self}: binary numbers"), 98 | [b'0', b'o', ..] => todo!(), 99 | [b'0'..=b'9', ..] => { 100 | let len = self.rem.as_bytes().iter().position(|&b| !b.is_ascii_digit()).unwrap_or(self.rem.len()); 101 | if let [b'.', b'0'..=b'9', ..] = self.rem.as_bytes()[len..] { 102 | let len2 = self.rem[len+1..].as_bytes().iter().position(|&b| !b.is_ascii_digit()).unwrap_or(self.rem.len()); 103 | assert!(len2 > 0); 104 | let s = self.consume(len+1+len2); 105 | Token::Float(s.parse().unwrap()) 106 | } 107 | else { 108 | let s = self.consume(len); 109 | Token::Integer(u128::from_str_radix(s, 10).unwrap()) 110 | } 111 | }, 112 | // Treat `_` as an ident only if it's follwed by an ident character 113 | [b'_', b'_'|b'0'..=b'9'|b'a'..=b'z'|b'A'..=b'Z'|0x80.., ..] 114 | |[b'a'..=b'z'|b'A'..=b'Z'|0x80.., ..] => { 115 | let len = self.rem.as_bytes().iter() 116 | .position(|&b| !(b.is_ascii_alphanumeric() || b == b'_' || b >= 0x80 || b == b'#')) 117 | .unwrap_or(self.rem.len()); 118 | Token::Ident( self.consume(len) ) 119 | }, 120 | 121 | [b'"', ..] => { 122 | self.consume(1); 123 | let end = 'o: { 124 | let mut prev = b' '; 125 | for (i,b) in self.rem.bytes().enumerate() { 126 | prev = match b { 127 | b'"' if prev != b'\\' => break 'o i, 128 | b'\\' if prev == b'\\' => { b' ' }, 129 | b => b, 130 | }; 131 | } 132 | self.rem.len() 133 | }; 134 | let rv = self.consume(end); 135 | self.consume(1); // Eat the closing double-quote 136 | Token::String(StringLit(rv)) 137 | }, 138 | 139 | [b'.',b'.',b'.', ..] => Token::Sym(self.consume(3)), 140 | 141 | [b'-', b'>', ..] => Token::Sym(self.consume(2)), // Thin arrow 142 | [b'=', b'>', ..] => Token::Sym(self.consume(2)), // Fat arrow, is this used? 143 | [b'=', b'=', ..] => Token::Sym(self.consume(2)), // Equality 144 | [b'!', b'=', ..] => Token::Sym(self.consume(2)), // Inequality 145 | [b'<', b'=', ..] => Token::Sym(self.consume(2)), // LE 146 | [b'>', b'=', ..] => Token::Sym(self.consume(2)), // GE 147 | [b'<', b'<', ..] => Token::Sym(self.consume(2)), // SHL 148 | [b'>', b'>', ..] => Token::Sym(self.consume(2)), // SHR 149 | 150 | [b';'|b':', ..] => Token::Sym(self.consume(1)), 151 | [b','|b'.', ..] => Token::Sym(self.consume(1)), 152 | [b'{'|b'}', ..] => Token::Sym(self.consume(1)), 153 | [b'['|b']', ..] => Token::Sym(self.consume(1)), 154 | [b'('|b')', ..] => Token::Sym(self.consume(1)), 155 | [b'-'|b'+', ..] => Token::Sym(self.consume(1)), 156 | [b'|'|b'^', ..] => Token::Sym(self.consume(1)), 157 | [b'*'|b'/', ..] => Token::Sym(self.consume(1)), 158 | [b'<'|b'>', ..] => Token::Sym(self.consume(1)), 159 | [b'@', ..] => Token::Sym(self.consume(1)), 160 | [b'&', ..] => Token::Sym(self.consume(1)), 161 | [b'%', ..] => Token::Sym(self.consume(1)), 162 | [b'=', ..] => Token::Sym(self.consume(1)), 163 | [b'!', ..] => Token::Sym(self.consume(1)), 164 | [b'_', ..] => Token::Sym(self.consume(1)), 165 | &[byte, ..] => todo!("{self}: Unexpected byte {:#x} ({:?})", byte, byte as char) 166 | }) 167 | } 168 | fn consume(&mut self, count: usize) -> &'a str { 169 | let rv = &self.rem[..count]; 170 | self.rem = &self.rem[count..]; 171 | rv 172 | } 173 | pub fn get_tok_noeof(&mut self) -> Token<'a> { 174 | match self.get_tok() { 175 | Some(v) => v, 176 | None => panic!("{self}: Unexpected EOF"), 177 | } 178 | } 179 | pub fn consume_comment(&mut self) -> Option<&'a str> { 180 | self.consume_whitespace(); 181 | if self.rem.starts_with("//") { 182 | self.rem = &self.rem[2..]; 183 | let end = self.rem.bytes().position(|b| b == b'\n' || b == b'\r').unwrap_or(self.rem.len()); 184 | let rv = &self.rem[..end]; 185 | self.rem = &self.rem[end..]; 186 | //println!(">> {} {:?}", self, &self.rem[..5]); 187 | Some(rv) 188 | } 189 | else if self.rem.starts_with("/*") { 190 | self.rem = &self.rem[2..]; 191 | let end = 'o: { 192 | let mut level = 0; 193 | let mut prev = b' '; 194 | for (i,b) in self.rem.bytes().enumerate() { 195 | prev = match b { 196 | b'/' if prev == b'*' => if level == 0 { 197 | break 'o i 198 | } 199 | else { 200 | level -= 1; 201 | b' ' 202 | }, 203 | b'*' if prev == b'/' => { level += 1; b' ' }, 204 | b @ b'\n' => { 205 | self.line += 1; 206 | b 207 | }, 208 | b => b, 209 | }; 210 | } 211 | self.rem.len() 212 | }; 213 | let rv = &self.rem[..end - 1]; // Minus 1, because `[end]` is the closing `/` 214 | self.rem = &self.rem[end+1..]; 215 | //println!(">> {} {:?}", self, &self.rem[..5]); 216 | Some(rv) 217 | } 218 | else { 219 | None 220 | } 221 | } 222 | fn consume_whitespace(&mut self) { 223 | let mut it = self.rem.chars(); 224 | loop { 225 | match it.next() 226 | { 227 | Some(c) if c.is_whitespace() => { 228 | if c == '\n' { 229 | self.line += 1; 230 | } 231 | self.rem = it.as_str(); 232 | } 233 | _ => break, 234 | } 235 | } 236 | } 237 | } 238 | #[allow(dead_code)] 239 | impl<'a> Lexer<'_, 'a> { 240 | pub fn consume_if_str(&mut self) -> Option> { 241 | let mut s = self.clone(); 242 | if let Token::String(rv) = s.get_tok_noeof() { 243 | *self = s; 244 | Some(rv) 245 | } 246 | else { 247 | None 248 | } 249 | } 250 | pub fn consume_if_int(&mut self) -> Option { 251 | let mut s = self.clone(); 252 | if let Token::Integer(rv) = s.get_tok_noeof() { 253 | *self = s; 254 | Some(rv) 255 | } 256 | else { 257 | None 258 | } 259 | } 260 | pub fn consume_if_ident(&mut self) -> Option<&'a str> { 261 | let mut s = self.clone(); 262 | if let Token::Ident(rv) = s.get_tok_noeof() { 263 | *self = s; 264 | Some(rv) 265 | } 266 | else { 267 | None 268 | } 269 | } 270 | pub fn consume_if_keyword(&mut self, i: &str) -> bool { 271 | let mut s = self.clone(); 272 | match s.get_tok_noeof() 273 | { 274 | Token::Ident(rv) if rv == i => { 275 | *self = s; 276 | true 277 | }, 278 | _ => false, 279 | } 280 | } 281 | pub fn consume_if_sym(&mut self, i: &str) -> bool { 282 | let mut s = self.clone(); 283 | match s.get_tok_noeof() 284 | { 285 | Token::Sym(rv) if rv == i => { 286 | *self = s; 287 | true 288 | }, 289 | _ => false, 290 | } 291 | } 292 | } 293 | impl<'a> Lexer<'_, 'a> { 294 | #[track_caller] 295 | pub fn consume_str(&mut self) -> StringLit<'a> { 296 | match self.get_tok_noeof() { 297 | Token::String(rv) => rv, 298 | t => panic!("{self}: Unexpected {t:?}, expected String"), 299 | } 300 | } 301 | #[track_caller] 302 | pub fn consume_int(&mut self) -> u128 { 303 | match self.get_tok_noeof() { 304 | Token::Integer(rv) => rv, 305 | t => panic!("{self}: Unexpected {t:?}, expected Integer"), 306 | } 307 | } 308 | #[track_caller] 309 | pub fn consume_float(&mut self) -> f64 { 310 | match self.get_tok_noeof() { 311 | Token::Float(rv) => rv, 312 | t => panic!("{self}: Unexpected {t:?}, expected Float"), 313 | } 314 | } 315 | #[track_caller] 316 | pub fn consume_ident(&mut self) -> &'a str { 317 | match self.get_tok_noeof() { 318 | Token::Ident(rv) => rv, 319 | t => panic!("{self}: Unexpected {t:?}, expected Identifier"), 320 | } 321 | } 322 | #[track_caller] 323 | pub fn consume_keyword(&mut self, i: &str) { 324 | match self.get_tok_noeof() { 325 | Token::Ident(rv) if rv == i => {}, 326 | t => panic!("{self}: Unexpected {t:?}, expected Ident({i:?})"), 327 | } 328 | } 329 | #[track_caller] 330 | pub fn consume_sym(&mut self, i: &str) { 331 | match self.get_tok_noeof() { 332 | Token::Sym(rv) if rv == i => {}, 333 | t => panic!("{self}: Unexpected {t:?}, expected Sym({i:?})"), 334 | } 335 | } 336 | } 337 | 338 | impl ::std::fmt::Display for Lexer<'_, '_> { 339 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 340 | write!(f, "{}:{}", self.path.display(), self.line) 341 | } 342 | } -------------------------------------------------------------------------------- /tools/mmir_opt/src/logger.rs: -------------------------------------------------------------------------------- 1 | pub struct Logger<'a> 2 | { 3 | function: String, 4 | buffer: ::std::cell::RefCell<&'a mut String>, 5 | force_log: bool, 6 | } 7 | impl<'a> Logger<'a> { 8 | pub fn new(buffer: &'a mut String, name: &str, force_log: bool,) -> Self { 9 | Logger { 10 | function: name.to_owned(), 11 | buffer: ::std::cell::RefCell::new(buffer), 12 | force_log, 13 | } 14 | } 15 | 16 | pub fn log(&self, args: ::std::fmt::Arguments) { 17 | if self.force_log { 18 | println!("{}", args) 19 | } 20 | else { 21 | let mut b = self.buffer.borrow_mut(); 22 | ::std::fmt::write(&mut *b, args).unwrap(); 23 | b.push_str("\n"); 24 | } 25 | } 26 | #[track_caller] 27 | pub fn error(&self, args: ::std::fmt::Arguments) -> ! { 28 | let mut b = self.buffer.borrow_mut(); 29 | print!("{}", *b); 30 | println!(">> {}", self.function); 31 | b.clear(); 32 | panic!("{}", args); 33 | } 34 | 35 | pub fn writer<'s>(&'s mut self) -> Writer<'s,'a> { 36 | Writer(self) 37 | } 38 | } 39 | impl Drop for Logger<'_> { 40 | fn drop(&mut self) { 41 | if !self.buffer.get_mut().is_empty() && false { 42 | print!("{}", self.buffer.get_mut()); 43 | println!(">> {}", self.function); 44 | } 45 | } 46 | } 47 | 48 | pub struct Writer<'a,'b>(&'a mut Logger<'b>); 49 | impl ::std::io::Write for Writer<'_,'_> { 50 | fn write(&mut self, buf: &[u8]) -> std::io::Result { 51 | self.0.buffer.get_mut().push_str(&String::from_utf8_lossy(buf)); 52 | Ok(buf.len()) 53 | } 54 | 55 | fn flush(&mut self) -> std::io::Result<()> { 56 | Ok( () ) 57 | } 58 | } 59 | impl Drop for Writer<'_,'_> { 60 | fn drop(&mut self) { 61 | if ! self.0.buffer.get_mut().ends_with("\n") { 62 | self.0.buffer.get_mut().push_str("\n"); 63 | } 64 | } 65 | } 66 | 67 | macro_rules! log_debug { 68 | ($logger:expr, $fmt:literal $($args:tt)*) => {{ 69 | crate::logger::Logger::log($logger, format_args!($fmt $($args)*)); 70 | }} 71 | } 72 | macro_rules! log_panic { 73 | ($logger:expr, $fmt:literal $($args:tt)*) => {{ 74 | crate::logger::Logger::error($logger, format_args!($fmt $($args)*)); 75 | }} 76 | } 77 | -------------------------------------------------------------------------------- /tools/mmir_opt/src/main.rs: -------------------------------------------------------------------------------- 1 | 2 | #[macro_use] 3 | mod logger; 4 | 5 | mod helper_types; 6 | 7 | mod lexer; 8 | mod parser; 9 | mod mir; 10 | mod modtree; 11 | mod types; 12 | mod validate; 13 | mod optimise; 14 | mod dump; 15 | 16 | #[derive(::structopt::StructOpt)] 17 | struct Options 18 | { 19 | #[structopt(parse(from_os_str))] 20 | input: Vec<::std::path::PathBuf>, 21 | 22 | #[structopt(short="o", parse(from_os_str))] 23 | output: ::std::path::PathBuf, 24 | 25 | #[structopt(long="--only", short="O")] 26 | only: Vec, 27 | 28 | #[structopt(long="--pre-validate")] 29 | pre_validate: bool, 30 | #[structopt(long="--no-optimise")] 31 | no_optimise: bool, 32 | } 33 | 34 | fn main() 35 | { 36 | let opts: Options = ::structopt::StructOpt::from_args(); 37 | let mut tree = crate::modtree::Root::new(); 38 | 39 | for path in &opts.input { 40 | parser::parse_file(&mut tree, path); 41 | } 42 | 43 | if opts.pre_validate 44 | { 45 | let mut logbuf = String::new(); 46 | for (name, fcn) in tree.functions.iter_mut() { 47 | if ! opts.only.is_empty() { 48 | if !opts.only.iter().any(|v| v == name) { 49 | continue ; 50 | } 51 | } 52 | if let Some(ref mut b) = fcn.body { 53 | let mut logger = crate::logger::Logger::new(&mut logbuf, name, ! opts.only.is_empty()); 54 | println!("--- PV: {}", name); 55 | validate::validate_function(&mut logger, b, &fcn.sig); 56 | } 57 | } 58 | } 59 | 60 | if !opts.no_optimise 61 | { 62 | let mut logbuf = String::new(); 63 | for (name, fcn) in tree.functions.iter_mut() { 64 | if ! opts.only.is_empty() { 65 | if !opts.only.iter().any(|v| v == name) { 66 | continue ; 67 | } 68 | } 69 | if let Some(ref mut b) = fcn.body { 70 | let mut logger = crate::logger::Logger::new(&mut logbuf, name, ! opts.only.is_empty()); 71 | println!("--- {}", name); 72 | optimise::optimise_function(&mut logger, b, &fcn.sig); 73 | 74 | validate::validate_function(&mut logger, b, &fcn.sig); 75 | } 76 | } 77 | } 78 | 79 | dump::dump_tree(&opts.output, &tree) 80 | .expect("IO error while writing"); 81 | } 82 | 83 | 84 | #[derive(Clone, PartialEq, Eq, PartialOrd, Ord)] 85 | struct LineRef { 86 | bb_idx: usize, 87 | stmt_idx: StmtIdx, 88 | } 89 | #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] 90 | enum StmtIdx { 91 | Stmt(u16), 92 | Term, 93 | } 94 | impl ::core::fmt::Display for LineRef { 95 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 96 | write!(f, "BB{}/", self.bb_idx)?; 97 | match self.stmt_idx { 98 | StmtIdx::Stmt(i) => write!(f, "{}", i), 99 | StmtIdx::Term => f.write_str("TERM"), 100 | } 101 | } 102 | } 103 | impl ::core::fmt::Debug for LineRef { 104 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 105 | ::std::fmt::Display::fmt(self, f) 106 | } 107 | } -------------------------------------------------------------------------------- /tools/mmir_opt/src/mir.rs: -------------------------------------------------------------------------------- 1 | use crate::types::TypeRef; 2 | 3 | #[derive(PartialEq)] 4 | pub struct Function 5 | { 6 | pub locals: Vec<(String, TypeRef)>, 7 | pub drop_flags: Vec<(String, bool)>, 8 | pub blocks: Vec, 9 | } 10 | #[derive(PartialEq)] 11 | pub struct BasicBlock { 12 | pub statements: Vec, 13 | pub terminator: Terminator, 14 | } 15 | #[derive(Debug)] 16 | pub enum Statement { 17 | SpanComment(String), 18 | Assign(Slot, Value), 19 | } 20 | impl PartialEq for Statement { 21 | fn eq(&self, other: &Self) -> bool { 22 | match (self, other) { 23 | (Self::SpanComment(_), Self::SpanComment(_)) => true, 24 | (Self::Assign(l0, l1), Self::Assign(r0, r1)) => l0 == r0 && l1 == r1, 25 | _ => false, 26 | } 27 | } 28 | } 29 | #[derive(PartialEq, Debug)] 30 | pub enum Terminator { 31 | Removed, 32 | Invalid, 33 | Return, 34 | Diverge, 35 | Goto(usize), 36 | Call(TerminatorCall), 37 | If(Slot, usize, usize), 38 | SwitchValue(Slot, SwitchValues, Vec, usize) 39 | } 40 | #[derive(PartialEq, Debug)] 41 | pub struct TerminatorCall { 42 | pub target: CallTarget, 43 | pub args: Vec, 44 | pub dst: Slot, 45 | pub bb_ret: usize, 46 | pub bb_panic: usize, 47 | } 48 | #[derive(PartialEq, Debug)] 49 | pub enum CallTarget { 50 | Path(String), 51 | Intrinsic(String, Vec), 52 | Value(Slot), 53 | } 54 | #[derive(PartialEq, Debug)] 55 | pub enum SwitchValues { 56 | Signed(Vec), 57 | Unsigned(Vec), 58 | Float(Vec), 59 | String(Vec< Vec >), 60 | } 61 | 62 | #[derive(PartialEq, Debug)] 63 | pub struct Slot { 64 | pub root: SlotRoot, 65 | pub wrappers: Vec, 66 | } 67 | impl Slot { 68 | pub fn is_local(&self) -> Option { 69 | match self.root { 70 | SlotRoot::Local(i) if self.wrappers.is_empty() => Some(i), 71 | _ => None, 72 | } 73 | } 74 | } 75 | #[derive(PartialEq, Debug, Clone)] 76 | pub enum SlotRoot { 77 | Named(String), 78 | Argument(usize), 79 | Local(usize), 80 | Return, 81 | } 82 | #[derive(PartialEq, Debug, Clone)] 83 | pub enum SlotWrapper { 84 | Deref, 85 | Index(usize), 86 | Field(usize), 87 | Downcast(usize), 88 | } 89 | 90 | // Aka `RValue` 91 | #[derive(PartialEq, Debug)] 92 | pub enum Value { 93 | Constant(Const), 94 | Use(Slot), 95 | Borrow(crate::types::Mutability, Slot), 96 | BinOp(Param, BinOp, Param), 97 | UniOp(UniOp, Slot), 98 | Cast(Slot, crate::types::TypeRef), 99 | DstPtr(Slot), 100 | DstMeta(Slot), 101 | Tuple(Vec), 102 | Array(Vec), 103 | Struct(String, Vec), 104 | UnionVariant(String, usize, Param), 105 | EnumVariant(String, usize, Vec), 106 | } 107 | #[derive(PartialEq, Debug)] 108 | pub enum UniOp { 109 | Inv, 110 | Neg, 111 | } 112 | #[derive(PartialEq, Debug)] 113 | pub enum BinOp { 114 | Add, Sub, Div, Mul, Rem, 115 | Shr, Shl, 116 | BitAnd, BitOr, BitXor, 117 | 118 | Less, Greater, 119 | LessEqual, GreaterEqual, 120 | Equals, NotEquals, 121 | } 122 | 123 | #[derive(PartialEq, Debug)] 124 | pub enum Param { 125 | Const(Const), 126 | Slot(Slot), 127 | } 128 | #[derive(PartialEq, PartialOrd, Debug, Clone)] 129 | pub enum Const { 130 | Boolean(bool), 131 | Unsigned(u128, crate::types::Bits), 132 | Signed(i128, crate::types::Bits), 133 | Float(f64, crate::types::Bits), 134 | String(String), 135 | ItemAddr(String), 136 | } -------------------------------------------------------------------------------- /tools/mmir_opt/src/modtree.rs: -------------------------------------------------------------------------------- 1 | use crate::types::TypeRef; 2 | use ::std::collections::BTreeMap; 3 | 4 | pub struct Root 5 | { 6 | pub loaded_crates: ::std::collections::HashSet<::std::path::PathBuf>, 7 | pub types: BTreeMap, 8 | pub functions: BTreeMap, 9 | pub statics: BTreeMap, 10 | } 11 | impl Root { 12 | pub fn new() -> Self { 13 | Root { 14 | loaded_crates: Default::default(), 15 | types: Default::default(), 16 | functions: Default::default(), 17 | statics: Default::default(), 18 | } 19 | } 20 | } 21 | pub struct Type 22 | { 23 | pub size: usize, 24 | pub align: usize, 25 | pub fields: Vec, 26 | } 27 | pub struct Field { 28 | pub offset: usize, 29 | pub ty: TypeRef, 30 | pub comment: Option, 31 | } 32 | pub struct Function 33 | { 34 | pub define_location: String, 35 | pub link_name: Option, 36 | pub sig: crate::types::FcnTy, 37 | pub arg_names: Vec, 38 | pub body: Option, 39 | } 40 | pub struct Static 41 | { 42 | pub define_location: String, 43 | pub link_name: Option, 44 | pub ty: TypeRef, 45 | pub value: Option, 46 | } 47 | pub struct StaticValue { 48 | pub data: Vec, 49 | pub reloc: Vec, 50 | } 51 | pub struct Reloc { 52 | pub ofs: usize, 53 | pub size: usize, 54 | pub value: RelocVal, 55 | } 56 | pub enum RelocVal { 57 | Symbol(String), 58 | Data(Vec), 59 | } -------------------------------------------------------------------------------- /tools/mmir_opt/src/optimise.rs: -------------------------------------------------------------------------------- 1 | use crate::mir::Terminator; 2 | use crate::mir::Statement; 3 | use crate::logger::Logger; 4 | 5 | pub fn optimise_function(logger: &mut Logger, fcn: &mut crate::mir::Function, _sig: &crate::types::FcnTy) 6 | { 7 | loop { 8 | log_debug!(logger, "-- PASS"); 9 | let mut changed = false; 10 | // - Simplify flow control: If a block is just a goto, replace uses of it with the target 11 | while simplify_control_flow(logger, fcn) { 12 | changed = true; 13 | crate::dump::dump_function_body(&mut logger.writer(), fcn, None).unwrap(); 14 | } 15 | // - Redundant Pointer Casts 16 | // > Two casts in a row can be simplfied into just the latter (if the initial source is a pointer or usize) 17 | // * This happens when `NULL` is cast to something. 18 | #[cfg(any())] 19 | while remove_redundant_casts(logger, fcn, sig) { 20 | changed = true; 21 | crate::dump::dump_function_body(&mut logger.writer(), fcn, None).unwrap(); 22 | } 23 | // - Single write/use temporaries 24 | #[cfg(any())] 25 | while remove_single_use(logger, fcn) { 26 | changed = true; 27 | crate::dump::dump_function_body(&mut logger.writer(), fcn, None).unwrap(); 28 | } 29 | // - Unused writes 30 | while remove_dead_writes(logger, fcn) { 31 | changed = true; 32 | crate::dump::dump_function_body(&mut logger.writer(), fcn, None).unwrap(); 33 | } 34 | // - Constant propagation 35 | // > Replace casts with literals 36 | // > Propagate through to operators 37 | while const_propagate(logger, fcn) { 38 | changed = true; 39 | crate::dump::dump_function_body(&mut logger.writer(), fcn, None).unwrap(); 40 | } 41 | if !changed { 42 | break 43 | } 44 | } 45 | 46 | log_debug!(logger, "-- Cleanup"); 47 | crate::dump::dump_function_body(&mut logger.writer(), fcn, None).unwrap(); 48 | // Delete unused blocks 49 | clean_unused_blocks(logger, fcn); 50 | //println!(">>"); 51 | //crate::dump::dump_function_body(&mut ::std::io::stdout(), fcn, None).unwrap(); 52 | simplify_control_flow(logger, fcn); 53 | } 54 | 55 | fn simplify_control_flow(logger: &mut Logger, fcn: &mut crate::mir::Function) -> bool 56 | { 57 | log_debug!(logger, "simplify_control_flow"); 58 | let rewrites: Vec<_> = fcn.blocks.iter() 59 | .map(|bb| { 60 | match bb.terminator { 61 | Terminator::Goto(v) if bb.statements.is_empty() => Some(v), 62 | _ => None, 63 | } 64 | }) 65 | .collect(); 66 | let mut rv = false; 67 | helpers::visit_block_targets_mut(fcn, |tgt: &mut usize| if let Some(new) = rewrites[*tgt] { 68 | *tgt = new; 69 | rv = true; 70 | }); 71 | rv 72 | } 73 | 74 | mod const_propagate; 75 | use const_propagate::const_propagate; 76 | 77 | fn remove_dead_writes(logger: &mut Logger, fcn: &mut crate::mir::Function) -> bool 78 | { 79 | log_debug!(logger, "remove_dead_writes"); 80 | // Identify writes to variables that are never read, just overwritten 81 | // - This catches `"uninit"` assignments, AND just dead statements 82 | 83 | /* 84 | #[derive(Clone)] 85 | struct Path(Vec); 86 | impl Path { 87 | pub fn contains(&self, idx: usize) -> Option { 88 | self.0.iter().position(|v| *v == idx as u32) 89 | } 90 | } 91 | fn path_contains(p: &[&u32], idx: usize) -> Option { 92 | p.iter().position(|&&v| v == idx as u32) 93 | } 94 | type PathSet = crate::helper_types::Trie; 95 | fn enumerate_paths_for_bb(logger: &Logger, fcn: &crate::mir::Function, bb_idx: usize) -> PathSet { 96 | let mut paths = PathSet::new(); 97 | let mut stack: Vec<(usize, Path)> = Vec::new(); 98 | stack.push((bb_idx, Path(Vec::new()))); 99 | while let Some( (idx, path)) = stack.pop() { 100 | let path = helpers::visit_terminator_targets(&fcn.blocks[idx].terminator, path, |mut path,idx| { 101 | if ! path.contains(idx).is_some() { 102 | path.0.push(idx as u32); 103 | stack.push((idx, path)); 104 | } 105 | else { 106 | path.0.push(idx as u32); 107 | //log_debug!(logger, "Paths BB{}: {:?}", bb_idx, path.0); 108 | paths.push(&path.0); 109 | } 110 | }); 111 | if let Some(path) = path { 112 | //log_debug!(logger, "Paths BB{}: {:?}", bb_idx, path.0); 113 | paths.push(&path.0); 114 | } 115 | } 116 | log_debug!(logger, "Paths BB{}", bb_idx); 117 | paths.dump(); 118 | paths 119 | } 120 | let paths_from_each_bb: Vec<_> = (0 .. fcn.blocks.len()).map(|bb_idx| { 121 | log_debug!(logger, "Paths BB{}", bb_idx); 122 | enumerate_paths_for_bb(logger, fcn, bb_idx) 123 | }).collect(); 124 | */ 125 | 126 | // - Enumerate reads/writes of each variable (locations) 127 | // > For each write, see if there is a read before the next write (along the path) 128 | #[derive(PartialEq, Eq, PartialOrd, Ord, Debug)] 129 | enum Op { 130 | Read, 131 | Write, 132 | } 133 | use crate::{LineRef,StmtIdx}; 134 | use ::std::collections::BTreeMap; 135 | let mut ops = BTreeMap::>::new(); 136 | for (bb_idx,bb) in fcn.blocks.iter().enumerate() 137 | { 138 | fn visit_read(ops: &mut BTreeMap>, slot: &crate::mir::Slot, lr: &LineRef) { 139 | for w in &slot.wrappers { 140 | if let crate::mir::SlotWrapper::Index(i) = *w { 141 | ops.entry(i).or_default().push((lr.clone(), Op::Read)); 142 | } 143 | } 144 | if let crate::mir::SlotRoot::Local(i) = slot.root { 145 | ops.entry(i).or_default().push((lr.clone(), Op::Read)); 146 | } 147 | } 148 | fn visit_write(ops: &mut BTreeMap>, slot: &crate::mir::Slot, lr: LineRef) { 149 | for w in &slot.wrappers { 150 | if let crate::mir::SlotWrapper::Index(i) = *w { 151 | ops.entry(i).or_default().push((lr.clone(), Op::Read)); 152 | } 153 | } 154 | if let Some(i) = slot.is_local() { 155 | ops.entry(i).or_default().push((lr, Op::Write)); 156 | } 157 | else { 158 | // Any other usage is assumed to be a read 159 | if let crate::mir::SlotRoot::Local(i) = slot.root { 160 | ops.entry(i).or_default().push((lr, Op::Read)); 161 | } 162 | } 163 | } 164 | 165 | for (stmt_idx, stmt) in bb.statements.iter().enumerate() { 166 | match stmt { 167 | Statement::SpanComment(_) => {}, 168 | Statement::Assign(dst, src) => { 169 | // Look for usage 170 | let lr = LineRef { bb_idx, stmt_idx: StmtIdx::Stmt(stmt_idx.try_into().expect("Too many statements?!")) }; 171 | helpers::visit_slot_in_value(src, |slot| { 172 | visit_read(&mut ops, slot, &lr); 173 | }); 174 | 175 | visit_write(&mut ops, dst, lr); 176 | } 177 | } 178 | } 179 | let lr = LineRef { bb_idx, stmt_idx: StmtIdx::Term }; 180 | match bb.terminator { 181 | Terminator::Removed => {}, 182 | Terminator::Invalid => {}, 183 | Terminator::Return => {}, 184 | Terminator::Diverge => {}, 185 | Terminator::Goto(_) => {}, 186 | Terminator::Call(ref call) => { 187 | 188 | match &call.target { 189 | crate::mir::CallTarget::Path(_) => {}, 190 | crate::mir::CallTarget::Intrinsic(_, _) => {}, 191 | crate::mir::CallTarget::Value(slot) => { 192 | visit_read(&mut ops, slot, &lr); 193 | }, 194 | } 195 | 196 | for p in &call.args { 197 | helpers::visit_slot_in_param(p, |slot| { 198 | visit_read(&mut ops, slot, &lr); 199 | }); 200 | } 201 | 202 | visit_write(&mut ops, &call.dst, lr); 203 | } 204 | Terminator::If(ref slot, _, _) => { 205 | visit_read(&mut ops, slot, &lr); 206 | } 207 | Terminator::SwitchValue(ref slot, _, _, _) => { 208 | visit_read(&mut ops, slot, &lr); 209 | } 210 | } 211 | } 212 | 213 | for (_,o) in ops.iter_mut() { 214 | o.sort(); 215 | } 216 | let mut rv = false; 217 | for (&var_idx, ops) in &ops { 218 | log_debug!(logger, "{var_idx} {ops:?}"); 219 | let find_usage = |fcn: &crate::mir::Function, wr_pos: &LineRef, var_idx: usize| { 220 | let _ = var_idx; 221 | // Look for a read/write with the same BB but a higher statement index 222 | // - Assumes the list is sorted 223 | if let Some(v) = ops.iter() 224 | .find(|(lr,_)| lr.bb_idx == wr_pos.bb_idx && lr.stmt_idx > wr_pos.stmt_idx) { 225 | return Some(v); 226 | } 227 | 228 | let mut found_read = None; 229 | let mut found_write = None; 230 | helpers::visit_path_from_bb(logger, fcn, wr_pos.bb_idx, |_path_index, bb_idx| { 231 | for ops_ent @ (lr,op) in ops.iter() { 232 | if lr.bb_idx == bb_idx { 233 | log_debug!(logger, ">> {_path_index} {op:?} @ {lr}"); 234 | match op { 235 | Op::Read => { 236 | found_read = Some(ops_ent); 237 | // Any read (before a write) from this is an immediate exit 238 | return helpers::PathVisit::StopAll; 239 | } 240 | Op::Write => { 241 | found_write = Some(ops_ent); 242 | return helpers::PathVisit::StopArm; 243 | } 244 | } 245 | } 246 | } 247 | return helpers::PathVisit::Continue; 248 | }); 249 | if let Some(r) = found_read { 250 | return Some(r); 251 | } 252 | // If there was no write, then return not-found 253 | return None; 254 | }; 255 | for (lr, o) in ops.iter() { 256 | if let Op::Write = o { 257 | let try_remove = if ops.iter().all(|(_,o)| matches!(o, Op::Write)) { 258 | // Only use is a write? Remove 259 | log_debug!(logger, "_{var_idx}: Write {lr} - Never read; remove"); 260 | true 261 | } 262 | else if ops.iter().filter(|(_,o)| matches!(o, Op::Write)).count() == 1 { 263 | // Only one write (and non-zero reads)? Don't remove 264 | log_debug!(logger, "_{var_idx}: Write {lr} - One write, has reads; keep"); 265 | false 266 | } 267 | else { 268 | log_debug!(logger, "_{var_idx}: Write {lr} - Check"); 269 | // Otherwise, there's multiple writes - so we need to determine if this is invalidated 270 | match find_usage(fcn, lr, var_idx) { 271 | None | Some((_,Op::Write)) => true, 272 | _ => false 273 | } 274 | }; 275 | if try_remove { 276 | let bb = &mut fcn.blocks[lr.bb_idx]; 277 | match lr.stmt_idx { 278 | StmtIdx::Term => { 279 | let can_remove = match bb.terminator { 280 | Terminator::Call(ref call) => { 281 | match call.target { 282 | crate::mir::CallTarget::Path(_) => None, 283 | crate::mir::CallTarget::Intrinsic(ref name, _) => match &name[..] { 284 | // These intrinsics have no side-effects (not the case with all intrinsics) 285 | "uninit" 286 | |"init" 287 | |"offset" 288 | => Some(call.bb_ret), 289 | _ => None, 290 | }, 291 | crate::mir::CallTarget::Value(_) => None, 292 | } 293 | 294 | }, 295 | _ => panic!(), 296 | }; 297 | if let Some(target) = can_remove { 298 | log_debug!(logger, "remove write at {lr} - terminator {:?}", bb.terminator); 299 | bb.terminator = Terminator::Goto(target); 300 | rv = true; 301 | } 302 | else { 303 | log_debug!(logger, "CANNOT remove write at {lr} - terminator {:?}", bb.terminator); 304 | } 305 | }, 306 | StmtIdx::Stmt(stmt_idx) => { 307 | log_debug!(logger, "remove write at {lr} - statement {:?}", bb.statements[stmt_idx as usize]); 308 | bb.statements[stmt_idx as usize] = Statement::SpanComment("".into()); 309 | rv = true; 310 | } 311 | } 312 | } 313 | } 314 | } 315 | } 316 | 317 | for bb in &mut fcn.blocks { 318 | bb.statements.retain(|s| match s { 319 | Statement::SpanComment(ref c) if c.len() == 0 => false, 320 | _ => true, 321 | }); 322 | } 323 | 324 | rv 325 | } 326 | 327 | fn clean_unused_blocks(logger: &mut Logger, fcn: &mut crate::mir::Function) 328 | { 329 | log_debug!(logger, "clean_unused_blocks"); 330 | // Determine if each block is referenced 331 | loop { 332 | let mut used: Vec<_> = (0..fcn.blocks.len()).map(|_| false).collect(); 333 | used[0] = true; 334 | helpers::visit_block_targets_mut(fcn, |&mut tgt| used[tgt] = true); 335 | let mut changed = false; 336 | for (used,bb) in Iterator::zip(used.into_iter(), fcn.blocks.iter_mut()) { 337 | match bb.terminator { 338 | Terminator::Removed if bb.statements.is_empty() => {}, 339 | _ if !used => { 340 | bb.statements.clear(); 341 | bb.terminator = Terminator::Removed; 342 | changed = true; 343 | }, 344 | _ => {}, 345 | } 346 | } 347 | if !changed { 348 | break; 349 | } 350 | } 351 | 352 | let mut new_idx = 0; 353 | let mapping: Vec<_> = fcn.blocks.iter() 354 | .map(|bb| { 355 | if let Terminator::Removed = bb.terminator { 356 | None 357 | } 358 | else { 359 | let v = new_idx; 360 | new_idx += 1; 361 | Some(v) 362 | } 363 | }) 364 | .collect() 365 | ; 366 | fcn.blocks.retain(|bb| match bb.terminator { 367 | Terminator::Removed => false, 368 | _ => true, 369 | }); 370 | helpers::visit_block_targets_mut(fcn, |tgt: &mut usize| *tgt = match mapping[*tgt] 371 | { 372 | Some(v) => v, 373 | None => log_panic!(logger, "Block {tgt} still referenced but was deleted"), 374 | }); 375 | } 376 | 377 | mod helpers { 378 | use crate::mir::{Function,Terminator, Value}; 379 | pub fn visit_block_targets_mut(fcn: &mut Function, mut check: impl FnMut(&mut usize)) { 380 | for bb in &mut fcn.blocks { 381 | match &mut bb.terminator { 382 | Terminator::Removed => {}, 383 | Terminator::Invalid => {}, 384 | Terminator::Return => {}, 385 | Terminator::Diverge => {}, 386 | Terminator::Goto(ref mut tgt) => check(tgt), 387 | Terminator::Call(call) => { 388 | check(&mut call.bb_panic); 389 | check(&mut call.bb_ret); 390 | }, 391 | Terminator::If(_, bb_true, bb_false) => { 392 | check(bb_true); 393 | check(bb_false); 394 | }, 395 | Terminator::SwitchValue(_, _, targets, bb_default) => { 396 | for t in targets { 397 | check(t); 398 | } 399 | check(bb_default); 400 | } 401 | } 402 | } 403 | } 404 | 405 | pub fn visit_slot_in_param(param: &crate::mir::Param, mut fcn: impl FnMut(&crate::mir::Slot)) { 406 | if let crate::mir::Param::Slot(s) = param { 407 | fcn(s); 408 | } 409 | } 410 | pub fn visit_slot_in_value(value: &Value, mut fcn: impl FnMut(&crate::mir::Slot)) { 411 | match value { 412 | Value::Constant(_) => {}, 413 | Value::Use(v) => { fcn(v); }, 414 | Value::Borrow(_, v) => { fcn(v); }, 415 | Value::BinOp(a, _, b) => { 416 | visit_slot_in_param(a, &mut fcn); 417 | visit_slot_in_param(b, &mut fcn); 418 | }, 419 | Value::UniOp(_, a) => { 420 | fcn(a); 421 | }, 422 | Value::Cast(a, _) => { fcn(a) }, 423 | Value::DstPtr(a) => { fcn(a) }, 424 | Value::DstMeta(a) => { fcn(a) }, 425 | Value::Tuple(ents) => { 426 | for p in ents { 427 | visit_slot_in_param(p, &mut fcn); 428 | } 429 | }, 430 | Value::Array(_) => todo!(), 431 | Value::Struct(_, _) => todo!(), 432 | Value::UnionVariant(_, _, _) => todo!(), 433 | Value::EnumVariant(_, _, _) => todo!(), 434 | } 435 | } 436 | 437 | pub fn visit_terminator_targets(term: &Terminator, state: S, mut check: impl FnMut(S, usize)) -> Option 438 | { 439 | match *term { 440 | Terminator::Removed => Some(state), 441 | Terminator::Invalid => Some(state), 442 | Terminator::Return => Some(state), 443 | Terminator::Diverge => Some(state), 444 | Terminator::Goto(blk) => { 445 | check(state, blk); 446 | None 447 | }, 448 | Terminator::Call(ref call) => { 449 | check(state.clone(), call.bb_ret); 450 | check(state, call.bb_panic); 451 | None 452 | }, 453 | Terminator::If(_, bb_true, bb_false) => { 454 | check(state.clone(), bb_true); 455 | check(state, bb_false); 456 | None 457 | }, 458 | Terminator::SwitchValue(_, _, ref targets, bb_default) => { 459 | for &t in targets { 460 | check(state.clone(), t); 461 | } 462 | check(state, bb_default); 463 | None 464 | } 465 | } 466 | } 467 | 468 | 469 | // If `cb` returns true, the path is stopped 470 | pub enum PathVisit { 471 | Continue, 472 | StopArm, 473 | StopAll, 474 | } 475 | pub fn visit_path_from_bb(logger: &crate::logger::Logger, fcn: &crate::mir::Function, bb_idx: usize, mut cb: impl FnMut(usize, usize)->PathVisit) { 476 | let _ = logger; 477 | struct Counter(::std::cell::Cell); 478 | impl Counter { 479 | fn get(&self) -> usize { 480 | let rv = self.0.get(); 481 | self.0.set(rv + 1); 482 | rv 483 | } 484 | } 485 | struct State<'a> { 486 | state_idx: usize, 487 | next_state_idx: &'a Counter, 488 | cur_bb: u32, 489 | visited: crate::helper_types::Bitmap, 490 | } 491 | impl Clone for State<'_> { 492 | fn clone(&self) -> Self { 493 | Self { 494 | state_idx: self.next_state_idx.get(), 495 | next_state_idx: self.next_state_idx, 496 | cur_bb: self.cur_bb, 497 | visited: self.visited.clone() 498 | } 499 | } 500 | } 501 | let next_state_idx = Counter(Default::default()); 502 | let mut visited = crate::helper_types::Bitmap::new(fcn.blocks.len()); 503 | visited.set(bb_idx); 504 | let mut stack: Vec> = Vec::new(); 505 | stack.push(State { 506 | state_idx: next_state_idx.get(), 507 | next_state_idx: &next_state_idx, 508 | cur_bb: bb_idx as u32, 509 | visited, 510 | }); 511 | let mut abort = false; 512 | while let Some(s) = stack.pop() { 513 | if abort { 514 | break; 515 | } 516 | //log_debug!(logger, "-- {}: BB{}", s.state_idx, s.cur_bb); 517 | visit_terminator_targets(&fcn.blocks[s.cur_bb as usize].terminator, s, |mut s, idx| { 518 | match cb(s.state_idx, idx) 519 | { 520 | PathVisit::StopAll => { 521 | abort = true; 522 | }, 523 | PathVisit::StopArm => { 524 | // Stop processing this state, just let `s` drop 525 | }, 526 | PathVisit::Continue => { 527 | if s.visited.get(idx) { 528 | // We've already visited this block 529 | } 530 | else { 531 | s.visited.set(idx); 532 | s.cur_bb = idx as u32; 533 | stack.push(s); 534 | } 535 | } 536 | } 537 | }); 538 | } 539 | } 540 | } 541 | -------------------------------------------------------------------------------- /tools/mmir_opt/src/optimise/const_propagate.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! 3 | //! 4 | use crate::mir::Terminator; 5 | use crate::mir::Statement; 6 | use crate::mir::Value; 7 | use super::helpers; 8 | 9 | pub(super) fn const_propagate(logger: &mut crate::logger::Logger, fcn: &mut crate::mir::Function) -> bool 10 | { 11 | log_debug!(logger, "const_propagate"); 12 | use crate::mir::Const; 13 | 14 | let mut rv = false; 15 | let usage_count = { 16 | let mut usage_count: Vec<_> = (0..fcn.blocks.len()).map(|_| 0).collect(); 17 | usage_count[0] = 1; 18 | helpers::visit_block_targets_mut(fcn, |&mut tgt| usage_count[tgt] += 1); 19 | usage_count 20 | }; 21 | 22 | let mut known_values = ::std::collections::HashMap::new(); 23 | for bb in &mut fcn.blocks { 24 | known_values.clear(); 25 | 26 | fn get_for_slot<'a>( 27 | known_values: &::std::collections::HashMap, 28 | lv: &crate::mir::Slot 29 | ) -> Option<&'a crate::mir::Const> { 30 | if let Some(i) = lv.is_local() { 31 | if let Some(&v) = known_values.get(&i) { 32 | return Some(v); 33 | } 34 | } 35 | None 36 | } 37 | fn get_for_param<'a>( 38 | known_values: &::std::collections::HashMap, 39 | lv: &crate::mir::Param 40 | ) -> Option<&'a crate::mir::Const> { 41 | if let crate::mir::Param::Slot(lv) = lv { 42 | return get_for_slot(known_values, lv); 43 | } 44 | None 45 | } 46 | for stmt in &mut bb.statements { 47 | match stmt { 48 | Statement::SpanComment(_) => {}, 49 | Statement::Assign(dst, src) => { 50 | if let Some(i) = dst.is_local() { 51 | known_values.remove(&i); 52 | } 53 | match src 54 | { 55 | Value::Constant(c) => { 56 | if let Some(idx) = dst.is_local() { 57 | known_values.insert(idx, &*c); 58 | } 59 | }, 60 | Value::Use(v) => { 61 | if let Some(c) = get_for_slot(&known_values, v) { 62 | *src = Value::Constant(c.clone()); 63 | rv = true; 64 | } 65 | } 66 | Value::Borrow(_, slot) => { 67 | if let Some(i) = slot.is_local() { 68 | known_values.remove(&i); 69 | } 70 | }, 71 | Value::BinOp(a, op, b) => { 72 | if let Some(c) = get_for_param(&known_values, a) { 73 | *a = crate::mir::Param::Const(c.clone()); 74 | rv = true; 75 | } 76 | if let Some(c) = get_for_param(&known_values, b) { 77 | *b = crate::mir::Param::Const(c.clone()); 78 | rv = true; 79 | } 80 | if let (crate::mir::Param::Const(a), crate::mir::Param::Const(b)) = (a,b) { 81 | use crate::mir::{BinOp,Const}; 82 | fn cu(v: u128, bits: &crate::types::Bits) -> Value { 83 | Value::Constant(Const::Unsigned(bits.mask_unsigned(v), *bits)) 84 | } 85 | fn cs(v: i128, bits: &crate::types::Bits) -> Value { 86 | Value::Constant(Const::Signed(bits.mask_signed(v), *bits)) 87 | } 88 | fn cf(v: f64, bits: &crate::types::Bits) -> Value { 89 | Value::Constant(Const::Float(v, *bits)) 90 | } 91 | match op { 92 | BinOp::Add => match (a,b) 93 | { 94 | (Const::Unsigned(a, bits),Const::Unsigned(b, _)) => *src = cu(*a + *b, bits), 95 | (Const::Signed (a, bits),Const::Signed (b, _)) => *src = cs(*a + *b, bits), 96 | (Const::Float(a, bits), Const::Float(b, _)) => *src = cf(*a + *b, bits), 97 | _ => {}, 98 | }, 99 | BinOp::Sub => match (a,b) 100 | { 101 | (Const::Unsigned(a, bits),Const::Unsigned(b, _)) => *src = cu(*a - *b, bits), 102 | (Const::Signed (a, bits),Const::Signed (b, _)) => *src = cs(*a - *b, bits), 103 | (Const::Float(a, bits), Const::Float(b, _)) => *src = cf(*a - *b, bits), 104 | _ => {}, 105 | }, 106 | BinOp::Mul => match (a,b) 107 | { 108 | (Const::Unsigned(a, bits),Const::Unsigned(b, _)) => *src = cu(*a * *b, bits), 109 | (Const::Signed (a, bits),Const::Signed (b, _)) => *src = cs(*a * *b, bits), 110 | (Const::Float(a, bits), Const::Float(b, _)) => *src = cf(*a * *b, bits), 111 | _ => {}, 112 | }, 113 | BinOp::Div => match (a,b) 114 | { 115 | (Const::Unsigned(a, bits),Const::Unsigned(b, _)) => *src = cu(*a / *b, bits), 116 | (Const::Signed (a, bits),Const::Signed (b, _)) => *src = cs(*a / *b, bits), 117 | (Const::Float(a, bits), Const::Float(b, _)) => *src = cf(*a / *b, bits), 118 | _ => {}, 119 | }, 120 | BinOp::Rem => match (a,b) 121 | { 122 | (Const::Unsigned(a, bits),Const::Unsigned(b, _)) => *src = cu(*a % *b, bits), 123 | (Const::Signed (a, bits),Const::Signed (b, _)) => *src = cs(*a % *b, bits), 124 | (Const::Float(a, bits), Const::Float(b, _)) => *src = cf(*a % *b, bits), 125 | _ => {}, 126 | }, 127 | BinOp::Shr => match (a,b) 128 | { 129 | (Const::Unsigned(a, bits),Const::Unsigned(b, _)) => *src = cu(*a >> *b, bits), 130 | (Const::Unsigned(a, bits),Const::Signed (b, _)) => *src = cu(*a >> *b, bits), 131 | _ => {}, 132 | }, 133 | BinOp::Shl => match (a,b) 134 | { 135 | (Const::Unsigned(a, bits),Const::Unsigned(b, _)) => *src = cu(*a << *b, bits), 136 | (Const::Unsigned(a, bits),Const::Signed (b, _)) => *src = cu(*a << *b, bits), 137 | _ => {}, 138 | }, 139 | BinOp::BitAnd => match (a,b) 140 | { 141 | (Const::Unsigned(a, bits),Const::Unsigned(b, _)) => *src = cu(*a & *b, bits), 142 | (Const::Signed (a, bits),Const::Signed (b, _)) => *src = cs(*a & *b, bits), 143 | _ => {}, 144 | }, 145 | BinOp::BitOr => match (a,b) 146 | { 147 | (Const::Unsigned(a, bits),Const::Unsigned(b, _)) => *src = cu(*a | *b, bits), 148 | (Const::Signed (a, bits),Const::Signed (b, _)) => *src = cs(*a | *b, bits), 149 | _ => {}, 150 | }, 151 | BinOp::BitXor => match (a,b) 152 | { 153 | (Const::Unsigned(a, bits),Const::Unsigned(b, _)) => *src = cu(*a ^ *b, bits), 154 | (Const::Signed (a, bits),Const::Signed (b, _)) => *src = cs(*a ^ *b, bits), 155 | _ => {}, 156 | }, 157 | BinOp::Less => *src = Value::Constant(Const::Boolean( a < b )), 158 | BinOp::Greater => *src = Value::Constant(Const::Boolean( a > b )), 159 | BinOp::LessEqual => *src = Value::Constant(Const::Boolean( a <= b )), 160 | BinOp::GreaterEqual => *src = Value::Constant(Const::Boolean( a >= b )), 161 | BinOp::Equals => *src = Value::Constant(Const::Boolean( a == b )), 162 | BinOp::NotEquals => *src = Value::Constant(Const::Boolean( a != b )), 163 | } 164 | } 165 | }, 166 | Value::UniOp(op, a) => { 167 | if let Some(c) = get_for_slot(&known_values, a) { 168 | *src = Value::Constant(match op { 169 | crate::mir::UniOp::Inv => match c { 170 | Const::Boolean(_) => todo!("Evaluate UniOp {:?} {:?}", op, c), 171 | Const::Unsigned(v, bits) => Const::Unsigned(bits.mask_unsigned(!*v), bits.clone()), 172 | Const::Signed(v, bits) => Const::Signed(!*v, bits.clone()), 173 | Const::Float(_, _) => todo!("Evaluate UniOp {:?} {:?}", op, c), 174 | Const::String(_) => todo!("Evaluate UniOp {:?} {:?}", op, c), 175 | Const::ItemAddr(_) => todo!("Evaluate UniOp {:?} {:?}", op, c), 176 | }, 177 | crate::mir::UniOp::Neg => match c { 178 | Const::Boolean(_) => todo!("Evaluate UniOp {:?} {:?}", op, c), 179 | Const::Unsigned(_, _) => todo!("Evaluate UniOp {:?} {:?}", op, c), 180 | Const::Signed(v, bits) => Const::Signed(-*v, bits.clone()), 181 | Const::Float(v, bits) => Const::Float(-*v, bits.clone()), 182 | Const::String(_) => todo!("Evaluate UniOp {:?} {:?}", op, c), 183 | Const::ItemAddr(_) => todo!("Evaluate UniOp {:?} {:?}", op, c), 184 | }, 185 | }); 186 | rv = true; 187 | } 188 | }, 189 | Value::Cast(v, ty) => { 190 | if let Some(c) = get_for_slot(&known_values, v) { 191 | use crate::types::Root; 192 | let v = match &ty.root { 193 | Root::Unsigned(bits) if ty.wrappers.is_empty() => { 194 | let new_v = match c 195 | { 196 | Const::Boolean(v) => *v as u128, 197 | Const::Unsigned(v, _bits) => *v, 198 | Const::Signed(v, _bits) => *v as u128, 199 | Const::Float(_, _) => todo!(), 200 | Const::String(_) 201 | | Const::ItemAddr(_) => panic!("Malformed cast: {:?} to {:?}", c, ty), 202 | }; 203 | Some(Const::Unsigned(new_v, bits.clone())) 204 | }, 205 | Root::Signed(bits) if ty.wrappers.is_empty() => { 206 | let new_v = match c 207 | { 208 | Const::Boolean(v) => *v as i128, 209 | Const::Unsigned(v, _bits) => *v as i128, 210 | Const::Signed(v, _bits) => *v, 211 | Const::Float(_, _) => todo!(), 212 | Const::String(_) 213 | | Const::ItemAddr(_) => panic!("Malformed cast: {:?} to {:?}", c, ty), 214 | }; 215 | Some(Const::Signed(new_v, bits.clone())) 216 | }, 217 | Root::Float(bits) if ty.wrappers.is_empty() => { 218 | let new_v = match c 219 | { 220 | Const::Boolean(_) => panic!("Malformed cast: {:?} to {:?}", c, ty), 221 | Const::Unsigned(v, _bits) => *v as f64, 222 | Const::Signed(v, _bits) => *v as f64, 223 | Const::Float(_, _) => todo!(), 224 | Const::String(_) 225 | | Const::ItemAddr(_) => panic!("Malformed cast: {:?} to {:?}", c, ty), 226 | }; 227 | Some(Const::Float(new_v, bits.clone())) 228 | }, 229 | _ => None, 230 | }; 231 | if let Some(c) = v { 232 | *src = Value::Constant(c); 233 | rv = true; 234 | } 235 | } 236 | }, 237 | Value::DstPtr(_) => {}, 238 | Value::DstMeta(_) => {}, 239 | Value::Tuple(vals) => { 240 | for v in vals { 241 | if let Some(c) = get_for_param(&known_values, v) { 242 | *v = crate::mir::Param::Const(c.clone()); 243 | rv = true; 244 | } 245 | } 246 | }, 247 | Value::Array(vals) => { 248 | for v in vals { 249 | if let Some(c) = get_for_param(&known_values, v) { 250 | *v = crate::mir::Param::Const(c.clone()); 251 | rv = true; 252 | } 253 | } 254 | }, 255 | Value::Struct(_, vals) => { 256 | for v in vals { 257 | if let Some(c) = get_for_param(&known_values, v) { 258 | *v = crate::mir::Param::Const(c.clone()); 259 | rv = true; 260 | } 261 | } 262 | }, 263 | Value::UnionVariant(_, _, _) => {}, 264 | Value::EnumVariant(_, _, _) => {}, 265 | } 266 | }, 267 | } 268 | } 269 | 270 | match bb.terminator { 271 | Terminator::Removed => {}, 272 | Terminator::Invalid => {}, 273 | Terminator::Return => {}, 274 | Terminator::Diverge => {}, 275 | Terminator::Goto(tgt) => if usage_count[tgt] == 1 { 276 | // Continue into this block 277 | }, 278 | Terminator::Call(ref call) => { 279 | if usage_count[call.bb_ret] == 1 { 280 | // Continue into this block 281 | } 282 | }, 283 | Terminator::If(_, bb_true, bb_false) => { 284 | if usage_count[bb_true] == 1 { 285 | // Continue into this block 286 | } 287 | if usage_count[bb_false] == 1 { 288 | // Continue into this block 289 | } 290 | }, 291 | Terminator::SwitchValue(ref value, _, ref targets, bb_default) => { 292 | if let Some(c) = get_for_slot(&known_values, value) { 293 | todo!("Replace SwitchValue with a jump due to known value - {:?}", c); 294 | } 295 | for &t in targets { 296 | if usage_count[t] == 1 { 297 | // Continue into this block 298 | } 299 | } 300 | if usage_count[bb_default] == 1 { 301 | // Continue into this block 302 | } 303 | 304 | } 305 | } 306 | } 307 | rv 308 | } 309 | -------------------------------------------------------------------------------- /tools/mmir_opt/src/types.rs: -------------------------------------------------------------------------------- 1 | #[derive(PartialEq, Debug)] 2 | pub struct TypeRef 3 | { 4 | pub root: Root, 5 | pub wrappers: Vec, 6 | } 7 | impl TypeRef { 8 | pub fn root(root: Root) -> Self { 9 | TypeRef { root, wrappers: Vec::new() } 10 | } 11 | pub fn unit() -> Self { 12 | TypeRef { root: Root::Tuple(Vec::new()), wrappers: Vec::new() } 13 | } 14 | 15 | pub fn is_unit(&self) -> bool { 16 | match self.root { 17 | Root::Tuple(ref ents) if ents.is_empty() && self.wrappers.is_empty() => true, 18 | _ => false, 19 | } 20 | } 21 | pub fn wrapped(mut self, w: Wrapper) -> Self { 22 | self.wrappers.push(w); 23 | self 24 | } 25 | } 26 | #[derive(PartialEq, Debug, Copy, Clone)] 27 | pub enum Mutability { 28 | Shared, 29 | Unique, 30 | Move, 31 | } 32 | impl Mutability { 33 | pub fn to_str(&self) -> &'static str { 34 | match self { 35 | Mutability::Shared => "const", 36 | Mutability::Unique => "mut", 37 | Mutability::Move => "move", 38 | } 39 | } 40 | } 41 | 42 | #[derive(PartialEq, Debug)] 43 | pub enum Root { 44 | Diverge, 45 | Str, 46 | Unsigned(Bits), 47 | Signed(Bits), 48 | Float(Bits), 49 | Named(String), 50 | Tuple(Vec), 51 | Function(Box), 52 | } 53 | #[derive(PartialEq, Debug)] 54 | pub enum Wrapper { 55 | Slice, 56 | Array(usize), 57 | Pointer(Mutability), 58 | Borrow(Mutability), 59 | } 60 | #[repr(transparent)] 61 | #[derive(PartialEq,PartialOrd, Debug, Copy, Clone)] 62 | pub struct Bits(u8); 63 | impl Bits { 64 | pub const SIZE: Bits = Bits(0xFF); 65 | pub const _8: Bits = Bits(3); 66 | pub const _16: Bits = Bits(4); 67 | pub const _32: Bits = Bits(5); 68 | pub const _64: Bits = Bits(6); 69 | pub const _128: Bits = Bits(7); 70 | } 71 | impl Bits { 72 | pub fn mask_unsigned(&self, v: u128) -> u128 { 73 | if self.0 == 0xFF { 74 | v & (1 << 64)-1 75 | } 76 | else if self.0 >= 7 { 77 | v 78 | } 79 | else { 80 | v & (1 << (8*self.0))-1 81 | } 82 | } 83 | pub fn mask_signed(&self, v: i128) -> i128 { 84 | if v < 0 { 85 | let v = -v; 86 | -(self.mask_unsigned(v as u128) as i128) 87 | } 88 | else { 89 | self.mask_unsigned(v as u128) as i128 90 | } 91 | } 92 | } 93 | impl ::std::fmt::Display for Bits { 94 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 95 | if self.0 == 0xFF { 96 | "size".fmt(f) 97 | } 98 | else { 99 | (1 << self.0).fmt(f) 100 | } 101 | } 102 | } 103 | #[derive(PartialEq, Debug)] 104 | pub struct FcnTy { 105 | pub abi: String, 106 | pub args: Vec, 107 | pub is_variadic: bool, 108 | pub ret: TypeRef, 109 | } -------------------------------------------------------------------------------- /tools/mmir_opt/src/validate.rs: -------------------------------------------------------------------------------- 1 | use crate::mir::Terminator; 2 | use crate::mir::Statement; 3 | use crate::mir::Value; 4 | use crate::logger::Logger; 5 | 6 | pub fn validate_function(logger: &mut Logger, fcn: &mut crate::mir::Function, _sig: &crate::types::FcnTy) 7 | { 8 | // Check 9 | set_before_use(logger, fcn, _sig); 10 | } 11 | 12 | trait Vistior { 13 | fn set_location(&mut self, location: crate::LineRef) {let _ = location;} 14 | fn slot_write(&mut self, slot: &crate::mir::Slot); 15 | fn slot_borrow(&mut self, slot: &crate::mir::Slot, mutablity: crate::types::Mutability); 16 | fn slot_read(&mut self, slot: &crate::mir::Slot); 17 | } 18 | fn visit_slot_wrappers(slot: &crate::mir::Slot, visitor: &mut V) 19 | { 20 | for (i,w) in slot.wrappers.iter().enumerate().rev() { 21 | match w { 22 | crate::mir::SlotWrapper::Deref => { 23 | visitor.slot_read(&crate::mir::Slot { root: slot.root.clone(), wrappers: slot.wrappers[..i].to_owned() }); 24 | } 25 | crate::mir::SlotWrapper::Index(var) => { 26 | visitor.slot_read(&crate::mir::Slot { root: crate::mir::SlotRoot::Local(*var), wrappers: vec![] }); 27 | } 28 | crate::mir::SlotWrapper::Field(_) => {}, 29 | crate::mir::SlotWrapper::Downcast(_) => {}, 30 | } 31 | } 32 | } 33 | impl Vistior for &mut V { 34 | 35 | fn set_location(&mut self, location: crate::LineRef) { 36 | (**self).set_location(location); 37 | } 38 | 39 | fn slot_write(&mut self, slot: &crate::mir::Slot) { 40 | (**self).slot_write(slot) 41 | } 42 | 43 | fn slot_borrow(&mut self, slot: &crate::mir::Slot, mutablity: crate::types::Mutability) { 44 | (**self).slot_borrow(slot, mutablity) 45 | } 46 | 47 | fn slot_read(&mut self, slot: &crate::mir::Slot) { 48 | (**self).slot_read(slot) 49 | } 50 | } 51 | fn visit_slot_read(slot: &crate::mir::Slot, visitor: &mut V) 52 | { 53 | visit_slot_wrappers(slot, visitor); 54 | visitor.slot_read(slot) 55 | } 56 | fn visit_param(param: &crate::mir::Param, visitor: &mut V) 57 | { 58 | match param { 59 | crate::mir::Param::Const(_) => {}, 60 | crate::mir::Param::Slot(slot) => visit_slot_read(slot, visitor), 61 | } 62 | } 63 | fn visit_function(fcn: &mut crate::mir::Function, visitor: &mut V) 64 | where 65 | V: Vistior 66 | { 67 | for (bb_idx, block) in fcn.blocks.iter().enumerate() 68 | { 69 | for (stmt_idx, stmt) in block.statements.iter().enumerate() 70 | { 71 | visitor.set_location(crate::LineRef { bb_idx, stmt_idx: crate::StmtIdx::Stmt(stmt_idx as u16) }); 72 | match stmt { 73 | Statement::SpanComment(_) => {}, 74 | Statement::Assign(dst, src) => { 75 | match src { 76 | Value::Constant(_) => {}, 77 | Value::Use(slot) => { 78 | visit_slot_read(slot, visitor); 79 | }, 80 | Value::Borrow(mutability, slot) => { 81 | visit_slot_wrappers(slot, visitor); 82 | visitor.slot_borrow(slot, *mutability); 83 | }, 84 | Value::BinOp(param1, _, param2) => { 85 | visit_param(param1, visitor); 86 | visit_param(param2, visitor); 87 | }, 88 | Value::UniOp(_, slot) => { 89 | visit_slot_read(slot, visitor); 90 | }, 91 | Value::Cast(slot, _) => { 92 | visit_slot_read(slot, visitor); 93 | } 94 | Value::DstPtr(slot) => { 95 | visit_slot_wrappers(slot, visitor); 96 | visitor.slot_read(slot/*, true*/); 97 | }, 98 | Value::DstMeta(slot) => { 99 | visit_slot_wrappers(slot, visitor); 100 | visitor.slot_read(slot/*, true*/); 101 | }, 102 | Value::Array(vals) 103 | | Value::Tuple(vals) 104 | | Value::Struct(_, vals) 105 | | Value::EnumVariant(_, _, vals) 106 | => { 107 | for param in vals { 108 | visit_param(param, visitor); 109 | } 110 | }, 111 | Value::UnionVariant(_, _, param) => visit_param(param, visitor), 112 | } 113 | visit_slot_wrappers(dst, visitor); 114 | visitor.slot_write(dst); 115 | }, 116 | } 117 | } 118 | visitor.set_location(crate::LineRef { bb_idx, stmt_idx: crate::StmtIdx::Term }); 119 | match &block.terminator { 120 | Terminator::Removed => {}, 121 | Terminator::Invalid => {}, 122 | Terminator::Return => {}, 123 | Terminator::Diverge => {}, 124 | Terminator::Call(tc) => { 125 | match &tc.target { 126 | crate::mir::CallTarget::Path(_) => {}, 127 | crate::mir::CallTarget::Intrinsic(_name, _tys) => {}, 128 | crate::mir::CallTarget::Value(slot) => visit_slot_read(slot, visitor), 129 | } 130 | visit_slot_wrappers(&tc.dst, visitor); 131 | visitor.slot_write(&tc.dst); 132 | }, 133 | Terminator::Goto(_) => {}, 134 | Terminator::If(slot, _, _) => visit_slot_read(slot, visitor), 135 | Terminator::SwitchValue(slot, _switch_values, _vec, _) => visit_slot_read(slot, visitor), 136 | } 137 | } 138 | } 139 | 140 | fn set_before_use(logger: &mut Logger, fcn: &mut crate::mir::Function, _sig: &crate::types::FcnTy) 141 | { 142 | struct GetSet { 143 | set_locals: Vec, 144 | } 145 | impl Vistior for GetSet { 146 | fn slot_write(&mut self, slot: &crate::mir::Slot) { 147 | if let Some(v) = slot.is_local() { 148 | self.set_locals[v] = true; 149 | } 150 | // Just assume any write makes the slot valid 151 | if ! slot.wrappers.iter().any(|w| matches!(w, crate::mir::SlotWrapper::Deref)) { 152 | if let crate::mir::SlotRoot::Local(v) = slot.root { 153 | self.set_locals[v] = true; 154 | } 155 | } 156 | } 157 | 158 | fn slot_borrow(&mut self, _slot: &crate::mir::Slot, _mutablity: crate::types::Mutability) { 159 | } 160 | 161 | fn slot_read(&mut self, _slot: &crate::mir::Slot) { 162 | } 163 | } 164 | let mut visitor_get_set = GetSet { 165 | set_locals: vec![false; fcn.locals.len()], 166 | }; 167 | visit_function(fcn, &mut visitor_get_set); 168 | 169 | struct CheckSet<'a,'l,'l2> { 170 | location: crate::LineRef, 171 | logger: &'l mut Logger<'l2>, 172 | gs: &'a GetSet, 173 | //fail: bool, 174 | } 175 | impl CheckSet<'_,'_,'_> { 176 | fn check_slot(&mut self, slot: &crate::mir::Slot) { 177 | if let Some(v) = slot.is_local() { 178 | if !self.gs.set_locals[v] { 179 | self.logger.error(format_args!("{}: Local #{v} used without ever being set", self.location)); 180 | //self.fail = true; 181 | } 182 | } 183 | } 184 | } 185 | impl Vistior for CheckSet<'_,'_,'_> { 186 | fn set_location(&mut self, location: crate::LineRef) { 187 | self.location = location; 188 | } 189 | 190 | fn slot_write(&mut self, _slot: &crate::mir::Slot) { 191 | } 192 | 193 | fn slot_borrow(&mut self, slot: &crate::mir::Slot, _mutablity: crate::types::Mutability) { 194 | self.check_slot(slot); 195 | } 196 | fn slot_read(&mut self, slot: &crate::mir::Slot) { 197 | self.check_slot(slot); 198 | } 199 | } 200 | 201 | visit_function(fcn, &mut CheckSet { 202 | location: crate::LineRef { bb_idx: 0, stmt_idx: crate::StmtIdx::Term }, 203 | logger, 204 | gs: &visitor_get_set, 205 | //fail: false, 206 | }); 207 | } --------------------------------------------------------------------------------