├── .gitignore ├── .rustfmt.toml ├── CHANGELOG.md ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── build.rs └── src ├── ast.rs ├── codegen ├── builder.rs ├── func.rs ├── gen.rs ├── info.rs ├── mod.rs └── values.rs ├── irgen ├── eval.rs ├── func.rs ├── gen.rs ├── mod.rs ├── scopes.rs └── values.rs ├── main.rs └── sysy.lalrpop /.gitignore: -------------------------------------------------------------------------------- 1 | # macOS 2 | *.DS_Store 3 | 4 | # Rust 5 | /target 6 | 7 | # debugging 8 | debug 9 | -------------------------------------------------------------------------------- /.rustfmt.toml: -------------------------------------------------------------------------------- 1 | tab_spaces = 2 2 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to the `kira-rs` will be documented in this file. 4 | 5 | ## 0.0.4 - 2023-06-02 6 | 7 | ### Changed 8 | 9 | * Bumped dependency `koopa` to version 0.0.7. 10 | * Bumped dependency `lalrpop` to version 0.20.0. 11 | 12 | ## 0.0.3 - 2023-01-12 13 | 14 | ### Changed 15 | 16 | * Bumped dependency `regex` to version 1.5.6. 17 | * Bumped dependency `koopa` to version 0.0.6. 18 | 19 | ### Fixed 20 | 21 | * Some deprecated and non-recommended uses in the source code. 22 | 23 | ## 0.0.2 - 2022-02-17 24 | 25 | ### Changed 26 | 27 | * Changed the constant type of evaluator from `i64` to `i32`. 28 | 29 | ### Fixed 30 | 31 | * Problems about function parameters. 32 | * Problems about array pointers. 33 | * Problems about values in stack frame. 34 | 35 | ## 0.0.1 - 2022-01-27 36 | -------------------------------------------------------------------------------- /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 = "0.7.18" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" 10 | dependencies = [ 11 | "memchr", 12 | ] 13 | 14 | [[package]] 15 | name = "ascii-canvas" 16 | version = "3.0.0" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "8824ecca2e851cec16968d54a01dd372ef8f95b244fb84b84e70128be347c3c6" 19 | dependencies = [ 20 | "term", 21 | ] 22 | 23 | [[package]] 24 | name = "atty" 25 | version = "0.2.14" 26 | source = "registry+https://github.com/rust-lang/crates.io-index" 27 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 28 | dependencies = [ 29 | "hermit-abi 0.1.19", 30 | "libc", 31 | "winapi", 32 | ] 33 | 34 | [[package]] 35 | name = "autocfg" 36 | version = "1.0.1" 37 | source = "registry+https://github.com/rust-lang/crates.io-index" 38 | checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" 39 | 40 | [[package]] 41 | name = "bit-set" 42 | version = "0.5.2" 43 | source = "registry+https://github.com/rust-lang/crates.io-index" 44 | checksum = "6e11e16035ea35e4e5997b393eacbf6f63983188f7a2ad25bfb13465f5ad59de" 45 | dependencies = [ 46 | "bit-vec", 47 | ] 48 | 49 | [[package]] 50 | name = "bit-vec" 51 | version = "0.6.3" 52 | source = "registry+https://github.com/rust-lang/crates.io-index" 53 | checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" 54 | 55 | [[package]] 56 | name = "bitflags" 57 | version = "1.3.2" 58 | source = "registry+https://github.com/rust-lang/crates.io-index" 59 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 60 | 61 | [[package]] 62 | name = "cc" 63 | version = "1.0.79" 64 | source = "registry+https://github.com/rust-lang/crates.io-index" 65 | checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" 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 = "colored" 75 | version = "2.0.0" 76 | source = "registry+https://github.com/rust-lang/crates.io-index" 77 | checksum = "b3616f750b84d8f0de8a58bda93e08e2a81ad3f523089b05f1dffecab48c6cbd" 78 | dependencies = [ 79 | "atty", 80 | "lazy_static", 81 | "winapi", 82 | ] 83 | 84 | [[package]] 85 | name = "crunchy" 86 | version = "0.2.2" 87 | source = "registry+https://github.com/rust-lang/crates.io-index" 88 | checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" 89 | 90 | [[package]] 91 | name = "diff" 92 | version = "0.1.12" 93 | source = "registry+https://github.com/rust-lang/crates.io-index" 94 | checksum = "0e25ea47919b1560c4e3b7fe0aaab9becf5b84a10325ddf7db0f0ba5e1026499" 95 | 96 | [[package]] 97 | name = "dirs-next" 98 | version = "2.0.0" 99 | source = "registry+https://github.com/rust-lang/crates.io-index" 100 | checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" 101 | dependencies = [ 102 | "cfg-if", 103 | "dirs-sys-next", 104 | ] 105 | 106 | [[package]] 107 | name = "dirs-sys-next" 108 | version = "0.1.2" 109 | source = "registry+https://github.com/rust-lang/crates.io-index" 110 | checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" 111 | dependencies = [ 112 | "libc", 113 | "redox_users", 114 | "winapi", 115 | ] 116 | 117 | [[package]] 118 | name = "either" 119 | version = "1.6.1" 120 | source = "registry+https://github.com/rust-lang/crates.io-index" 121 | checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" 122 | 123 | [[package]] 124 | name = "ena" 125 | version = "0.14.0" 126 | source = "registry+https://github.com/rust-lang/crates.io-index" 127 | checksum = "d7402b94a93c24e742487327a7cd839dc9d36fec9de9fb25b09f2dae459f36c3" 128 | dependencies = [ 129 | "log", 130 | ] 131 | 132 | [[package]] 133 | name = "errno" 134 | version = "0.3.1" 135 | source = "registry+https://github.com/rust-lang/crates.io-index" 136 | checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" 137 | dependencies = [ 138 | "errno-dragonfly", 139 | "libc", 140 | "windows-sys", 141 | ] 142 | 143 | [[package]] 144 | name = "errno-dragonfly" 145 | version = "0.1.2" 146 | source = "registry+https://github.com/rust-lang/crates.io-index" 147 | checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" 148 | dependencies = [ 149 | "cc", 150 | "libc", 151 | ] 152 | 153 | [[package]] 154 | name = "fixedbitset" 155 | version = "0.4.2" 156 | source = "registry+https://github.com/rust-lang/crates.io-index" 157 | checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" 158 | 159 | [[package]] 160 | name = "getrandom" 161 | version = "0.2.4" 162 | source = "registry+https://github.com/rust-lang/crates.io-index" 163 | checksum = "418d37c8b1d42553c93648be529cb70f920d3baf8ef469b74b9638df426e0b4c" 164 | dependencies = [ 165 | "cfg-if", 166 | "libc", 167 | "wasi", 168 | ] 169 | 170 | [[package]] 171 | name = "hashbrown" 172 | version = "0.11.2" 173 | source = "registry+https://github.com/rust-lang/crates.io-index" 174 | checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" 175 | 176 | [[package]] 177 | name = "hermit-abi" 178 | version = "0.1.19" 179 | source = "registry+https://github.com/rust-lang/crates.io-index" 180 | checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" 181 | dependencies = [ 182 | "libc", 183 | ] 184 | 185 | [[package]] 186 | name = "hermit-abi" 187 | version = "0.3.1" 188 | source = "registry+https://github.com/rust-lang/crates.io-index" 189 | checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" 190 | 191 | [[package]] 192 | name = "indexmap" 193 | version = "1.8.0" 194 | source = "registry+https://github.com/rust-lang/crates.io-index" 195 | checksum = "282a6247722caba404c065016bbfa522806e51714c34f5dfc3e4a3a46fcb4223" 196 | dependencies = [ 197 | "autocfg", 198 | "hashbrown", 199 | ] 200 | 201 | [[package]] 202 | name = "instant" 203 | version = "0.1.12" 204 | source = "registry+https://github.com/rust-lang/crates.io-index" 205 | checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" 206 | dependencies = [ 207 | "cfg-if", 208 | ] 209 | 210 | [[package]] 211 | name = "io-lifetimes" 212 | version = "1.0.11" 213 | source = "registry+https://github.com/rust-lang/crates.io-index" 214 | checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" 215 | dependencies = [ 216 | "hermit-abi 0.3.1", 217 | "libc", 218 | "windows-sys", 219 | ] 220 | 221 | [[package]] 222 | name = "is-terminal" 223 | version = "0.4.7" 224 | source = "registry+https://github.com/rust-lang/crates.io-index" 225 | checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" 226 | dependencies = [ 227 | "hermit-abi 0.3.1", 228 | "io-lifetimes", 229 | "rustix", 230 | "windows-sys", 231 | ] 232 | 233 | [[package]] 234 | name = "itertools" 235 | version = "0.10.3" 236 | source = "registry+https://github.com/rust-lang/crates.io-index" 237 | checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3" 238 | dependencies = [ 239 | "either", 240 | ] 241 | 242 | [[package]] 243 | name = "key-node-list" 244 | version = "0.0.5" 245 | source = "registry+https://github.com/rust-lang/crates.io-index" 246 | checksum = "653fee522596ae32adbb1ca91b9553be5b2d8e34556c6148899c9132a7a46295" 247 | 248 | [[package]] 249 | name = "kira" 250 | version = "0.0.4" 251 | dependencies = [ 252 | "koopa", 253 | "lalrpop", 254 | "lalrpop-util", 255 | "regex", 256 | ] 257 | 258 | [[package]] 259 | name = "koopa" 260 | version = "0.0.7" 261 | source = "registry+https://github.com/rust-lang/crates.io-index" 262 | checksum = "95041421f66312c40f397fcbbdbaed68f1090131f8e7e6b01d8cc896a598605b" 263 | dependencies = [ 264 | "colored", 265 | "key-node-list", 266 | ] 267 | 268 | [[package]] 269 | name = "lalrpop" 270 | version = "0.20.0" 271 | source = "registry+https://github.com/rust-lang/crates.io-index" 272 | checksum = "da4081d44f4611b66c6dd725e6de3169f9f63905421e8626fcb86b6a898998b8" 273 | dependencies = [ 274 | "ascii-canvas", 275 | "bit-set", 276 | "diff", 277 | "ena", 278 | "is-terminal", 279 | "itertools", 280 | "lalrpop-util", 281 | "petgraph", 282 | "pico-args", 283 | "regex", 284 | "regex-syntax 0.7.2", 285 | "string_cache", 286 | "term", 287 | "tiny-keccak", 288 | "unicode-xid", 289 | ] 290 | 291 | [[package]] 292 | name = "lalrpop-util" 293 | version = "0.20.0" 294 | source = "registry+https://github.com/rust-lang/crates.io-index" 295 | checksum = "3f35c735096c0293d313e8f2a641627472b83d01b937177fe76e5e2708d31e0d" 296 | dependencies = [ 297 | "regex", 298 | ] 299 | 300 | [[package]] 301 | name = "lazy_static" 302 | version = "1.4.0" 303 | source = "registry+https://github.com/rust-lang/crates.io-index" 304 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 305 | 306 | [[package]] 307 | name = "libc" 308 | version = "0.2.144" 309 | source = "registry+https://github.com/rust-lang/crates.io-index" 310 | checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1" 311 | 312 | [[package]] 313 | name = "linux-raw-sys" 314 | version = "0.3.8" 315 | source = "registry+https://github.com/rust-lang/crates.io-index" 316 | checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" 317 | 318 | [[package]] 319 | name = "lock_api" 320 | version = "0.4.5" 321 | source = "registry+https://github.com/rust-lang/crates.io-index" 322 | checksum = "712a4d093c9976e24e7dbca41db895dabcbac38eb5f4045393d17a95bdfb1109" 323 | dependencies = [ 324 | "scopeguard", 325 | ] 326 | 327 | [[package]] 328 | name = "log" 329 | version = "0.4.14" 330 | source = "registry+https://github.com/rust-lang/crates.io-index" 331 | checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" 332 | dependencies = [ 333 | "cfg-if", 334 | ] 335 | 336 | [[package]] 337 | name = "memchr" 338 | version = "2.4.1" 339 | source = "registry+https://github.com/rust-lang/crates.io-index" 340 | checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" 341 | 342 | [[package]] 343 | name = "new_debug_unreachable" 344 | version = "1.0.4" 345 | source = "registry+https://github.com/rust-lang/crates.io-index" 346 | checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" 347 | 348 | [[package]] 349 | name = "parking_lot" 350 | version = "0.11.2" 351 | source = "registry+https://github.com/rust-lang/crates.io-index" 352 | checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" 353 | dependencies = [ 354 | "instant", 355 | "lock_api", 356 | "parking_lot_core", 357 | ] 358 | 359 | [[package]] 360 | name = "parking_lot_core" 361 | version = "0.8.5" 362 | source = "registry+https://github.com/rust-lang/crates.io-index" 363 | checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216" 364 | dependencies = [ 365 | "cfg-if", 366 | "instant", 367 | "libc", 368 | "redox_syscall", 369 | "smallvec", 370 | "winapi", 371 | ] 372 | 373 | [[package]] 374 | name = "petgraph" 375 | version = "0.6.3" 376 | source = "registry+https://github.com/rust-lang/crates.io-index" 377 | checksum = "4dd7d28ee937e54fe3080c91faa1c3a46c06de6252988a7f4592ba2310ef22a4" 378 | dependencies = [ 379 | "fixedbitset", 380 | "indexmap", 381 | ] 382 | 383 | [[package]] 384 | name = "phf_shared" 385 | version = "0.8.0" 386 | source = "registry+https://github.com/rust-lang/crates.io-index" 387 | checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7" 388 | dependencies = [ 389 | "siphasher", 390 | ] 391 | 392 | [[package]] 393 | name = "pico-args" 394 | version = "0.5.0" 395 | source = "registry+https://github.com/rust-lang/crates.io-index" 396 | checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" 397 | 398 | [[package]] 399 | name = "precomputed-hash" 400 | version = "0.1.1" 401 | source = "registry+https://github.com/rust-lang/crates.io-index" 402 | checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" 403 | 404 | [[package]] 405 | name = "redox_syscall" 406 | version = "0.2.10" 407 | source = "registry+https://github.com/rust-lang/crates.io-index" 408 | checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" 409 | dependencies = [ 410 | "bitflags", 411 | ] 412 | 413 | [[package]] 414 | name = "redox_users" 415 | version = "0.4.0" 416 | source = "registry+https://github.com/rust-lang/crates.io-index" 417 | checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64" 418 | dependencies = [ 419 | "getrandom", 420 | "redox_syscall", 421 | ] 422 | 423 | [[package]] 424 | name = "regex" 425 | version = "1.5.6" 426 | source = "registry+https://github.com/rust-lang/crates.io-index" 427 | checksum = "d83f127d94bdbcda4c8cc2e50f6f84f4b611f69c902699ca385a39c3a75f9ff1" 428 | dependencies = [ 429 | "aho-corasick", 430 | "memchr", 431 | "regex-syntax 0.6.26", 432 | ] 433 | 434 | [[package]] 435 | name = "regex-syntax" 436 | version = "0.6.26" 437 | source = "registry+https://github.com/rust-lang/crates.io-index" 438 | checksum = "49b3de9ec5dc0a3417da371aab17d729997c15010e7fd24ff707773a33bddb64" 439 | 440 | [[package]] 441 | name = "regex-syntax" 442 | version = "0.7.2" 443 | source = "registry+https://github.com/rust-lang/crates.io-index" 444 | checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78" 445 | 446 | [[package]] 447 | name = "rustix" 448 | version = "0.37.19" 449 | source = "registry+https://github.com/rust-lang/crates.io-index" 450 | checksum = "acf8729d8542766f1b2cf77eb034d52f40d375bb8b615d0b147089946e16613d" 451 | dependencies = [ 452 | "bitflags", 453 | "errno", 454 | "io-lifetimes", 455 | "libc", 456 | "linux-raw-sys", 457 | "windows-sys", 458 | ] 459 | 460 | [[package]] 461 | name = "rustversion" 462 | version = "1.0.6" 463 | source = "registry+https://github.com/rust-lang/crates.io-index" 464 | checksum = "f2cc38e8fa666e2de3c4aba7edeb5ffc5246c1c2ed0e3d17e560aeeba736b23f" 465 | 466 | [[package]] 467 | name = "scopeguard" 468 | version = "1.1.0" 469 | source = "registry+https://github.com/rust-lang/crates.io-index" 470 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 471 | 472 | [[package]] 473 | name = "siphasher" 474 | version = "0.3.9" 475 | source = "registry+https://github.com/rust-lang/crates.io-index" 476 | checksum = "a86232ab60fa71287d7f2ddae4a7073f6b7aac33631c3015abb556f08c6d0a3e" 477 | 478 | [[package]] 479 | name = "smallvec" 480 | version = "1.8.0" 481 | source = "registry+https://github.com/rust-lang/crates.io-index" 482 | checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83" 483 | 484 | [[package]] 485 | name = "string_cache" 486 | version = "0.8.2" 487 | source = "registry+https://github.com/rust-lang/crates.io-index" 488 | checksum = "923f0f39b6267d37d23ce71ae7235602134b250ace715dd2c90421998ddac0c6" 489 | dependencies = [ 490 | "lazy_static", 491 | "new_debug_unreachable", 492 | "parking_lot", 493 | "phf_shared", 494 | "precomputed-hash", 495 | ] 496 | 497 | [[package]] 498 | name = "term" 499 | version = "0.7.0" 500 | source = "registry+https://github.com/rust-lang/crates.io-index" 501 | checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f" 502 | dependencies = [ 503 | "dirs-next", 504 | "rustversion", 505 | "winapi", 506 | ] 507 | 508 | [[package]] 509 | name = "tiny-keccak" 510 | version = "2.0.2" 511 | source = "registry+https://github.com/rust-lang/crates.io-index" 512 | checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" 513 | dependencies = [ 514 | "crunchy", 515 | ] 516 | 517 | [[package]] 518 | name = "unicode-xid" 519 | version = "0.2.2" 520 | source = "registry+https://github.com/rust-lang/crates.io-index" 521 | checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" 522 | 523 | [[package]] 524 | name = "wasi" 525 | version = "0.10.2+wasi-snapshot-preview1" 526 | source = "registry+https://github.com/rust-lang/crates.io-index" 527 | checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" 528 | 529 | [[package]] 530 | name = "winapi" 531 | version = "0.3.9" 532 | source = "registry+https://github.com/rust-lang/crates.io-index" 533 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 534 | dependencies = [ 535 | "winapi-i686-pc-windows-gnu", 536 | "winapi-x86_64-pc-windows-gnu", 537 | ] 538 | 539 | [[package]] 540 | name = "winapi-i686-pc-windows-gnu" 541 | version = "0.4.0" 542 | source = "registry+https://github.com/rust-lang/crates.io-index" 543 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 544 | 545 | [[package]] 546 | name = "winapi-x86_64-pc-windows-gnu" 547 | version = "0.4.0" 548 | source = "registry+https://github.com/rust-lang/crates.io-index" 549 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 550 | 551 | [[package]] 552 | name = "windows-sys" 553 | version = "0.48.0" 554 | source = "registry+https://github.com/rust-lang/crates.io-index" 555 | checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" 556 | dependencies = [ 557 | "windows-targets", 558 | ] 559 | 560 | [[package]] 561 | name = "windows-targets" 562 | version = "0.48.0" 563 | source = "registry+https://github.com/rust-lang/crates.io-index" 564 | checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" 565 | dependencies = [ 566 | "windows_aarch64_gnullvm", 567 | "windows_aarch64_msvc", 568 | "windows_i686_gnu", 569 | "windows_i686_msvc", 570 | "windows_x86_64_gnu", 571 | "windows_x86_64_gnullvm", 572 | "windows_x86_64_msvc", 573 | ] 574 | 575 | [[package]] 576 | name = "windows_aarch64_gnullvm" 577 | version = "0.48.0" 578 | source = "registry+https://github.com/rust-lang/crates.io-index" 579 | checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" 580 | 581 | [[package]] 582 | name = "windows_aarch64_msvc" 583 | version = "0.48.0" 584 | source = "registry+https://github.com/rust-lang/crates.io-index" 585 | checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" 586 | 587 | [[package]] 588 | name = "windows_i686_gnu" 589 | version = "0.48.0" 590 | source = "registry+https://github.com/rust-lang/crates.io-index" 591 | checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" 592 | 593 | [[package]] 594 | name = "windows_i686_msvc" 595 | version = "0.48.0" 596 | source = "registry+https://github.com/rust-lang/crates.io-index" 597 | checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" 598 | 599 | [[package]] 600 | name = "windows_x86_64_gnu" 601 | version = "0.48.0" 602 | source = "registry+https://github.com/rust-lang/crates.io-index" 603 | checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" 604 | 605 | [[package]] 606 | name = "windows_x86_64_gnullvm" 607 | version = "0.48.0" 608 | source = "registry+https://github.com/rust-lang/crates.io-index" 609 | checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" 610 | 611 | [[package]] 612 | name = "windows_x86_64_msvc" 613 | version = "0.48.0" 614 | source = "registry+https://github.com/rust-lang/crates.io-index" 615 | checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" 616 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "kira" 3 | version = "0.0.4" 4 | authors = ["MaxXing "] 5 | edition = "2021" 6 | 7 | [build-dependencies] 8 | lalrpop = "0.20.0" 9 | 10 | [dependencies] 11 | lalrpop-util = { version = "0.20.0", features = ["lexer"] } 12 | regex = "1.5.6" 13 | koopa = "0.0.7" 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | CoolPepper 635 | Copyright (C) 2018 ustb-nscscc-team 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | CoolPepper Copyright (C) 2018 ustb-nscscc-team 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Kira (Rust version) 2 | 3 | Kira is an example compiler for PKU compiler course, it can compile SysY language into [Koopa IR](https://github.com/pku-minic/koopa) and RISC-V assembly. 4 | 5 | `kira-rs` is written in Rust, for C++ example, please see [`kira-cpp`](https://github.com/pku-minic/kira-cpp). 6 | 7 | ## Usage 8 | 9 | ```sh 10 | # compiler `input.c` to Koopa IR 11 | cargo run -- -koopa input.c -o output.koopa 12 | # compiler `input.c` to RISC-V assembly 13 | cargo run -- -riscv input.c -o output.S 14 | ``` 15 | 16 | ## Changelog 17 | 18 | See [CHANGELOG.md](CHANGELOG.md). 19 | 20 | ## Copyright and License 21 | 22 | Copyright (C) 2010-2022 MaxXing. License GPLv3. 23 | -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | lalrpop::process_root().unwrap(); 3 | } 4 | -------------------------------------------------------------------------------- /src/ast.rs: -------------------------------------------------------------------------------- 1 | pub struct CompUnit { 2 | pub items: Vec, 3 | } 4 | 5 | pub enum GlobalItem { 6 | Decl(Decl), 7 | FuncDef(FuncDef), 8 | } 9 | 10 | pub enum Decl { 11 | Const(ConstDecl), 12 | Var(VarDecl), 13 | } 14 | 15 | pub struct ConstDecl { 16 | pub defs: Vec, 17 | } 18 | 19 | pub struct ConstDef { 20 | pub id: String, 21 | pub dims: Vec, 22 | pub init: ConstInitVal, 23 | } 24 | 25 | pub enum ConstInitVal { 26 | Exp(ConstExp), 27 | List(Vec), 28 | } 29 | 30 | pub struct VarDecl { 31 | pub defs: Vec, 32 | } 33 | 34 | pub struct VarDef { 35 | pub id: String, 36 | pub dims: Vec, 37 | pub init: Option, 38 | } 39 | 40 | pub enum InitVal { 41 | Exp(Exp), 42 | List(Vec), 43 | } 44 | 45 | pub struct FuncDef { 46 | pub ty: FuncType, 47 | pub id: String, 48 | pub params: Vec, 49 | pub block: Block, 50 | } 51 | 52 | pub enum FuncType { 53 | Void, 54 | Int, 55 | } 56 | 57 | pub struct FuncFParam { 58 | pub id: String, 59 | pub dims: Option>, 60 | } 61 | 62 | pub struct Block { 63 | pub items: Vec, 64 | } 65 | 66 | pub enum BlockItem { 67 | Decl(Decl), 68 | Stmt(Stmt), 69 | } 70 | 71 | #[allow(clippy::enum_variant_names)] 72 | pub enum Stmt { 73 | Assign(Assign), 74 | ExpStmt(ExpStmt), 75 | Block(Block), 76 | If(Box), 77 | While(Box), 78 | Break(Break), 79 | Continue(Continue), 80 | Return(Return), 81 | } 82 | 83 | pub struct Assign { 84 | pub lval: LVal, 85 | pub exp: Exp, 86 | } 87 | 88 | pub struct ExpStmt { 89 | pub exp: Option, 90 | } 91 | 92 | pub struct If { 93 | pub cond: Exp, 94 | pub then: Stmt, 95 | pub else_then: Option, 96 | } 97 | 98 | pub struct While { 99 | pub cond: Exp, 100 | pub body: Stmt, 101 | } 102 | 103 | pub struct Break; 104 | 105 | pub struct Continue; 106 | 107 | pub struct Return { 108 | pub exp: Option, 109 | } 110 | 111 | pub struct Exp { 112 | pub lor: LOrExp, 113 | } 114 | 115 | pub struct LVal { 116 | pub id: String, 117 | pub indices: Vec, 118 | } 119 | 120 | pub enum PrimaryExp { 121 | Exp(Box), 122 | LVal(LVal), 123 | Number(i32), 124 | } 125 | 126 | pub enum UnaryExp { 127 | Primary(PrimaryExp), 128 | Call(FuncCall), 129 | Unary(UnaryOp, Box), 130 | } 131 | 132 | pub struct FuncCall { 133 | pub id: String, 134 | pub args: Vec, 135 | } 136 | 137 | pub enum MulExp { 138 | Unary(UnaryExp), 139 | MulUnary(Box, MulOp, UnaryExp), 140 | } 141 | 142 | pub enum AddExp { 143 | Mul(MulExp), 144 | AddMul(Box, AddOp, MulExp), 145 | } 146 | 147 | pub enum RelExp { 148 | Add(AddExp), 149 | RelAdd(Box, RelOp, AddExp), 150 | } 151 | 152 | pub enum EqExp { 153 | Rel(RelExp), 154 | EqRel(Box, EqOp, RelExp), 155 | } 156 | 157 | pub enum LAndExp { 158 | Eq(EqExp), 159 | LAndEq(Box, EqExp), 160 | } 161 | 162 | pub enum LOrExp { 163 | LAnd(LAndExp), 164 | LOrLAnd(Box, LAndExp), 165 | } 166 | 167 | pub struct ConstExp { 168 | pub exp: Exp, 169 | } 170 | 171 | pub enum UnaryOp { 172 | Neg, 173 | LNot, 174 | } 175 | 176 | pub enum MulOp { 177 | Mul, 178 | Div, 179 | Mod, 180 | } 181 | 182 | pub enum AddOp { 183 | Add, 184 | Sub, 185 | } 186 | 187 | pub enum RelOp { 188 | Lt, 189 | Gt, 190 | Le, 191 | Ge, 192 | } 193 | 194 | pub enum EqOp { 195 | Eq, 196 | Neq, 197 | } 198 | -------------------------------------------------------------------------------- /src/codegen/builder.rs: -------------------------------------------------------------------------------- 1 | use super::func::FunctionInfo; 2 | use std::fs::File; 3 | use std::io::{Result, Write}; 4 | 5 | /// Assembly builder. 6 | pub struct AsmBuilder<'f> { 7 | f: &'f mut File, 8 | temp: &'static str, 9 | } 10 | 11 | impl<'f> AsmBuilder<'f> { 12 | /// Creates a new assembly builder. 13 | pub fn new(f: &'f mut File, temp: &'static str) -> Self { 14 | Self { f, temp } 15 | } 16 | 17 | pub fn li(&mut self, dest: &str, imm: i32) -> Result<()> { 18 | writeln!(self.f, " li {dest}, {imm}") 19 | } 20 | 21 | pub fn la(&mut self, dest: &str, symbol: &str) -> Result<()> { 22 | writeln!(self.f, " la {dest}, {symbol}") 23 | } 24 | 25 | pub fn mv(&mut self, dest: &str, src: &str) -> Result<()> { 26 | if dest != src { 27 | writeln!(self.f, " mv {dest}, {src}") 28 | } else { 29 | Ok(()) 30 | } 31 | } 32 | 33 | pub fn op2(&mut self, op: &str, dest: &str, lhs: &str, rhs: &str) -> Result<()> { 34 | writeln!(self.f, " {op} {dest}, {lhs}, {rhs}") 35 | } 36 | 37 | pub fn op1(&mut self, op: &str, dest: &str, src: &str) -> Result<()> { 38 | writeln!(self.f, " {op} {dest}, {src}") 39 | } 40 | 41 | pub fn addi(&mut self, dest: &str, opr: &str, imm: i32) -> Result<()> { 42 | if (-2048..=2047).contains(&imm) { 43 | writeln!(self.f, " addi {dest}, {opr}, {imm}") 44 | } else { 45 | self.li(self.temp, imm)?; 46 | writeln!(self.f, " add {dest}, {opr}, {}", self.temp) 47 | } 48 | } 49 | 50 | pub fn slli(&mut self, dest: &str, opr: &str, imm: usize) -> Result<()> { 51 | writeln!(self.f, " slli {dest}, {opr}, {imm}") 52 | } 53 | 54 | pub fn muli(&mut self, dest: &str, opr: &str, imm: i32) -> Result<()> { 55 | if imm == 0 { 56 | self.mv(dest, "x0") 57 | } else if imm > 0 && (imm & (imm - 1)) == 0 { 58 | let mut shift = 0; 59 | let mut imm = imm >> 1; 60 | while imm != 0 { 61 | shift += 1; 62 | imm >>= 1; 63 | } 64 | self.slli(dest, opr, shift) 65 | } else { 66 | self.li(self.temp, imm)?; 67 | self.op2("mul", dest, opr, self.temp) 68 | } 69 | } 70 | 71 | pub fn sw(&mut self, src: &str, addr: &str, offset: i32) -> Result<()> { 72 | if (-2048..=2047).contains(&offset) { 73 | writeln!(self.f, " sw {src}, {offset}({addr})") 74 | } else { 75 | self.addi(self.temp, addr, offset)?; 76 | writeln!(self.f, " sw {src}, 0({})", self.temp) 77 | } 78 | } 79 | 80 | pub fn lw(&mut self, dest: &str, addr: &str, offset: i32) -> Result<()> { 81 | if (-2048..=2047).contains(&offset) { 82 | writeln!(self.f, " lw {dest}, {offset}({addr})") 83 | } else { 84 | self.addi(self.temp, addr, offset)?; 85 | writeln!(self.f, " lw {dest}, 0({})", self.temp) 86 | } 87 | } 88 | 89 | pub fn bnez(&mut self, cond: &str, label: &str) -> Result<()> { 90 | writeln!(self.f, " bnez {cond}, {label}") 91 | } 92 | 93 | pub fn j(&mut self, label: &str) -> Result<()> { 94 | writeln!(self.f, " j {label}") 95 | } 96 | 97 | pub fn call(&mut self, func: &str) -> Result<()> { 98 | writeln!(self.f, " call {func}") 99 | } 100 | 101 | pub fn prologue(&mut self, func_name: &str, info: &FunctionInfo) -> Result<()> { 102 | // declaration 103 | writeln!(self.f, " .text")?; 104 | writeln!(self.f, " .globl {}", &func_name[1..])?; 105 | writeln!(self.f, "{}:", &func_name[1..])?; 106 | // prologue 107 | let offset = info.sp_offset() as i32; 108 | if offset != 0 { 109 | self.addi("sp", "sp", -offset)?; 110 | if !info.is_leaf() { 111 | self.sw("ra", "sp", offset - 4)?; 112 | } 113 | } 114 | Ok(()) 115 | } 116 | 117 | pub fn epilogue(&mut self, info: &FunctionInfo) -> Result<()> { 118 | let offset = info.sp_offset() as i32; 119 | if offset != 0 { 120 | if !info.is_leaf() { 121 | self.lw("ra", "sp", offset - 4)?; 122 | } 123 | self.addi("sp", "sp", offset)?; 124 | } 125 | writeln!(self.f, " ret") 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/codegen/func.rs: -------------------------------------------------------------------------------- 1 | use koopa::ir::entities::ValueData; 2 | use koopa::ir::{BasicBlock, Function, TypeKind, ValueKind}; 3 | use std::cell::Cell; 4 | use std::collections::HashMap; 5 | 6 | /// Function information. 7 | pub struct FunctionInfo { 8 | func: Function, 9 | /// Maximum argument number of call instructions in the function. 10 | /// `None` if the current function is a leaf function. 11 | max_arg_num: Option, 12 | alloc_size: usize, 13 | allocs: HashMap<*const ValueData, Slot>, 14 | bbs: HashMap, 15 | sp_offset: Cell>, 16 | } 17 | 18 | impl FunctionInfo { 19 | thread_local! { 20 | static NEXT_TEMP_LABEL_ID: Cell = Cell::new(0); 21 | } 22 | 23 | /// Creates a new function information. 24 | pub fn new(func: Function) -> Self { 25 | Self { 26 | func, 27 | max_arg_num: None, 28 | alloc_size: 0, 29 | allocs: HashMap::new(), 30 | bbs: HashMap::new(), 31 | sp_offset: Cell::new(None), 32 | } 33 | } 34 | 35 | /// Returns the current function. 36 | pub fn func(&self) -> Function { 37 | self.func 38 | } 39 | 40 | /// Logs argument number. 41 | pub fn log_arg_num(&mut self, arg_num: usize) { 42 | if self.max_arg_num.is_none() || arg_num > self.max_arg_num.unwrap() { 43 | self.max_arg_num = Some(arg_num); 44 | } 45 | } 46 | 47 | /// Returns `true` if the current function is a leaf function. 48 | pub fn is_leaf(&self) -> bool { 49 | self.max_arg_num.is_none() 50 | } 51 | 52 | /// Allocates a new stack slot for the given value data. 53 | pub fn alloc_slot(&mut self, value: &ValueData) { 54 | match value.kind() { 55 | ValueKind::Alloc(_) => { 56 | self.allocs.insert(value, Slot::new(self.alloc_size, false)); 57 | self.alloc_size += match value.ty().kind() { 58 | TypeKind::Pointer(base) => base.size(), 59 | _ => unreachable!(), 60 | }; 61 | } 62 | _ => { 63 | let is_ptr = matches!(value.ty().kind(), TypeKind::Pointer(_)); 64 | let slot = Slot::new(self.alloc_size, is_ptr); 65 | self.allocs.insert(value, slot); 66 | self.alloc_size += value.ty().size(); 67 | } 68 | }; 69 | } 70 | 71 | /// Returns the slot offset (relative to `sp`) of the given value data. 72 | pub fn slot_offset(&self, value: &ValueData) -> Option { 73 | self.allocs.get(&(value as *const ValueData)).map(|&offset| { 74 | if self.is_leaf() { 75 | offset.map(|o| self.sp_offset() - self.alloc_size + o) 76 | } else { 77 | offset.map(|o| self.sp_offset() - 4 - self.alloc_size + o) 78 | } 79 | }) 80 | } 81 | 82 | /// Logs basic block name. 83 | pub fn log_bb_name(&mut self, bb: BasicBlock, name: &Option) { 84 | let id = Self::NEXT_TEMP_LABEL_ID.with(|id| id.replace(id.get() + 1)); 85 | let name = match name.as_ref() { 86 | Some(name) => format!(".L{}_{}", &name[1..], id), 87 | None => format!(".L{}", id), 88 | }; 89 | self.bbs.insert(bb, name); 90 | } 91 | 92 | /// Returns a reference to the name of the given basic block. 93 | pub fn bb_name(&self, bb: BasicBlock) -> &str { 94 | self.bbs.get(&bb).as_ref().unwrap() 95 | } 96 | 97 | /// Returns the stack pointer offset. 98 | pub fn sp_offset(&self) -> usize { 99 | if let Some(sp_offset) = self.sp_offset.get() { 100 | sp_offset 101 | } else { 102 | // slot for storing return address 103 | let ra = if self.is_leaf() { 0 } else { 4 }; 104 | // slot for storing arguments 105 | let args = match self.max_arg_num { 106 | Some(num) if num > 8 => (num - 8) * 4, 107 | _ => 0, 108 | }; 109 | // the final offset 110 | let offset = ra + self.alloc_size + args; 111 | // align to 16 bytes 112 | let sp_offset = (offset + 15) / 16 * 16; 113 | self.sp_offset.set(Some(sp_offset)); 114 | sp_offset 115 | } 116 | } 117 | } 118 | 119 | /// A stack slot. 120 | #[derive(Clone, Copy)] 121 | pub struct Slot { 122 | pub offset: usize, 123 | /// `true` if the slot stores an pointer but not an allocation. 124 | pub is_ptr: bool, 125 | } 126 | 127 | impl Slot { 128 | /// Creates a new stack slot. 129 | fn new(offset: usize, is_ptr: bool) -> Self { 130 | Self { offset, is_ptr } 131 | } 132 | 133 | /// Maps the offset by applying the given function. 134 | fn map(self, f: F) -> Self 135 | where 136 | F: FnOnce(usize) -> usize, 137 | { 138 | Self { 139 | offset: f(self.offset), 140 | is_ptr: self.is_ptr, 141 | } 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /src/codegen/gen.rs: -------------------------------------------------------------------------------- 1 | use super::builder::AsmBuilder; 2 | use super::func::FunctionInfo; 3 | use super::info::{cur_func, cur_func_mut, ProgramInfo}; 4 | use super::values::{asm_value, AsmValue, LocalValue}; 5 | use koopa::ir::entities::ValueData; 6 | use koopa::ir::values::*; 7 | use koopa::ir::{BasicBlock, Function, FunctionData, Program, TypeKind, Value, ValueKind}; 8 | use std::fs::File; 9 | use std::io::{Result, Write}; 10 | 11 | /// Trait for generating RISC-V assembly. 12 | pub trait GenerateToAsm<'p, 'i> { 13 | type Out; 14 | 15 | fn generate(&self, f: &mut File, info: &'i mut ProgramInfo<'p>) -> Result; 16 | } 17 | 18 | /// Trait for generating RISC-V assembly (for values). 19 | trait GenerateValueToAsm<'p, 'i> { 20 | type Out; 21 | 22 | fn generate( 23 | &self, 24 | f: &mut File, 25 | info: &'i mut ProgramInfo<'p>, 26 | v: &ValueData, 27 | ) -> Result; 28 | } 29 | 30 | impl<'p, 'i> GenerateToAsm<'p, 'i> for Program { 31 | type Out = (); 32 | 33 | fn generate(&self, f: &mut File, info: &mut ProgramInfo) -> Result { 34 | // generate global allocations 35 | for &value in self.inst_layout() { 36 | let data = self.borrow_value(value); 37 | let name = &data.name().as_ref().unwrap()[1..]; 38 | info.insert_value(value, name.into()); 39 | writeln!(f, " .data")?; 40 | writeln!(f, " .globl {name}")?; 41 | writeln!(f, "{name}:")?; 42 | data.generate(f, info)?; 43 | writeln!(f)?; 44 | } 45 | // generate functions 46 | for &func in self.func_layout() { 47 | info.set_cur_func(FunctionInfo::new(func)); 48 | self.func(func).generate(f, info)?; 49 | } 50 | Ok(()) 51 | } 52 | } 53 | 54 | impl<'p, 'i> GenerateToAsm<'p, 'i> for Function { 55 | type Out = &'p str; 56 | 57 | fn generate(&self, _: &mut File, info: &mut ProgramInfo<'p>) -> Result { 58 | Ok(&info.program().func(*self).name()[1..]) 59 | } 60 | } 61 | 62 | impl<'p, 'i> GenerateToAsm<'p, 'i> for FunctionData { 63 | type Out = (); 64 | 65 | fn generate(&self, f: &mut File, info: &mut ProgramInfo) -> Result { 66 | // skip declarations 67 | if self.layout().entry_bb().is_none() { 68 | return Ok(()); 69 | } 70 | // allocation stack slots and log argument number 71 | let func = cur_func_mut!(info); 72 | for value in self.dfg().values().values() { 73 | // allocate stack slot 74 | if value.kind().is_local_inst() && !value.used_by().is_empty() { 75 | func.alloc_slot(value); 76 | } 77 | // log argument number 78 | if let ValueKind::Call(call) = value.kind() { 79 | func.log_arg_num(call.args().len()); 80 | } 81 | } 82 | // generate basic block names 83 | for (&bb, data) in self.dfg().bbs() { 84 | // basic block parameters are not supported 85 | assert!(data.params().is_empty()); 86 | func.log_bb_name(bb, data.name()); 87 | } 88 | // generate prologue 89 | AsmBuilder::new(f, "t0").prologue(self.name(), func)?; 90 | // generate instructions in basic blocks 91 | for (bb, node) in self.layout().bbs() { 92 | let name = bb.generate(f, info)?; 93 | writeln!(f, "{name}:")?; 94 | for &inst in node.insts().keys() { 95 | self.dfg().value(inst).generate(f, info)?; 96 | } 97 | } 98 | writeln!(f) 99 | } 100 | } 101 | 102 | impl<'p, 'i> GenerateToAsm<'p, 'i> for BasicBlock { 103 | type Out = &'i str; 104 | 105 | fn generate(&self, _: &mut File, info: &'i mut ProgramInfo) -> Result { 106 | Ok(cur_func!(info).bb_name(*self)) 107 | } 108 | } 109 | 110 | impl<'p, 'i> GenerateToAsm<'p, 'i> for Value { 111 | type Out = AsmValue<'i>; 112 | 113 | fn generate(&self, _: &mut File, info: &'i mut ProgramInfo) -> Result { 114 | if self.is_global() { 115 | Ok(AsmValue::Global(info.value(*self))) 116 | } else { 117 | let func = cur_func!(info); 118 | let value = info.program().func(func.func()).dfg().value(*self); 119 | Ok(match value.kind() { 120 | ValueKind::Integer(i) => AsmValue::Const(i.value()), 121 | ValueKind::FuncArgRef(i) => AsmValue::Arg(i.index()), 122 | _ => AsmValue::from(func.slot_offset(value)), 123 | }) 124 | } 125 | } 126 | } 127 | 128 | impl<'p, 'i> GenerateToAsm<'p, 'i> for ValueData { 129 | type Out = (); 130 | 131 | fn generate(&self, f: &mut File, info: &mut ProgramInfo) -> Result { 132 | match self.kind() { 133 | ValueKind::Integer(v) => v.generate(f, info), 134 | ValueKind::ZeroInit(v) => v.generate(f, info, self), 135 | ValueKind::Aggregate(v) => v.generate(f, info), 136 | ValueKind::GlobalAlloc(v) => v.generate(f, info), 137 | ValueKind::Load(v) => v.generate(f, info, self), 138 | ValueKind::Store(v) => v.generate(f, info), 139 | ValueKind::GetPtr(v) => v.generate(f, info, self), 140 | ValueKind::GetElemPtr(v) => v.generate(f, info, self), 141 | ValueKind::Binary(v) => v.generate(f, info, self), 142 | ValueKind::Branch(v) => v.generate(f, info), 143 | ValueKind::Jump(v) => v.generate(f, info), 144 | ValueKind::Call(v) => v.generate(f, info, self), 145 | ValueKind::Return(v) => v.generate(f, info), 146 | _ => Ok(()), 147 | } 148 | } 149 | } 150 | 151 | impl<'p, 'i> GenerateToAsm<'p, 'i> for Integer { 152 | type Out = (); 153 | 154 | fn generate(&self, f: &mut File, _: &mut ProgramInfo) -> Result { 155 | writeln!(f, " .word {}", self.value()) 156 | } 157 | } 158 | 159 | impl<'p, 'i> GenerateValueToAsm<'p, 'i> for ZeroInit { 160 | type Out = (); 161 | 162 | fn generate(&self, f: &mut File, _: &mut ProgramInfo, v: &ValueData) -> Result { 163 | writeln!(f, " .zero {}", v.ty().size()) 164 | } 165 | } 166 | 167 | impl<'p, 'i> GenerateToAsm<'p, 'i> for Aggregate { 168 | type Out = (); 169 | 170 | fn generate(&self, f: &mut File, info: &mut ProgramInfo) -> Result { 171 | for &elem in self.elems() { 172 | info.program().borrow_value(elem).generate(f, info)?; 173 | } 174 | Ok(()) 175 | } 176 | } 177 | 178 | impl<'p, 'i> GenerateToAsm<'p, 'i> for GlobalAlloc { 179 | type Out = (); 180 | 181 | fn generate(&self, f: &mut File, info: &mut ProgramInfo) -> Result { 182 | info.program().borrow_value(self.init()).generate(f, info) 183 | } 184 | } 185 | 186 | impl<'p, 'i> GenerateValueToAsm<'p, 'i> for Load { 187 | type Out = (); 188 | 189 | fn generate(&self, f: &mut File, info: &mut ProgramInfo, v: &ValueData) -> Result { 190 | let src = self.src().generate(f, info)?; 191 | src.write_to(f, "t0")?; 192 | if src.is_ptr() { 193 | AsmBuilder::new(f, "t1").lw("t0", "t0", 0)?; 194 | } 195 | asm_value!(info, v).read_from(f, "t0", "t1") 196 | } 197 | } 198 | 199 | impl<'p, 'i> GenerateToAsm<'p, 'i> for Store { 200 | type Out = (); 201 | 202 | fn generate(&self, f: &mut File, info: &mut ProgramInfo) -> Result { 203 | let sp_offset = cur_func!(info).sp_offset(); 204 | let value = self.value().generate(f, info)?; 205 | if matches!(value, AsmValue::Arg(_)) { 206 | value.write_arg_to(f, "t0", sp_offset)?; 207 | } else { 208 | value.write_to(f, "t0")?; 209 | } 210 | let dest = self.dest().generate(f, info)?; 211 | if dest.is_ptr() { 212 | dest.write_to(f, "t1")?; 213 | AsmBuilder::new(f, "t2").sw("t0", "t1", 0) 214 | } else { 215 | dest.read_from(f, "t0", "t1") 216 | } 217 | } 218 | } 219 | 220 | impl<'p, 'i> GenerateValueToAsm<'p, 'i> for GetPtr { 221 | type Out = (); 222 | 223 | fn generate(&self, f: &mut File, info: &mut ProgramInfo, v: &ValueData) -> Result { 224 | let src = self.src().generate(f, info)?; 225 | if src.is_ptr() { 226 | src.write_to(f, "t0")?; 227 | } else { 228 | src.write_addr_to(f, "t0")?; 229 | } 230 | self.index().generate(f, info)?.write_to(f, "t1")?; 231 | let size = match v.ty().kind() { 232 | TypeKind::Pointer(base) => base.size(), 233 | _ => unreachable!(), 234 | }; 235 | let mut builder = AsmBuilder::new(f, "t2"); 236 | builder.muli("t1", "t1", size as i32)?; 237 | builder.op2("add", "t0", "t0", "t1")?; 238 | asm_value!(info, v).read_from(f, "t0", "t1") 239 | } 240 | } 241 | 242 | impl<'p, 'i> GenerateValueToAsm<'p, 'i> for GetElemPtr { 243 | type Out = (); 244 | 245 | fn generate(&self, f: &mut File, info: &mut ProgramInfo, v: &ValueData) -> Result { 246 | let src = self.src().generate(f, info)?; 247 | if src.is_ptr() { 248 | src.write_to(f, "t0")?; 249 | } else { 250 | src.write_addr_to(f, "t0")?; 251 | } 252 | self.index().generate(f, info)?.write_to(f, "t1")?; 253 | let size = match v.ty().kind() { 254 | TypeKind::Pointer(base) => base.size(), 255 | _ => unreachable!(), 256 | }; 257 | let mut builder = AsmBuilder::new(f, "t2"); 258 | builder.muli("t1", "t1", size as i32)?; 259 | builder.op2("add", "t0", "t0", "t1")?; 260 | asm_value!(info, v).read_from(f, "t0", "t1") 261 | } 262 | } 263 | 264 | impl<'p, 'i> GenerateValueToAsm<'p, 'i> for Binary { 265 | type Out = (); 266 | 267 | fn generate(&self, f: &mut File, info: &mut ProgramInfo, v: &ValueData) -> Result { 268 | self.lhs().generate(f, info)?.write_to(f, "t0")?; 269 | self.rhs().generate(f, info)?.write_to(f, "t1")?; 270 | let mut builder = AsmBuilder::new(f, "t2"); 271 | match self.op() { 272 | BinaryOp::NotEq => { 273 | builder.op2("xor", "t0", "t0", "t1")?; 274 | builder.op1("snez", "t0", "t0")?; 275 | } 276 | BinaryOp::Eq => { 277 | builder.op2("xor", "t0", "t0", "t1")?; 278 | builder.op1("seqz", "t0", "t0")?; 279 | } 280 | BinaryOp::Gt => builder.op2("sgt", "t0", "t0", "t1")?, 281 | BinaryOp::Lt => builder.op2("slt", "t0", "t0", "t1")?, 282 | BinaryOp::Ge => { 283 | builder.op2("slt", "t0", "t0", "t1")?; 284 | builder.op1("seqz", "t0", "t0")?; 285 | } 286 | BinaryOp::Le => { 287 | builder.op2("sgt", "t0", "t0", "t1")?; 288 | builder.op1("seqz", "t0", "t0")?; 289 | } 290 | BinaryOp::Add => builder.op2("add", "t0", "t0", "t1")?, 291 | BinaryOp::Sub => builder.op2("sub", "t0", "t0", "t1")?, 292 | BinaryOp::Mul => builder.op2("mul", "t0", "t0", "t1")?, 293 | BinaryOp::Div => builder.op2("div", "t0", "t0", "t1")?, 294 | BinaryOp::Mod => builder.op2("rem", "t0", "t0", "t1")?, 295 | BinaryOp::And => builder.op2("and", "t0", "t0", "t1")?, 296 | BinaryOp::Or => builder.op2("or", "t0", "t0", "t1")?, 297 | BinaryOp::Xor => builder.op2("xor", "t0", "t0", "t1")?, 298 | BinaryOp::Shl => builder.op2("sll", "t0", "t0", "t1")?, 299 | BinaryOp::Shr => builder.op2("srl", "t0", "t0", "t1")?, 300 | BinaryOp::Sar => builder.op2("sra", "t0", "t0", "t1")?, 301 | } 302 | asm_value!(info, v).read_from(f, "t0", "t1") 303 | } 304 | } 305 | 306 | impl<'p, 'i> GenerateToAsm<'p, 'i> for Branch { 307 | type Out = (); 308 | 309 | fn generate(&self, f: &mut File, info: &mut ProgramInfo) -> Result { 310 | self.cond().generate(f, info)?.write_to(f, "t0")?; 311 | let tlabel = self.true_bb().generate(f, info)?; 312 | AsmBuilder::new(f, "t1").bnez("t0", tlabel)?; 313 | let flabel = self.false_bb().generate(f, info)?; 314 | AsmBuilder::new(f, "t1").j(flabel) 315 | } 316 | } 317 | 318 | impl<'p, 'i> GenerateToAsm<'p, 'i> for Jump { 319 | type Out = (); 320 | 321 | fn generate(&self, f: &mut File, info: &mut ProgramInfo) -> Result { 322 | let label = self.target().generate(f, info)?; 323 | AsmBuilder::new(f, "t0").j(label) 324 | } 325 | } 326 | 327 | impl<'p, 'i> GenerateValueToAsm<'p, 'i> for Call { 328 | type Out = (); 329 | 330 | fn generate(&self, f: &mut File, info: &mut ProgramInfo, v: &ValueData) -> Result { 331 | let args = self 332 | .args() 333 | .iter() 334 | .map(|v| Ok(v.generate(f, info)?.into())) 335 | .collect::>>()?; 336 | for (i, arg) in args.into_iter().enumerate() { 337 | AsmValue::from(arg).write_to(f, "t0")?; 338 | AsmValue::Arg(i).read_from(f, "t0", "t1")?; 339 | } 340 | let callee = self.callee().generate(f, info)?; 341 | AsmBuilder::new(f, "t0").call(callee)?; 342 | if !v.used_by().is_empty() { 343 | asm_value!(info, v).read_from(f, "a0", "t0") 344 | } else { 345 | Ok(()) 346 | } 347 | } 348 | } 349 | 350 | impl<'p, 'i> GenerateToAsm<'p, 'i> for Return { 351 | type Out = (); 352 | 353 | fn generate(&self, f: &mut File, info: &mut ProgramInfo) -> Result { 354 | if let Some(value) = self.value() { 355 | value.generate(f, info)?.write_to(f, "a0")?; 356 | } 357 | AsmBuilder::new(f, "t0").epilogue(cur_func!(info)) 358 | } 359 | } 360 | -------------------------------------------------------------------------------- /src/codegen/info.rs: -------------------------------------------------------------------------------- 1 | use super::func::FunctionInfo; 2 | use koopa::ir::{Program, Value}; 3 | use std::collections::HashMap; 4 | 5 | /// Some necessary information during assembly generation. 6 | pub struct ProgramInfo<'p> { 7 | program: &'p Program, 8 | values: HashMap, 9 | cur_func: Option, 10 | } 11 | 12 | /// Returns a reference to the current function information. 13 | macro_rules! cur_func { 14 | ($info:expr) => { 15 | $info.cur_func().unwrap() 16 | }; 17 | } 18 | pub(crate) use cur_func; 19 | 20 | /// Returns a mutable reference to the current function information. 21 | macro_rules! cur_func_mut { 22 | ($info:expr) => { 23 | $info.cur_func_mut().unwrap() 24 | }; 25 | } 26 | pub(crate) use cur_func_mut; 27 | 28 | impl<'p> ProgramInfo<'p> { 29 | /// Creates a new program information. 30 | pub fn new(program: &'p Program) -> Self { 31 | Self { 32 | program, 33 | values: HashMap::new(), 34 | cur_func: None, 35 | } 36 | } 37 | 38 | /// Returns a reference to the program. 39 | pub fn program(&self) -> &'p Program { 40 | self.program 41 | } 42 | 43 | /// Returns the name of the given global value. 44 | pub fn value(&self, value: Value) -> &str { 45 | self.values.get(&value).unwrap() 46 | } 47 | 48 | /// Inserts a new global value name. 49 | pub fn insert_value(&mut self, value: Value, name: String) { 50 | self.values.insert(value, name); 51 | } 52 | 53 | /// Returns a reference to the current function information. 54 | pub fn cur_func(&self) -> Option<&FunctionInfo> { 55 | self.cur_func.as_ref() 56 | } 57 | 58 | /// Returns a mutable reference to the current function information. 59 | pub fn cur_func_mut(&mut self) -> Option<&mut FunctionInfo> { 60 | self.cur_func.as_mut() 61 | } 62 | 63 | /// Sets the current function information. 64 | pub fn set_cur_func(&mut self, func: FunctionInfo) { 65 | self.cur_func = Some(func); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/codegen/mod.rs: -------------------------------------------------------------------------------- 1 | mod builder; 2 | mod func; 3 | mod gen; 4 | mod info; 5 | mod values; 6 | 7 | use gen::GenerateToAsm; 8 | use info::ProgramInfo; 9 | use koopa::ir::{Program, Type}; 10 | use std::fs::File; 11 | use std::io::Result; 12 | 13 | /// Generates the given Koopa IR program to RISC-V assembly. 14 | pub fn generate_asm(program: &Program, path: &str) -> Result<()> { 15 | Type::set_ptr_size(4); 16 | program.generate(&mut File::create(path)?, &mut ProgramInfo::new(program)) 17 | } 18 | -------------------------------------------------------------------------------- /src/codegen/values.rs: -------------------------------------------------------------------------------- 1 | use super::builder::AsmBuilder; 2 | use super::func::Slot; 3 | use std::fs::File; 4 | use std::io::Result; 5 | 6 | /// A global/local value. 7 | pub enum AsmValue<'i> { 8 | Global(&'i str), 9 | Local(Slot), 10 | Const(i32), 11 | Arg(usize), 12 | Void, 13 | } 14 | 15 | /// Returns the assembly value of the given value data. 16 | macro_rules! asm_value { 17 | ($info:expr, $v:expr) => { 18 | AsmValue::from(cur_func!($info).slot_offset($v)) 19 | }; 20 | } 21 | pub(crate) use asm_value; 22 | 23 | impl<'i> AsmValue<'i> { 24 | /// Returns `true` if the value is a pointer. 25 | pub fn is_ptr(&self) -> bool { 26 | matches!(self, Self::Local(slot) if slot.is_ptr) 27 | } 28 | 29 | /// Writes the assembly value to the given register. 30 | pub fn write_to(&self, f: &mut File, reg: &'static str) -> Result<()> { 31 | let mut builder = AsmBuilder::new(f, reg); 32 | match self { 33 | Self::Global(symbol) => { 34 | builder.la(reg, symbol)?; 35 | builder.lw(reg, reg, 0) 36 | } 37 | Self::Local(slot) => builder.lw(reg, "sp", slot.offset as i32), 38 | Self::Const(num) => builder.li(reg, *num), 39 | _ => unreachable!(), 40 | } 41 | } 42 | 43 | /// Writes the address of assembly value to the give register. 44 | pub fn write_addr_to(&self, f: &mut File, reg: &'static str) -> Result<()> { 45 | let mut builder = AsmBuilder::new(f, reg); 46 | match self { 47 | Self::Global(symbol) => builder.la(reg, symbol), 48 | Self::Local(slot) => builder.addi(reg, "sp", slot.offset as i32), 49 | _ => unreachable!(), 50 | } 51 | } 52 | 53 | /// Writes the assembly value (argument) to the given register. 54 | pub fn write_arg_to(&self, f: &mut File, reg: &'static str, sp_offset: usize) -> Result<()> { 55 | let mut builder = AsmBuilder::new(f, reg); 56 | match self { 57 | Self::Arg(index) => { 58 | if *index < 8 { 59 | builder.mv(reg, &format!("a{}", *index)) 60 | } else { 61 | builder.lw(reg, "sp", (sp_offset + (*index - 8) * 4) as i32) 62 | } 63 | } 64 | _ => unreachable!(), 65 | } 66 | } 67 | 68 | /// Reads the value of the given register to the assembly value. 69 | pub fn read_from(&self, f: &mut File, reg: &'static str, temp: &'static str) -> Result<()> { 70 | let mut builder = AsmBuilder::new(f, temp); 71 | match self { 72 | Self::Global(symbol) => { 73 | builder.la(temp, symbol)?; 74 | builder.sw(reg, temp, 0) 75 | } 76 | Self::Local(slot) => builder.sw(reg, "sp", slot.offset as i32), 77 | Self::Const(_) => unreachable!(), 78 | Self::Arg(index) => { 79 | if *index < 8 { 80 | builder.mv(&format!("a{}", *index), reg) 81 | } else { 82 | builder.sw(reg, "sp", ((*index - 8) * 4) as i32) 83 | } 84 | } 85 | Self::Void => Ok(()), 86 | } 87 | } 88 | } 89 | 90 | impl<'i> From for AsmValue<'i> { 91 | fn from(v: LocalValue) -> Self { 92 | match v { 93 | LocalValue::Local(slot) => Self::Local(slot), 94 | LocalValue::Const(num) => Self::Const(num), 95 | } 96 | } 97 | } 98 | 99 | impl<'i> From> for AsmValue<'i> { 100 | fn from(v: Option) -> Self { 101 | match v { 102 | Some(slot) => Self::Local(slot), 103 | None => Self::Void, 104 | } 105 | } 106 | } 107 | 108 | /// A local value (simplified version of assembly value). 109 | pub enum LocalValue { 110 | Local(Slot), 111 | Const(i32), 112 | } 113 | 114 | impl<'i> From> for LocalValue { 115 | fn from(value: AsmValue) -> Self { 116 | match value { 117 | AsmValue::Local(slot) => Self::Local(slot), 118 | AsmValue::Const(num) => Self::Const(num), 119 | _ => unreachable!(), 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/irgen/eval.rs: -------------------------------------------------------------------------------- 1 | use super::scopes::Scopes; 2 | use super::values::Value; 3 | use crate::ast::*; 4 | 5 | /// Trait for evaluating constant. 6 | pub trait Evaluate { 7 | fn eval(&self, scopes: &Scopes) -> Option; 8 | } 9 | 10 | impl Evaluate for Exp { 11 | fn eval(&self, scopes: &Scopes) -> Option { 12 | self.lor.eval(scopes) 13 | } 14 | } 15 | 16 | impl Evaluate for LVal { 17 | fn eval(&self, scopes: &Scopes) -> Option { 18 | let val = scopes.value(&self.id).ok()?; 19 | if self.indices.is_empty() { 20 | match val { 21 | Value::Const(i) => Some(*i), 22 | _ => None, 23 | } 24 | } else { 25 | None 26 | } 27 | } 28 | } 29 | 30 | impl Evaluate for PrimaryExp { 31 | fn eval(&self, scopes: &Scopes) -> Option { 32 | match self { 33 | Self::Exp(exp) => exp.eval(scopes), 34 | Self::LVal(lval) => lval.eval(scopes), 35 | Self::Number(num) => Some(*num), 36 | } 37 | } 38 | } 39 | 40 | impl Evaluate for UnaryExp { 41 | fn eval(&self, scopes: &Scopes) -> Option { 42 | match self { 43 | Self::Primary(primary) => primary.eval(scopes), 44 | Self::Call(_) => None, 45 | Self::Unary(op, exp) => exp.eval(scopes).map(|exp| match op { 46 | UnaryOp::Neg => -exp, 47 | UnaryOp::LNot => (exp == 0) as i32, 48 | }), 49 | } 50 | } 51 | } 52 | 53 | impl Evaluate for MulExp { 54 | fn eval(&self, scopes: &Scopes) -> Option { 55 | match self { 56 | Self::Unary(exp) => exp.eval(scopes), 57 | Self::MulUnary(lhs, op, rhs) => match (lhs.eval(scopes), rhs.eval(scopes)) { 58 | (Some(lhs), Some(rhs)) => match op { 59 | MulOp::Mul => Some(lhs * rhs), 60 | MulOp::Div => (rhs != 0).then_some(lhs / rhs), 61 | MulOp::Mod => (rhs != 0).then_some(lhs % rhs), 62 | }, 63 | _ => None, 64 | }, 65 | } 66 | } 67 | } 68 | 69 | impl Evaluate for AddExp { 70 | fn eval(&self, scopes: &Scopes) -> Option { 71 | match self { 72 | Self::Mul(exp) => exp.eval(scopes), 73 | Self::AddMul(lhs, op, rhs) => match (lhs.eval(scopes), rhs.eval(scopes)) { 74 | (Some(lhs), Some(rhs)) => Some(match op { 75 | AddOp::Add => lhs + rhs, 76 | AddOp::Sub => lhs - rhs, 77 | }), 78 | _ => None, 79 | }, 80 | } 81 | } 82 | } 83 | 84 | impl Evaluate for RelExp { 85 | fn eval(&self, scopes: &Scopes) -> Option { 86 | match self { 87 | Self::Add(exp) => exp.eval(scopes), 88 | Self::RelAdd(lhs, op, rhs) => match (lhs.eval(scopes), rhs.eval(scopes)) { 89 | (Some(lhs), Some(rhs)) => Some(match op { 90 | RelOp::Lt => (lhs < rhs) as i32, 91 | RelOp::Gt => (lhs > rhs) as i32, 92 | RelOp::Le => (lhs <= rhs) as i32, 93 | RelOp::Ge => (lhs >= rhs) as i32, 94 | }), 95 | _ => None, 96 | }, 97 | } 98 | } 99 | } 100 | 101 | impl Evaluate for EqExp { 102 | fn eval(&self, scopes: &Scopes) -> Option { 103 | match self { 104 | Self::Rel(exp) => exp.eval(scopes), 105 | Self::EqRel(lhs, op, rhs) => match (lhs.eval(scopes), rhs.eval(scopes)) { 106 | (Some(lhs), Some(rhs)) => Some(match op { 107 | EqOp::Eq => (lhs == rhs) as i32, 108 | EqOp::Neq => (lhs != rhs) as i32, 109 | }), 110 | _ => None, 111 | }, 112 | } 113 | } 114 | } 115 | 116 | impl Evaluate for LAndExp { 117 | fn eval(&self, scopes: &Scopes) -> Option { 118 | match self { 119 | Self::Eq(exp) => exp.eval(scopes), 120 | Self::LAndEq(lhs, rhs) => match (lhs.eval(scopes), rhs.eval(scopes)) { 121 | (Some(lhs), Some(rhs)) => Some((lhs != 0 && rhs != 0) as i32), 122 | _ => None, 123 | }, 124 | } 125 | } 126 | } 127 | 128 | impl Evaluate for LOrExp { 129 | fn eval(&self, scopes: &Scopes) -> Option { 130 | match self { 131 | Self::LAnd(exp) => exp.eval(scopes), 132 | Self::LOrLAnd(lhs, rhs) => match (lhs.eval(scopes), rhs.eval(scopes)) { 133 | (Some(lhs), Some(rhs)) => Some((lhs != 0 || rhs != 0) as i32), 134 | _ => None, 135 | }, 136 | } 137 | } 138 | } 139 | 140 | impl Evaluate for ConstExp { 141 | fn eval(&self, scopes: &Scopes) -> Option { 142 | self.exp.eval(scopes) 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /src/irgen/func.rs: -------------------------------------------------------------------------------- 1 | use koopa::ir::Value; 2 | use koopa::ir::{builder::LocalBuilder, builder_traits::*}; 3 | use koopa::ir::{BasicBlock, Function, Program, Type}; 4 | 5 | /// Function information. 6 | pub struct FunctionInfo { 7 | func: Function, 8 | entry: BasicBlock, 9 | end: BasicBlock, 10 | cur: BasicBlock, 11 | ret_val: Option, 12 | } 13 | 14 | impl FunctionInfo { 15 | /// Creates a new function information. 16 | pub fn new(func: Function, entry: BasicBlock, end: BasicBlock, ret_val: Option) -> Self { 17 | Self { 18 | func, 19 | entry, 20 | end, 21 | cur: entry, 22 | ret_val, 23 | } 24 | } 25 | 26 | /// Returns the curren function. 27 | pub fn func(&self) -> Function { 28 | self.func 29 | } 30 | 31 | /// Returns the end basic block. 32 | pub fn end(&self) -> BasicBlock { 33 | self.end 34 | } 35 | 36 | /// Returns the return value. 37 | pub fn ret_val(&self) -> Option { 38 | self.ret_val 39 | } 40 | 41 | /// Creates a new basic block in function. 42 | pub fn new_bb(&self, program: &mut Program, name: Option<&str>) -> BasicBlock { 43 | program 44 | .func_mut(self.func) 45 | .dfg_mut() 46 | .new_bb() 47 | .basic_block(name.map(|s| s.into())) 48 | } 49 | 50 | /// Creates a new value in function. 51 | pub fn new_value<'p>(&self, program: &'p mut Program) -> LocalBuilder<'p> { 52 | program.func_mut(self.func).dfg_mut().new_value() 53 | } 54 | 55 | /// Pushes the basic block to the function, 56 | /// updates the current basic block. 57 | pub fn push_bb(&mut self, program: &mut Program, bb: BasicBlock) { 58 | program 59 | .func_mut(self.func) 60 | .layout_mut() 61 | .bbs_mut() 62 | .push_key_back(bb) 63 | .unwrap(); 64 | self.cur = bb; 65 | } 66 | 67 | /// Pushes the instruction to the back of the given basic block. 68 | pub fn push_inst_to(&self, program: &mut Program, bb: BasicBlock, inst: Value) { 69 | program 70 | .func_mut(self.func) 71 | .layout_mut() 72 | .bb_mut(bb) 73 | .insts_mut() 74 | .push_key_back(inst) 75 | .unwrap(); 76 | } 77 | 78 | /// Pushes the instruction to the back of the current basic block. 79 | pub fn push_inst(&self, program: &mut Program, inst: Value) { 80 | self.push_inst_to(program, self.cur, inst); 81 | } 82 | 83 | /// Creates a new allocation and inserts to the entry block. 84 | pub fn new_alloc(&self, program: &mut Program, ty: Type, name: Option<&str>) -> Value { 85 | let alloc = self.new_value(program).alloc(ty); 86 | if let Some(name) = name { 87 | program 88 | .func_mut(self.func) 89 | .dfg_mut() 90 | .set_value_name(alloc, Some(format!("@{}", name))); 91 | } 92 | self.push_inst_to(program, self.entry, alloc); 93 | alloc 94 | } 95 | 96 | /// Seals the entry block. 97 | pub fn seal_entry(&self, program: &mut Program, next: BasicBlock) { 98 | let jump = self.new_value(program).jump(next); 99 | self.push_inst_to(program, self.entry, jump); 100 | } 101 | 102 | /// Seals the function. 103 | pub fn seal_func(&mut self, program: &mut Program) { 104 | // jump to the end basic block 105 | let jump = self.new_value(program).jump(self.end); 106 | self.push_inst(program, jump); 107 | // push the end basic block 108 | self.push_bb(program, self.end); 109 | // generate return 110 | let value = self.ret_val.map(|alloc| { 111 | let value = self.new_value(program).load(alloc); 112 | self.push_inst(program, value); 113 | value 114 | }); 115 | let ret = self.new_value(program).ret(value); 116 | self.push_inst(program, ret); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/irgen/gen.rs: -------------------------------------------------------------------------------- 1 | use super::eval::Evaluate; 2 | use super::func::FunctionInfo; 3 | use super::scopes::{cur_func, cur_func_mut, Scopes}; 4 | use super::values::{ExpValue, Initializer, Value}; 5 | use super::{DimsToType, Error, Result}; 6 | use crate::ast::*; 7 | use koopa::ir::builder_traits::*; 8 | use koopa::ir::{BinaryOp, FunctionData, Program, Type, TypeKind}; 9 | 10 | /// Trait for generating Koopa IR program. 11 | pub trait GenerateProgram<'ast> { 12 | type Out; 13 | 14 | fn generate(&'ast self, program: &mut Program, scopes: &mut Scopes<'ast>) -> Result; 15 | } 16 | 17 | impl<'ast> GenerateProgram<'ast> for CompUnit { 18 | type Out = (); 19 | 20 | fn generate(&'ast self, program: &mut Program, scopes: &mut Scopes<'ast>) -> Result { 21 | let mut new_decl = |name, params_ty, ret_ty| { 22 | scopes 23 | .new_func( 24 | name, 25 | program.new_func(FunctionData::new_decl( 26 | format!("@{}", name), 27 | params_ty, 28 | ret_ty, 29 | )), 30 | ) 31 | .unwrap(); 32 | }; 33 | // generate SysY library function declarations 34 | new_decl("getint", vec![], Type::get_i32()); 35 | new_decl("getch", vec![], Type::get_i32()); 36 | new_decl( 37 | "getarray", 38 | vec![Type::get_pointer(Type::get_i32())], 39 | Type::get_i32(), 40 | ); 41 | new_decl("putint", vec![Type::get_i32()], Type::get_unit()); 42 | new_decl("putch", vec![Type::get_i32()], Type::get_unit()); 43 | new_decl( 44 | "putarray", 45 | vec![Type::get_i32(), Type::get_pointer(Type::get_i32())], 46 | Type::get_unit(), 47 | ); 48 | new_decl("starttime", vec![], Type::get_unit()); 49 | new_decl("stoptime", vec![], Type::get_unit()); 50 | // generate global items 51 | for item in &self.items { 52 | item.generate(program, scopes)?; 53 | } 54 | Ok(()) 55 | } 56 | } 57 | 58 | impl<'ast> GenerateProgram<'ast> for GlobalItem { 59 | type Out = (); 60 | 61 | fn generate(&'ast self, program: &mut Program, scopes: &mut Scopes<'ast>) -> Result { 62 | match self { 63 | Self::Decl(decl) => decl.generate(program, scopes), 64 | Self::FuncDef(def) => def.generate(program, scopes), 65 | } 66 | } 67 | } 68 | 69 | impl<'ast> GenerateProgram<'ast> for Decl { 70 | type Out = (); 71 | 72 | fn generate(&'ast self, program: &mut Program, scopes: &mut Scopes<'ast>) -> Result { 73 | match self { 74 | Self::Const(c) => c.generate(program, scopes), 75 | Self::Var(v) => v.generate(program, scopes), 76 | } 77 | } 78 | } 79 | 80 | impl<'ast> GenerateProgram<'ast> for ConstDecl { 81 | type Out = (); 82 | 83 | fn generate(&'ast self, program: &mut Program, scopes: &mut Scopes<'ast>) -> Result { 84 | for def in &self.defs { 85 | def.generate(program, scopes)?; 86 | } 87 | Ok(()) 88 | } 89 | } 90 | 91 | impl<'ast> GenerateProgram<'ast> for ConstDef { 92 | type Out = (); 93 | 94 | fn generate(&'ast self, program: &mut Program, scopes: &mut Scopes<'ast>) -> Result { 95 | // generate type and initializer 96 | let ty = self.dims.to_type(scopes)?; 97 | let init = self.init.generate(program, scopes)?.reshape(&ty)?; 98 | // generate constant 99 | if ty.is_i32() { 100 | match init { 101 | Initializer::Const(num) => scopes.new_value(&self.id, Value::Const(num))?, 102 | _ => unreachable!(), 103 | } 104 | } else { 105 | let value = if scopes.is_global() { 106 | let init = init.into_const(program, scopes)?; 107 | let value = program.new_value().global_alloc(init); 108 | program.set_value_name(value, Some(format!("@{}", self.id))); 109 | value 110 | } else { 111 | let info = cur_func!(scopes); 112 | let alloc = info.new_alloc(program, ty, Some(&self.id)); 113 | init.into_stores(program, scopes, alloc); 114 | alloc 115 | }; 116 | // add to scope 117 | scopes.new_value(&self.id, Value::Value(value))?; 118 | } 119 | Ok(()) 120 | } 121 | } 122 | 123 | impl<'ast> GenerateProgram<'ast> for ConstInitVal { 124 | type Out = Initializer; 125 | 126 | fn generate(&'ast self, program: &mut Program, scopes: &mut Scopes<'ast>) -> Result { 127 | Ok(match self { 128 | Self::Exp(exp) => Initializer::Const(exp.generate(program, scopes)?), 129 | Self::List(list) => Initializer::List( 130 | list 131 | .iter() 132 | .map(|v| v.generate(program, scopes)) 133 | .collect::>()?, 134 | ), 135 | }) 136 | } 137 | } 138 | 139 | impl<'ast> GenerateProgram<'ast> for VarDecl { 140 | type Out = (); 141 | 142 | fn generate(&'ast self, program: &mut Program, scopes: &mut Scopes<'ast>) -> Result { 143 | for def in &self.defs { 144 | def.generate(program, scopes)?; 145 | } 146 | Ok(()) 147 | } 148 | } 149 | 150 | impl<'ast> GenerateProgram<'ast> for VarDef { 151 | type Out = (); 152 | 153 | fn generate(&'ast self, program: &mut Program, scopes: &mut Scopes<'ast>) -> Result { 154 | // generate type and initializer 155 | let ty = self.dims.to_type(scopes)?; 156 | let init = self 157 | .init 158 | .as_ref() 159 | .map(|i| i.generate(program, scopes)?.reshape(&ty)) 160 | .transpose()?; 161 | // generate variable 162 | let value = if scopes.is_global() { 163 | let init = match init { 164 | Some(init) => init.into_const(program, scopes)?, 165 | None => program.new_value().zero_init(ty), 166 | }; 167 | let value = program.new_value().global_alloc(init); 168 | program.set_value_name(value, Some(format!("@{}", self.id))); 169 | value 170 | } else { 171 | let info = cur_func!(scopes); 172 | let alloc = info.new_alloc(program, ty, Some(&self.id)); 173 | if let Some(init) = init { 174 | init.into_stores(program, scopes, alloc); 175 | } 176 | alloc 177 | }; 178 | // add to scope 179 | scopes.new_value(&self.id, Value::Value(value))?; 180 | Ok(()) 181 | } 182 | } 183 | 184 | impl<'ast> GenerateProgram<'ast> for InitVal { 185 | type Out = Initializer; 186 | 187 | fn generate(&'ast self, program: &mut Program, scopes: &mut Scopes<'ast>) -> Result { 188 | Ok(match self { 189 | Self::Exp(exp) => { 190 | if scopes.is_global() { 191 | Initializer::Const(exp.eval(scopes).ok_or(Error::FailedToEval)?) 192 | } else { 193 | Initializer::Value(exp.generate(program, scopes)?.into_int(program, scopes)?) 194 | } 195 | } 196 | Self::List(list) => Initializer::List( 197 | list 198 | .iter() 199 | .map(|v| v.generate(program, scopes)) 200 | .collect::>()?, 201 | ), 202 | }) 203 | } 204 | } 205 | 206 | impl<'ast> GenerateProgram<'ast> for FuncDef { 207 | type Out = (); 208 | 209 | fn generate(&'ast self, program: &mut Program, scopes: &mut Scopes<'ast>) -> Result { 210 | // generate parameter types and return type 211 | let params_ty = self 212 | .params 213 | .iter() 214 | .map(|p| p.generate(program, scopes)) 215 | .collect::>>()?; 216 | let ret_ty = self.ty.generate(program, scopes)?; 217 | // create new fucntion 218 | let mut data = FunctionData::new(format!("@{}", self.id), params_ty, ret_ty); 219 | // get parameter list 220 | let params = data.params().to_owned(); 221 | // generate entry/end/cur block 222 | let entry = data.dfg_mut().new_bb().basic_block(Some("%entry".into())); 223 | let end = data.dfg_mut().new_bb().basic_block(Some("%end".into())); 224 | let cur = data.dfg_mut().new_bb().basic_block(None); 225 | let mut ret_val = None; 226 | // generate return value 227 | if matches!(self.ty, FuncType::Int) { 228 | let alloc = data.dfg_mut().new_value().alloc(Type::get_i32()); 229 | data.dfg_mut().set_value_name(alloc, Some("%ret".into())); 230 | ret_val = Some(alloc); 231 | } 232 | // update function information 233 | let func = program.new_func(data); 234 | let mut info = FunctionInfo::new(func, entry, end, ret_val); 235 | info.push_bb(program, entry); 236 | if let Some(ret_val) = info.ret_val() { 237 | info.push_inst(program, ret_val); 238 | } 239 | info.push_bb(program, cur); 240 | // generate allocations for parameters 241 | scopes.enter(); 242 | for (param, value) in self.params.iter().zip(params) { 243 | let ty = program.func(func).dfg().value(value).ty().clone(); 244 | let alloc = info.new_alloc(program, ty, Some(¶m.id)); 245 | let store = info.new_value(program).store(value, alloc); 246 | info.push_inst(program, store); 247 | scopes.new_value(¶m.id, Value::Value(alloc))?; 248 | } 249 | // update scope 250 | scopes.new_func(&self.id, func)?; 251 | scopes.cur_func = Some(info); 252 | // generate function body 253 | self.block.generate(program, scopes)?; 254 | scopes.exit(); 255 | // handle end basic block 256 | let mut info = scopes.cur_func.take().unwrap(); 257 | info.seal_entry(program, cur); 258 | info.seal_func(program); 259 | Ok(()) 260 | } 261 | } 262 | 263 | impl<'ast> GenerateProgram<'ast> for FuncType { 264 | type Out = Type; 265 | 266 | fn generate(&'ast self, _: &mut Program, _: &mut Scopes<'ast>) -> Result { 267 | Ok(match self { 268 | Self::Void => Type::get_unit(), 269 | Self::Int => Type::get_i32(), 270 | }) 271 | } 272 | } 273 | 274 | impl<'ast> GenerateProgram<'ast> for FuncFParam { 275 | type Out = Type; 276 | 277 | fn generate(&'ast self, _: &mut Program, scopes: &mut Scopes<'ast>) -> Result { 278 | Ok(match &self.dims { 279 | Some(dims) => Type::get_pointer(dims.to_type(scopes)?), 280 | None => Type::get_i32(), 281 | }) 282 | } 283 | } 284 | 285 | impl<'ast> GenerateProgram<'ast> for Block { 286 | type Out = (); 287 | 288 | fn generate(&'ast self, program: &mut Program, scopes: &mut Scopes<'ast>) -> Result { 289 | scopes.enter(); 290 | for item in &self.items { 291 | item.generate(program, scopes)?; 292 | } 293 | scopes.exit(); 294 | Ok(()) 295 | } 296 | } 297 | 298 | impl<'ast> GenerateProgram<'ast> for BlockItem { 299 | type Out = (); 300 | 301 | fn generate(&'ast self, program: &mut Program, scopes: &mut Scopes<'ast>) -> Result { 302 | match self { 303 | Self::Decl(decl) => decl.generate(program, scopes), 304 | Self::Stmt(stmt) => stmt.generate(program, scopes), 305 | } 306 | } 307 | } 308 | 309 | impl<'ast> GenerateProgram<'ast> for Stmt { 310 | type Out = (); 311 | 312 | fn generate(&'ast self, program: &mut Program, scopes: &mut Scopes<'ast>) -> Result { 313 | match self { 314 | Self::Assign(s) => s.generate(program, scopes), 315 | Self::ExpStmt(s) => s.generate(program, scopes), 316 | Self::Block(s) => s.generate(program, scopes), 317 | Self::If(s) => s.generate(program, scopes), 318 | Self::While(s) => s.generate(program, scopes), 319 | Self::Break(s) => s.generate(program, scopes), 320 | Self::Continue(s) => s.generate(program, scopes), 321 | Self::Return(s) => s.generate(program, scopes), 322 | } 323 | } 324 | } 325 | 326 | impl<'ast> GenerateProgram<'ast> for Assign { 327 | type Out = (); 328 | 329 | fn generate(&'ast self, program: &mut Program, scopes: &mut Scopes<'ast>) -> Result { 330 | // generate value and left-value pointer 331 | let exp = self 332 | .exp 333 | .generate(program, scopes)? 334 | .into_int(program, scopes)?; 335 | let lval = self.lval.generate(program, scopes)?.into_ptr()?; 336 | // generate store 337 | let info = cur_func!(scopes); 338 | let store = info.new_value(program).store(exp, lval); 339 | info.push_inst(program, store); 340 | Ok(()) 341 | } 342 | } 343 | 344 | impl<'ast> GenerateProgram<'ast> for ExpStmt { 345 | type Out = (); 346 | 347 | fn generate(&'ast self, program: &mut Program, scopes: &mut Scopes<'ast>) -> Result { 348 | if let Some(exp) = &self.exp { 349 | exp.generate(program, scopes)?; 350 | } 351 | Ok(()) 352 | } 353 | } 354 | 355 | impl<'ast> GenerateProgram<'ast> for If { 356 | type Out = (); 357 | 358 | fn generate(&'ast self, program: &mut Program, scopes: &mut Scopes<'ast>) -> Result { 359 | // generate condition 360 | let cond = self 361 | .cond 362 | .generate(program, scopes)? 363 | .into_int(program, scopes)?; 364 | // generate branch and then/else basic block 365 | let info = cur_func_mut!(scopes); 366 | let then_bb = info.new_bb(program, Some("%if_then")); 367 | let else_bb = info.new_bb(program, Some("%if_else")); 368 | let br = info.new_value(program).branch(cond, then_bb, else_bb); 369 | info.push_inst(program, br); 370 | info.push_bb(program, then_bb); 371 | // generate then statement 372 | self.then.generate(program, scopes)?; 373 | // generate jump and end basic block 374 | let info = cur_func_mut!(scopes); 375 | let end_bb = info.new_bb(program, Some("%if_end")); 376 | let jump = info.new_value(program).jump(end_bb); 377 | info.push_inst(program, jump); 378 | info.push_bb(program, else_bb); 379 | // generate else statement 380 | if let Some(else_then) = &self.else_then { 381 | else_then.generate(program, scopes)?; 382 | } 383 | // generate jump 384 | let info = cur_func_mut!(scopes); 385 | let jump = info.new_value(program).jump(end_bb); 386 | info.push_inst(program, jump); 387 | info.push_bb(program, end_bb); 388 | Ok(()) 389 | } 390 | } 391 | 392 | impl<'ast> GenerateProgram<'ast> for While { 393 | type Out = (); 394 | 395 | fn generate(&'ast self, program: &mut Program, scopes: &mut Scopes<'ast>) -> Result { 396 | // generate loop entry basic block 397 | let info = cur_func_mut!(scopes); 398 | let entry_bb = info.new_bb(program, Some("%while_entry")); 399 | let jump = info.new_value(program).jump(entry_bb); 400 | info.push_inst(program, jump); 401 | info.push_bb(program, entry_bb); 402 | // generate condition 403 | let cond = self 404 | .cond 405 | .generate(program, scopes)? 406 | .into_int(program, scopes)?; 407 | // generate branch and loop body/end basic block 408 | let info = cur_func_mut!(scopes); 409 | let body_bb = info.new_bb(program, Some("%while_body")); 410 | let end_bb = info.new_bb(program, Some("%while_end")); 411 | let br = info.new_value(program).branch(cond, body_bb, end_bb); 412 | info.push_inst(program, br); 413 | info.push_bb(program, body_bb); 414 | // generate loop body 415 | scopes.loop_info.push((entry_bb, end_bb)); 416 | self.body.generate(program, scopes)?; 417 | scopes.loop_info.pop(); 418 | // generate jump 419 | let info = cur_func_mut!(scopes); 420 | let jump = info.new_value(program).jump(entry_bb); 421 | info.push_inst(program, jump); 422 | info.push_bb(program, end_bb); 423 | Ok(()) 424 | } 425 | } 426 | 427 | impl<'ast> GenerateProgram<'ast> for Break { 428 | type Out = (); 429 | 430 | fn generate(&'ast self, program: &mut Program, scopes: &mut Scopes<'ast>) -> Result { 431 | // jump to the end of loop 432 | let info = &mut cur_func_mut!(scopes); 433 | let (_, end) = scopes.loop_info.last().ok_or(Error::NotInLoop)?; 434 | let jump = info.new_value(program).jump(*end); 435 | info.push_inst(program, jump); 436 | // push new basic block 437 | let next = info.new_bb(program, None); 438 | info.push_bb(program, next); 439 | Ok(()) 440 | } 441 | } 442 | 443 | impl<'ast> GenerateProgram<'ast> for Continue { 444 | type Out = (); 445 | 446 | fn generate(&'ast self, program: &mut Program, scopes: &mut Scopes<'ast>) -> Result { 447 | // jump to the entry of loop 448 | let info = &mut cur_func_mut!(scopes); 449 | let (entry, _) = scopes.loop_info.last().ok_or(Error::NotInLoop)?; 450 | let jump = info.new_value(program).jump(*entry); 451 | info.push_inst(program, jump); 452 | // push new basic block 453 | let next = info.new_bb(program, None); 454 | info.push_bb(program, next); 455 | Ok(()) 456 | } 457 | } 458 | 459 | impl<'ast> GenerateProgram<'ast> for Return { 460 | type Out = (); 461 | 462 | fn generate(&'ast self, program: &mut Program, scopes: &mut Scopes<'ast>) -> Result { 463 | if let Some(ret_val) = cur_func!(scopes).ret_val() { 464 | // generate store 465 | if let Some(val) = &self.exp { 466 | let value = val.generate(program, scopes)?.into_int(program, scopes)?; 467 | let info = cur_func!(scopes); 468 | let store = info.new_value(program).store(value, ret_val); 469 | info.push_inst(program, store); 470 | } 471 | } else if self.exp.is_some() { 472 | return Err(Error::RetValInVoidFunc); 473 | } 474 | // jump to the end basic block 475 | let info = &mut cur_func_mut!(scopes); 476 | let jump = info.new_value(program).jump(info.end()); 477 | info.push_inst(program, jump); 478 | // push new basic block 479 | let next = info.new_bb(program, None); 480 | info.push_bb(program, next); 481 | Ok(()) 482 | } 483 | } 484 | 485 | impl<'ast> GenerateProgram<'ast> for Exp { 486 | type Out = ExpValue; 487 | 488 | fn generate(&'ast self, program: &mut Program, scopes: &mut Scopes<'ast>) -> Result { 489 | self.lor.generate(program, scopes) 490 | } 491 | } 492 | 493 | impl<'ast> GenerateProgram<'ast> for LVal { 494 | type Out = ExpValue; 495 | 496 | fn generate(&'ast self, program: &mut Program, scopes: &mut Scopes<'ast>) -> Result { 497 | // handle constant 498 | let mut value = match scopes.value(&self.id)? { 499 | Value::Value(value) => *value, 500 | Value::Const(num) => { 501 | return if self.indices.is_empty() { 502 | let value = cur_func!(scopes).new_value(program).integer(*num); 503 | Ok(ExpValue::Int(value)) 504 | } else { 505 | Err(Error::DerefInt) 506 | }; 507 | } 508 | }; 509 | // check type 510 | let mut is_ptr_ptr = false; 511 | let mut dims = match scopes.ty(program, value).kind() { 512 | TypeKind::Pointer(base) => { 513 | let mut ty = base; 514 | let mut dims = 0; 515 | loop { 516 | ty = match ty.kind() { 517 | TypeKind::Array(base, _) => base, 518 | TypeKind::Pointer(base) => { 519 | is_ptr_ptr = true; 520 | base 521 | } 522 | _ => break dims, 523 | }; 524 | dims += 1; 525 | } 526 | } 527 | _ => 0, 528 | }; 529 | // generate load for array parameter 530 | if is_ptr_ptr { 531 | let info = cur_func!(scopes); 532 | value = info.new_value(program).load(value); 533 | info.push_inst(program, value); 534 | } 535 | // handle array dereference 536 | for (i, index) in self.indices.iter().enumerate() { 537 | // check if dereferencing integer 538 | if dims == 0 { 539 | return Err(Error::DerefInt); 540 | } 541 | dims -= 1; 542 | // generate index 543 | let index = index.generate(program, scopes)?.into_val(program, scopes)?; 544 | // generate pointer calculation 545 | let info = cur_func!(scopes); 546 | value = if is_ptr_ptr && i == 0 { 547 | info.new_value(program).get_ptr(value, index) 548 | } else { 549 | info.new_value(program).get_elem_ptr(value, index) 550 | }; 551 | info.push_inst(program, value); 552 | } 553 | // generate pointer calculation for function arguments 554 | if dims == 0 { 555 | Ok(ExpValue::IntPtr(value)) 556 | } else { 557 | if !is_ptr_ptr || !self.indices.is_empty() { 558 | let info = cur_func!(scopes); 559 | let zero = info.new_value(program).integer(0); 560 | value = info.new_value(program).get_elem_ptr(value, zero); 561 | info.push_inst(program, value); 562 | } 563 | Ok(ExpValue::ArrPtr(value)) 564 | } 565 | } 566 | } 567 | 568 | impl<'ast> GenerateProgram<'ast> for PrimaryExp { 569 | type Out = ExpValue; 570 | 571 | fn generate(&'ast self, program: &mut Program, scopes: &mut Scopes<'ast>) -> Result { 572 | match self { 573 | Self::Exp(exp) => exp.generate(program, scopes), 574 | Self::LVal(lval) => lval.generate(program, scopes), 575 | Self::Number(num) => Ok(ExpValue::Int( 576 | cur_func!(scopes).new_value(program).integer(*num), 577 | )), 578 | } 579 | } 580 | } 581 | 582 | impl<'ast> GenerateProgram<'ast> for UnaryExp { 583 | type Out = ExpValue; 584 | 585 | fn generate(&'ast self, program: &mut Program, scopes: &mut Scopes<'ast>) -> Result { 586 | match self { 587 | Self::Primary(exp) => exp.generate(program, scopes), 588 | Self::Call(call) => call.generate(program, scopes), 589 | Self::Unary(op, exp) => { 590 | let exp = exp.generate(program, scopes)?.into_int(program, scopes)?; 591 | let info = cur_func!(scopes); 592 | let zero = info.new_value(program).integer(0); 593 | let value = match op { 594 | UnaryOp::Neg => info.new_value(program).binary(BinaryOp::Sub, zero, exp), 595 | UnaryOp::LNot => info.new_value(program).binary(BinaryOp::Eq, exp, zero), 596 | }; 597 | info.push_inst(program, value); 598 | Ok(ExpValue::Int(value)) 599 | } 600 | } 601 | } 602 | } 603 | 604 | impl<'ast> GenerateProgram<'ast> for FuncCall { 605 | type Out = ExpValue; 606 | 607 | fn generate(&'ast self, program: &mut Program, scopes: &mut Scopes<'ast>) -> Result { 608 | // get function from scope 609 | let func = scopes.func(&self.id)?; 610 | // get function type 611 | let (params_ty, is_void) = match program.func(func).ty().kind() { 612 | TypeKind::Function(params, ret) => (params.clone(), ret.is_unit()), 613 | _ => unreachable!(), 614 | }; 615 | // generate arguments 616 | let args = self 617 | .args 618 | .iter() 619 | .map(|a| a.generate(program, scopes)?.into_val(program, scopes)) 620 | .collect::>>()?; 621 | // check argument types 622 | if params_ty.len() != args.len() { 623 | return Err(Error::ArgMismatch); 624 | } 625 | for (param_ty, arg) in params_ty.iter().zip(&args) { 626 | if param_ty != &scopes.ty(program, *arg) { 627 | return Err(Error::ArgMismatch); 628 | } 629 | } 630 | // generate function call 631 | let info = cur_func!(scopes); 632 | let call = info.new_value(program).call(func, args); 633 | info.push_inst(program, call); 634 | // return value if not void 635 | if is_void { 636 | Ok(ExpValue::Void) 637 | } else { 638 | Ok(ExpValue::Int(call)) 639 | } 640 | } 641 | } 642 | 643 | impl<'ast> GenerateProgram<'ast> for MulExp { 644 | type Out = ExpValue; 645 | 646 | fn generate(&'ast self, program: &mut Program, scopes: &mut Scopes<'ast>) -> Result { 647 | match self { 648 | Self::Unary(exp) => exp.generate(program, scopes), 649 | Self::MulUnary(lhs, op, rhs) => { 650 | let lhs = lhs.generate(program, scopes)?.into_int(program, scopes)?; 651 | let rhs = rhs.generate(program, scopes)?.into_int(program, scopes)?; 652 | let op = op.generate(program, scopes)?; 653 | let info = cur_func!(scopes); 654 | let value = info.new_value(program).binary(op, lhs, rhs); 655 | info.push_inst(program, value); 656 | Ok(ExpValue::Int(value)) 657 | } 658 | } 659 | } 660 | } 661 | 662 | impl<'ast> GenerateProgram<'ast> for AddExp { 663 | type Out = ExpValue; 664 | 665 | fn generate(&'ast self, program: &mut Program, scopes: &mut Scopes<'ast>) -> Result { 666 | match self { 667 | Self::Mul(exp) => exp.generate(program, scopes), 668 | Self::AddMul(lhs, op, rhs) => { 669 | let lhs = lhs.generate(program, scopes)?.into_int(program, scopes)?; 670 | let rhs = rhs.generate(program, scopes)?.into_int(program, scopes)?; 671 | let op = op.generate(program, scopes)?; 672 | let info = cur_func!(scopes); 673 | let value = info.new_value(program).binary(op, lhs, rhs); 674 | info.push_inst(program, value); 675 | Ok(ExpValue::Int(value)) 676 | } 677 | } 678 | } 679 | } 680 | 681 | impl<'ast> GenerateProgram<'ast> for RelExp { 682 | type Out = ExpValue; 683 | 684 | fn generate(&'ast self, program: &mut Program, scopes: &mut Scopes<'ast>) -> Result { 685 | match self { 686 | Self::Add(exp) => exp.generate(program, scopes), 687 | Self::RelAdd(lhs, op, rhs) => { 688 | let lhs = lhs.generate(program, scopes)?.into_int(program, scopes)?; 689 | let rhs = rhs.generate(program, scopes)?.into_int(program, scopes)?; 690 | let op = op.generate(program, scopes)?; 691 | let info = cur_func!(scopes); 692 | let value = info.new_value(program).binary(op, lhs, rhs); 693 | info.push_inst(program, value); 694 | Ok(ExpValue::Int(value)) 695 | } 696 | } 697 | } 698 | } 699 | 700 | impl<'ast> GenerateProgram<'ast> for EqExp { 701 | type Out = ExpValue; 702 | 703 | fn generate(&'ast self, program: &mut Program, scopes: &mut Scopes<'ast>) -> Result { 704 | match self { 705 | Self::Rel(exp) => exp.generate(program, scopes), 706 | Self::EqRel(lhs, op, rhs) => { 707 | let lhs = lhs.generate(program, scopes)?.into_int(program, scopes)?; 708 | let rhs = rhs.generate(program, scopes)?.into_int(program, scopes)?; 709 | let op = op.generate(program, scopes)?; 710 | let info = cur_func!(scopes); 711 | let value = info.new_value(program).binary(op, lhs, rhs); 712 | info.push_inst(program, value); 713 | Ok(ExpValue::Int(value)) 714 | } 715 | } 716 | } 717 | } 718 | 719 | /// Generates logical operators. 720 | macro_rules! generate_logical_ops { 721 | ( 722 | $lhs:expr, $rhs:expr, $program:expr, $scopes:expr, 723 | $prefix:literal, $rhs_bb:ident, $end_bb:ident, $tbb:ident, $fbb:ident 724 | ) => {{ 725 | // generate result 726 | let result = cur_func!($scopes).new_alloc($program, Type::get_i32(), None); 727 | // generate left-hand side expression 728 | let lhs = $lhs 729 | .generate($program, $scopes)? 730 | .into_int($program, $scopes)?; 731 | let info = cur_func_mut!($scopes); 732 | let zero = info.new_value($program).integer(0); 733 | let lhs = info.new_value($program).binary(BinaryOp::NotEq, lhs, zero); 734 | info.push_inst($program, lhs); 735 | let store = info.new_value($program).store(lhs, result); 736 | info.push_inst($program, store); 737 | // generate basic blocks and branch 738 | let $rhs_bb = info.new_bb($program, Some(concat!("%", $prefix, "_rhs"))); 739 | let $end_bb = info.new_bb($program, Some(concat!("%", $prefix, "_end"))); 740 | let br = info.new_value($program).branch(lhs, $tbb, $fbb); 741 | info.push_inst($program, br); 742 | // generate right-hand side expression 743 | info.push_bb($program, $rhs_bb); 744 | let rhs = $rhs 745 | .generate($program, $scopes)? 746 | .into_int($program, $scopes)?; 747 | let info = cur_func_mut!($scopes); 748 | let rhs = info.new_value($program).binary(BinaryOp::NotEq, rhs, zero); 749 | info.push_inst($program, rhs); 750 | let store = info.new_value($program).store(rhs, result); 751 | info.push_inst($program, store); 752 | // generate jump 753 | let jump = info.new_value($program).jump($end_bb); 754 | info.push_inst($program, jump); 755 | info.push_bb($program, $end_bb); 756 | // generate load 757 | let load = info.new_value($program).load(result); 758 | info.push_inst($program, load); 759 | Ok(ExpValue::Int(load)) 760 | }}; 761 | } 762 | 763 | impl<'ast> GenerateProgram<'ast> for LAndExp { 764 | type Out = ExpValue; 765 | 766 | fn generate(&'ast self, program: &mut Program, scopes: &mut Scopes<'ast>) -> Result { 767 | match self { 768 | Self::Eq(exp) => exp.generate(program, scopes), 769 | Self::LAndEq(lhs, rhs) => generate_logical_ops! { 770 | lhs, rhs, program, scopes, "land", rhs_bb, end_bb, rhs_bb, end_bb 771 | }, 772 | } 773 | } 774 | } 775 | 776 | impl<'ast> GenerateProgram<'ast> for LOrExp { 777 | type Out = ExpValue; 778 | 779 | fn generate(&'ast self, program: &mut Program, scopes: &mut Scopes<'ast>) -> Result { 780 | match self { 781 | Self::LAnd(exp) => exp.generate(program, scopes), 782 | Self::LOrLAnd(lhs, rhs) => generate_logical_ops! { 783 | lhs, rhs, program, scopes, "lor", rhs_bb, end_bb, end_bb, rhs_bb 784 | }, 785 | } 786 | } 787 | } 788 | 789 | impl<'ast> GenerateProgram<'ast> for ConstExp { 790 | type Out = i32; 791 | 792 | fn generate(&'ast self, _: &mut Program, scopes: &mut Scopes<'ast>) -> Result { 793 | self.eval(scopes).ok_or(Error::FailedToEval) 794 | } 795 | } 796 | 797 | impl<'ast> GenerateProgram<'ast> for MulOp { 798 | type Out = BinaryOp; 799 | 800 | fn generate(&'ast self, _: &mut Program, _: &mut Scopes<'ast>) -> Result { 801 | Ok(match self { 802 | MulOp::Mul => BinaryOp::Mul, 803 | MulOp::Div => BinaryOp::Div, 804 | MulOp::Mod => BinaryOp::Mod, 805 | }) 806 | } 807 | } 808 | 809 | impl<'ast> GenerateProgram<'ast> for AddOp { 810 | type Out = BinaryOp; 811 | 812 | fn generate(&'ast self, _: &mut Program, _: &mut Scopes<'ast>) -> Result { 813 | Ok(match self { 814 | AddOp::Add => BinaryOp::Add, 815 | AddOp::Sub => BinaryOp::Sub, 816 | }) 817 | } 818 | } 819 | 820 | impl<'ast> GenerateProgram<'ast> for RelOp { 821 | type Out = BinaryOp; 822 | 823 | fn generate(&'ast self, _: &mut Program, _: &mut Scopes<'ast>) -> Result { 824 | Ok(match self { 825 | RelOp::Lt => BinaryOp::Lt, 826 | RelOp::Gt => BinaryOp::Gt, 827 | RelOp::Le => BinaryOp::Le, 828 | RelOp::Ge => BinaryOp::Ge, 829 | }) 830 | } 831 | } 832 | 833 | impl<'ast> GenerateProgram<'ast> for EqOp { 834 | type Out = BinaryOp; 835 | 836 | fn generate(&'ast self, _: &mut Program, _: &mut Scopes<'ast>) -> Result { 837 | Ok(match self { 838 | EqOp::Eq => BinaryOp::Eq, 839 | EqOp::Neq => BinaryOp::NotEq, 840 | }) 841 | } 842 | } 843 | -------------------------------------------------------------------------------- /src/irgen/mod.rs: -------------------------------------------------------------------------------- 1 | mod eval; 2 | mod func; 3 | mod gen; 4 | mod scopes; 5 | mod values; 6 | 7 | use crate::ast::{CompUnit, ConstExp}; 8 | use eval::Evaluate; 9 | use gen::GenerateProgram; 10 | use koopa::ir::{Program, Type}; 11 | use scopes::Scopes; 12 | use std::fmt; 13 | 14 | /// Generates Koopa IR program for the given compile unit (ASTs). 15 | pub fn generate_program(comp_unit: &CompUnit) -> Result { 16 | let mut program = Program::new(); 17 | comp_unit.generate(&mut program, &mut Scopes::new())?; 18 | Ok(program) 19 | } 20 | 21 | /// Error returned by IR generator. 22 | pub enum Error { 23 | DuplicatedDef, 24 | SymbolNotFound, 25 | FailedToEval, 26 | InvalidArrayLen, 27 | InvalidInit, 28 | ArrayAssign, 29 | NotInLoop, 30 | RetValInVoidFunc, 31 | DerefInt, 32 | UseVoidValue, 33 | ArgMismatch, 34 | NonIntCalc, 35 | } 36 | 37 | impl fmt::Display for Error { 38 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 39 | match self { 40 | Self::DuplicatedDef => write!(f, "duplicated symbol definition"), 41 | Self::SymbolNotFound => write!(f, "symbol not found"), 42 | Self::FailedToEval => write!(f, "failed to evaluate constant"), 43 | Self::InvalidArrayLen => write!(f, "invalid array length"), 44 | Self::InvalidInit => write!(f, "invalid initializer"), 45 | Self::ArrayAssign => write!(f, "assigning to array"), 46 | Self::NotInLoop => write!(f, "using break/continue outside of loop"), 47 | Self::RetValInVoidFunc => write!(f, "returning value in void fucntion"), 48 | Self::DerefInt => write!(f, "dereferencing an integer"), 49 | Self::UseVoidValue => write!(f, "using a void value"), 50 | Self::ArgMismatch => write!(f, "argument mismatch"), 51 | Self::NonIntCalc => write!(f, "non-integer calculation"), 52 | } 53 | } 54 | } 55 | 56 | impl fmt::Debug for Error { 57 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 58 | write!(f, "{}", self) 59 | } 60 | } 61 | 62 | /// Result type of IR generator. 63 | pub type Result = std::result::Result; 64 | 65 | /// Helper trait for converting dimentions to type. 66 | pub(crate) trait DimsToType { 67 | fn to_type(&self, scopes: &Scopes) -> Result; 68 | } 69 | 70 | impl DimsToType for Vec { 71 | fn to_type(&self, scopes: &Scopes) -> Result { 72 | self.iter().rev().fold(Ok(Type::get_i32()), |b, exp| { 73 | let base = b?; 74 | let len = exp.eval(scopes).ok_or(Error::FailedToEval)?; 75 | (len >= 1) 76 | .then(|| Type::get_array(base, len as usize)) 77 | .ok_or(Error::InvalidArrayLen) 78 | }) 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/irgen/scopes.rs: -------------------------------------------------------------------------------- 1 | use super::func::FunctionInfo; 2 | use super::values::Value; 3 | use super::{Error, Result}; 4 | use koopa::ir::Value as IrValue; 5 | use koopa::ir::{BasicBlock, Function, Program, Type}; 6 | use std::collections::HashMap; 7 | 8 | /// Scopes, including all values, constants and functions definitions. 9 | pub struct Scopes<'ast> { 10 | vals: Vec>, 11 | funcs: HashMap<&'ast str, Function>, 12 | pub cur_func: Option, 13 | pub loop_info: Vec<(BasicBlock, BasicBlock)>, 14 | } 15 | 16 | /// Returns a reference to the current function information. 17 | macro_rules! cur_func { 18 | ($scopes:expr) => { 19 | $scopes.cur_func.as_ref().unwrap() 20 | }; 21 | } 22 | pub(crate) use cur_func; 23 | 24 | /// Returns a mutable reference to the current function information. 25 | macro_rules! cur_func_mut { 26 | ($scopes:expr) => { 27 | $scopes.cur_func.as_mut().unwrap() 28 | }; 29 | } 30 | pub(crate) use cur_func_mut; 31 | 32 | impl<'ast> Scopes<'ast> { 33 | /// Creates a new `Scopes`. 34 | pub fn new() -> Self { 35 | Self { 36 | vals: vec![HashMap::new()], 37 | funcs: HashMap::new(), 38 | cur_func: None, 39 | loop_info: Vec::new(), 40 | } 41 | } 42 | 43 | /// Returns `true` if is currently in global scope. 44 | pub fn is_global(&self) -> bool { 45 | self.cur_func.is_none() 46 | } 47 | 48 | /// Inserts a new value to the current scope. 49 | pub fn new_value(&mut self, id: &'ast str, value: Value) -> Result<()> { 50 | let is_global = self.is_global(); 51 | let cur = self.vals.last_mut().unwrap(); 52 | if cur.contains_key(id) || (is_global && self.funcs.contains_key(id)) { 53 | Err(Error::DuplicatedDef) 54 | } else { 55 | cur.insert(id, value); 56 | Ok(()) 57 | } 58 | } 59 | 60 | /// Returns the value by the given identifier. 61 | pub fn value(&self, id: &str) -> Result<&Value> { 62 | let mut cur = self.vals.len() as i32 - 1; 63 | while cur >= 0 { 64 | if let Some(value) = self.vals[cur as usize].get(id) { 65 | return Ok(value); 66 | } 67 | cur -= 1; 68 | } 69 | Err(Error::SymbolNotFound) 70 | } 71 | 72 | /// Inserts a new function to the current scope. 73 | pub fn new_func(&mut self, id: &'ast str, func: Function) -> Result<()> { 74 | if self.funcs.contains_key(id) || self.vals.first().unwrap().contains_key(id) { 75 | Err(Error::DuplicatedDef) 76 | } else { 77 | self.funcs.insert(id, func); 78 | Ok(()) 79 | } 80 | } 81 | 82 | /// Returns the function by the given identifier. 83 | pub fn func(&self, id: &str) -> Result { 84 | self.funcs.get(id).copied().ok_or(Error::SymbolNotFound) 85 | } 86 | 87 | /// Enters a new scope. 88 | pub fn enter(&mut self) { 89 | self.vals.push(HashMap::new()); 90 | } 91 | 92 | /// Exits from the current scope. 93 | pub fn exit(&mut self) { 94 | self.vals.pop(); 95 | } 96 | 97 | /// Returns type of the given value. 98 | pub fn ty(&self, program: &Program, value: IrValue) -> Type { 99 | if value.is_global() { 100 | program.borrow_value(value).ty().clone() 101 | } else { 102 | program 103 | .func(cur_func!(self).func()) 104 | .dfg() 105 | .value(value) 106 | .ty() 107 | .clone() 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/irgen/values.rs: -------------------------------------------------------------------------------- 1 | use super::scopes::{cur_func, Scopes}; 2 | use super::{Error, Result}; 3 | use koopa::ir::builder_traits::*; 4 | use koopa::ir::Value as IrValue; 5 | use koopa::ir::{Program, Type, TypeKind}; 6 | use std::iter::repeat_with; 7 | 8 | /// A value. 9 | pub enum Value { 10 | /// Koopa IR value. 11 | Value(IrValue), 12 | /// Constant integer. 13 | Const(i32), 14 | } 15 | 16 | /// An initializer. 17 | pub enum Initializer { 18 | Const(i32), 19 | Value(IrValue), 20 | List(Vec), 21 | } 22 | 23 | impl Initializer { 24 | /// Reshapes the current initializer by using the given type. 25 | /// Returns the reshaped initializer. 26 | pub fn reshape(self, mut ty: &Type) -> Result { 27 | // get length list 28 | // array `int a[2][3][4]` yields [(4, 4), (3, 12), (2, 24)] 29 | let mut lens = Vec::new(); 30 | loop { 31 | match ty.kind() { 32 | TypeKind::Int32 => break, 33 | TypeKind::Array(base, len) => { 34 | lens.push(*len); 35 | ty = base; 36 | } 37 | _ => unreachable!(), 38 | } 39 | } 40 | let mut last_len = 1; 41 | let lens: Vec<_> = lens 42 | .into_iter() 43 | .rev() 44 | .map(|l| { 45 | last_len *= l; 46 | (l, last_len) 47 | }) 48 | .collect(); 49 | // perform reshape 50 | match self { 51 | v @ (Self::Const(_) | Self::Value(_)) if lens.is_empty() => Ok(v), 52 | Self::List(l) if !lens.is_empty() => Self::reshape_impl(l, &lens), 53 | _ => Err(Error::InvalidInit), 54 | } 55 | } 56 | 57 | fn reshape_impl(inits: Vec, lens: &[(usize, usize)]) -> Result { 58 | let mut reshaped: Vec> = repeat_with(Vec::new).take(lens.len() + 1).collect(); 59 | let mut len = 0; 60 | // handle initializer elements 61 | for init in inits { 62 | // too many elements 63 | if len >= lens.last().unwrap().1 { 64 | return Err(Error::InvalidInit); 65 | } 66 | match init { 67 | Self::List(list) => { 68 | // get the next-level length list 69 | let next_lens = match reshaped.iter().position(|v| !v.is_empty()) { 70 | // not aligned 71 | Some(0) => return Err(Error::InvalidInit), 72 | Some(i) => &lens[..i], 73 | None => &lens[..lens.len() - 1], 74 | }; 75 | // reshape, and add to reshaped initializer list 76 | reshaped[next_lens.len()].push(Self::reshape_impl(list, next_lens)?); 77 | Self::carry(&mut reshaped, lens); 78 | len += next_lens.last().unwrap().1; 79 | } 80 | _ => { 81 | // just push 82 | reshaped[0].push(init); 83 | Self::carry(&mut reshaped, lens); 84 | len += 1; 85 | } 86 | } 87 | } 88 | // fill zeros 89 | while len < lens.last().unwrap().1 { 90 | reshaped[0].push(Self::Const(0)); 91 | Self::carry(&mut reshaped, lens); 92 | len += 1; 93 | } 94 | Ok(reshaped.pop().unwrap().pop().unwrap()) 95 | } 96 | 97 | fn carry(reshaped: &mut [Vec], lens: &[(usize, usize)]) { 98 | // perform carry 99 | for (i, &(len, _)) in lens.iter().enumerate() { 100 | if reshaped[i].len() == len { 101 | let init = Self::List(reshaped[i].drain(..).collect()); 102 | reshaped[i + 1].push(init); 103 | } 104 | } 105 | } 106 | 107 | /// Converts the initializer (must be reshaped first) into a constant. 108 | pub fn into_const(self, program: &mut Program, scopes: &Scopes) -> Result { 109 | match self { 110 | Self::Const(num) => Ok(if scopes.is_global() { 111 | program.new_value().integer(num) 112 | } else { 113 | cur_func!(scopes).new_value(program).integer(num) 114 | }), 115 | Self::Value(_) => Err(Error::FailedToEval), 116 | Self::List(list) => { 117 | let values = list 118 | .into_iter() 119 | .map(|i| i.into_const(program, scopes)) 120 | .collect::>()?; 121 | Ok(if scopes.is_global() { 122 | program.new_value().aggregate(values) 123 | } else { 124 | cur_func!(scopes).new_value(program).aggregate(values) 125 | }) 126 | } 127 | } 128 | } 129 | 130 | /// Converts the initializer (must be reshaped first) 131 | /// into store instructions. 132 | pub fn into_stores(self, program: &mut Program, scopes: &Scopes, ptr: IrValue) { 133 | let info = cur_func!(scopes); 134 | let store = match self { 135 | Self::Const(num) => { 136 | let value = info.new_value(program).integer(num); 137 | info.new_value(program).store(value, ptr) 138 | } 139 | Self::Value(value) => info.new_value(program).store(value, ptr), 140 | Self::List(list) => { 141 | for (i, init) in list.into_iter().enumerate() { 142 | let index = info.new_value(program).integer(i as i32); 143 | let ptr = info.new_value(program).get_elem_ptr(ptr, index); 144 | info.push_inst(program, ptr); 145 | init.into_stores(program, scopes, ptr); 146 | } 147 | return; 148 | } 149 | }; 150 | info.push_inst(program, store); 151 | } 152 | } 153 | 154 | /// An expression value. 155 | pub enum ExpValue { 156 | /// An `void`. 157 | Void, 158 | /// An integer. 159 | Int(IrValue), 160 | /// An integer pointer. 161 | IntPtr(IrValue), 162 | /// An array pointer (part of array). 163 | ArrPtr(IrValue), 164 | } 165 | 166 | impl ExpValue { 167 | /// Converts the value into a right value. 168 | pub fn into_val(self, program: &mut Program, scopes: &Scopes) -> Result { 169 | match self { 170 | Self::Void => Err(Error::UseVoidValue), 171 | Self::Int(val) => Ok(val), 172 | Self::IntPtr(ptr) => { 173 | let info = cur_func!(scopes); 174 | let load = info.new_value(program).load(ptr); 175 | info.push_inst(program, load); 176 | Ok(load) 177 | } 178 | Self::ArrPtr(ptr) => Ok(ptr), 179 | } 180 | } 181 | 182 | /// Converts the value into a integer right value. 183 | pub fn into_int(self, program: &mut Program, scopes: &Scopes) -> Result { 184 | match self { 185 | Self::ArrPtr(_) => Err(Error::NonIntCalc), 186 | _ => self.into_val(program, scopes), 187 | } 188 | } 189 | 190 | /// Converts the value into a left-value pointer. 191 | pub fn into_ptr(self) -> Result { 192 | match self { 193 | Self::IntPtr(ptr) => Ok(ptr), 194 | Self::ArrPtr(_) => Err(Error::ArrayAssign), 195 | _ => unreachable!(), 196 | } 197 | } 198 | } 199 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | mod ast; 2 | mod codegen; 3 | mod irgen; 4 | 5 | use koopa::back::KoopaGenerator; 6 | use lalrpop_util::lalrpop_mod; 7 | use std::env::args; 8 | use std::fs::read_to_string; 9 | use std::process::exit; 10 | use std::{fmt, io}; 11 | 12 | lalrpop_mod! { 13 | #[allow(clippy::all)] 14 | sysy 15 | } 16 | 17 | fn main() { 18 | if let Err(err) = try_main() { 19 | eprintln!("{}", err); 20 | exit(-1); 21 | } 22 | } 23 | 24 | fn try_main() -> Result<(), Error> { 25 | // parse command line arguments 26 | let CommandLineArgs { 27 | mode, 28 | input, 29 | output, 30 | } = CommandLineArgs::parse()?; 31 | // parse input file 32 | let input = read_to_string(input).map_err(Error::File)?; 33 | let comp_unit = sysy::CompUnitParser::new() 34 | .parse(&input) 35 | .map_err(|_| Error::Parse)?; 36 | // generate IR 37 | let program = irgen::generate_program(&comp_unit).map_err(Error::Generate)?; 38 | if matches!(mode, Mode::Koopa) { 39 | return KoopaGenerator::from_path(output) 40 | .map_err(Error::File)? 41 | .generate_on(&program) 42 | .map_err(Error::Io); 43 | } 44 | // generate RISC-V assembly 45 | codegen::generate_asm(&program, &output).map_err(Error::Io) 46 | } 47 | 48 | /// Error returned by `main` procedure. 49 | enum Error { 50 | InvalidArgs, 51 | File(io::Error), 52 | Parse, 53 | Generate(irgen::Error), 54 | Io(io::Error), 55 | } 56 | 57 | impl fmt::Display for Error { 58 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 59 | match self { 60 | Self::InvalidArgs => write!( 61 | f, 62 | r#"Usage: kira MODE INPUT -o OUTPUT 63 | 64 | Options: 65 | MODE: can be `-koopa`, `-riscv` or `-perf` 66 | INPUT: the input SysY source file 67 | OUTPUT: the output file"# 68 | ), 69 | Self::File(err) => write!(f, "invalid input SysY file: {}", err), 70 | Self::Parse => write!(f, "error occurred while parsing"), 71 | Self::Generate(err) => write!(f, "{}", err), 72 | Self::Io(err) => write!(f, "I/O error: {}", err), 73 | } 74 | } 75 | } 76 | 77 | /// Command line arguments. 78 | struct CommandLineArgs { 79 | mode: Mode, 80 | input: String, 81 | output: String, 82 | } 83 | 84 | impl CommandLineArgs { 85 | /// Parses the command line arguments, returns `Error` if error occurred. 86 | fn parse() -> Result { 87 | let mut args = args(); 88 | args.next(); 89 | match (args.next(), args.next(), args.next(), args.next()) { 90 | (Some(m), Some(input), Some(o), Some(output)) if o == "-o" => { 91 | let mode = match m.as_str() { 92 | "-koopa" => Mode::Koopa, 93 | "-riscv" => Mode::Riscv, 94 | "-perf" => Mode::Perf, 95 | _ => return Err(Error::InvalidArgs), 96 | }; 97 | Ok(Self { 98 | mode, 99 | input, 100 | output, 101 | }) 102 | } 103 | _ => Err(Error::InvalidArgs), 104 | } 105 | } 106 | } 107 | 108 | /// Compile mode. 109 | enum Mode { 110 | /// Compile SysY to Koopa IR. 111 | Koopa, 112 | /// Compile SysY to RISC-V assembly. 113 | Riscv, 114 | /// Compile SysY to optimized RISC-V assembly. 115 | Perf, 116 | } 117 | -------------------------------------------------------------------------------- /src/sysy.lalrpop: -------------------------------------------------------------------------------- 1 | use crate::ast::*; 2 | 3 | grammar; 4 | 5 | match { 6 | r"\s*" => {}, 7 | r"//[^\n\r]*[\n\r]*" => {}, 8 | r"/\*[^*]*\*+(?:[^/*][^*]*\*+)*/" => {}, 9 | _ 10 | } 11 | 12 | pub CompUnit: CompUnit = => CompUnit { <> }; 13 | 14 | GlobalItem: GlobalItem = { 15 | Decl => GlobalItem::Decl(<>), 16 | FuncDef => GlobalItem::FuncDef(<>), 17 | } 18 | 19 | Decl: Decl = { 20 | ConstDecl => Decl::Const(<>), 21 | VarDecl => Decl::Var(<>), 22 | } 23 | 24 | ConstDecl: ConstDecl = { 25 | "const" "int" )*> ";" => { 26 | defs.insert(0, def); 27 | ConstDecl { defs } 28 | }, 29 | } 30 | 31 | ConstDef: ConstDef = { 32 | "]")*> "=" => { 33 | ConstDef { <> } 34 | }, 35 | } 36 | 37 | ConstInitVal: ConstInitVal = { 38 | ConstExp => ConstInitVal::Exp(<>), 39 | "{" "}" => ConstInitVal::List(Vec::new()), 40 | "{" )*> "}" => { 41 | inits.insert(0, init); 42 | ConstInitVal::List(inits) 43 | }, 44 | } 45 | 46 | VarDecl: VarDecl = { 47 | "int" )*> ";" => { 48 | defs.insert(0, def); 49 | VarDecl { defs } 50 | }, 51 | } 52 | 53 | VarDef: VarDef = { 54 | "]")*> InitVal::List(Vec::new()), 62 | "{" )*> "}" => { 63 | inits.insert(0, init); 64 | InitVal::List(inits) 65 | }, 66 | } 67 | 68 | FuncDef: FuncDef = { 69 | ")" => { 70 | FuncDef { ty: head.0, id: head.1, params: Vec::new(), block } 71 | }, 72 | )*> ")" 73 | => { 74 | params.insert(0, param); 75 | FuncDef { ty: head.0, id: head.1, params, block } 76 | }, 77 | } 78 | 79 | FuncDefHead: (FuncType, String) = { 80 | "void" "(" => (FuncType::Void, <>), 81 | "int" "(" => (FuncType::Int, <>), 82 | } 83 | 84 | FuncFParam: FuncFParam = { 85 | "int" "]")*>)?> => { 86 | FuncFParam { <> } 87 | }, 88 | } 89 | 90 | Block: Block = "{" "}" => Block { <> }; 91 | 92 | BlockItem: BlockItem = { 93 | Decl => BlockItem::Decl(<>), 94 | Stmt => BlockItem::Stmt(<>), 95 | } 96 | 97 | Stmt: Stmt = { 98 | MatchedStmt => <>, 99 | OpenStmt => <>, 100 | } 101 | 102 | MatchedStmt: Stmt = { 103 | "=" ";" => Stmt::Assign(Assign { <> }), 104 | ";" => Stmt::ExpStmt(ExpStmt { <> }), 105 | Block => Stmt::Block(<>), 106 | "if" "(" ")" 107 | "else" => { 108 | Stmt::If(Box::new(If { cond, then, else_then: Some(else_then) })) 109 | }, 110 | "while" "(" ")" => { 111 | Stmt::While(Box::new(While { <> })) 112 | }, 113 | "break" ";" => Stmt::Break(Break), 114 | "continue" ";" => Stmt::Continue(Continue), 115 | "return" ";" => Stmt::Return(Return { <> }), 116 | } 117 | 118 | OpenStmt: Stmt = { 119 | "if" "(" ")" => { 120 | Stmt::If(Box::new(If { cond, then, else_then: None })) 121 | }, 122 | "if" "(" ")" 123 | "else" => { 124 | Stmt::If(Box::new(If { cond, then, else_then: Some(else_then) })) 125 | }, 126 | "while" "(" ")" => { 127 | Stmt::While(Box::new(While { <> })) 128 | }, 129 | } 130 | 131 | Exp: Exp = => Exp { <> }; 132 | 133 | LVal: LVal = "]")*> => LVal { <> }; 134 | 135 | PrimaryExp: PrimaryExp = { 136 | "(" ")" => PrimaryExp::Exp(Box::new(<>)), 137 | LVal => PrimaryExp::LVal(<>), 138 | Number => PrimaryExp::Number(<>), 139 | } 140 | 141 | UnaryExp: UnaryExp = { 142 | PrimaryExp => UnaryExp::Primary(<>), 143 | "(" ")" => UnaryExp::Call(FuncCall { id, args: Vec::new() }), 144 | "(" )*> ")" => { 145 | args.insert(0, arg); 146 | UnaryExp::Call(FuncCall { id, args }) 147 | }, 148 | "+" => <>, 149 | => UnaryExp::Unary(op, Box::new(exp)), 150 | } 151 | 152 | MulExp: MulExp = { 153 | UnaryExp => MulExp::Unary(<>), 154 | => { 155 | MulExp::MulUnary(Box::new(lhs), op, rhs) 156 | }, 157 | } 158 | 159 | AddExp: AddExp = { 160 | MulExp => AddExp::Mul(<>), 161 | => { 162 | AddExp::AddMul(Box::new(lhs), op, rhs) 163 | }, 164 | } 165 | 166 | RelExp: RelExp = { 167 | AddExp => RelExp::Add(<>), 168 | => { 169 | RelExp::RelAdd(Box::new(lhs), op, rhs) 170 | }, 171 | } 172 | 173 | EqExp: EqExp = { 174 | RelExp => EqExp::Rel(<>), 175 | => { 176 | EqExp::EqRel(Box::new(lhs), op, rhs) 177 | }, 178 | } 179 | 180 | LAndExp: LAndExp = { 181 | EqExp => LAndExp::Eq(<>), 182 | "&&" => { 183 | LAndExp::LAndEq(Box::new(lhs), rhs) 184 | }, 185 | } 186 | 187 | LOrExp: LOrExp = { 188 | LAndExp => LOrExp::LAnd(<>), 189 | "||" => { 190 | LOrExp::LOrLAnd(Box::new(lhs), rhs) 191 | }, 192 | } 193 | 194 | ConstExp: ConstExp = => ConstExp { <> }; 195 | 196 | Ident: String = r"[_a-zA-Z][_a-zA-Z0-9]*" => <>.to_string(); 197 | 198 | Number: i32 = { 199 | r"[1-9][0-9]*" => i32::from_str_radix(<>, 10).unwrap(), 200 | r"0[0-7]*" => i32::from_str_radix(<>, 8).unwrap(), 201 | r"0[xX][0-9a-fA-F]+" => i32::from_str_radix(&<>[2..], 16).unwrap(), 202 | } 203 | 204 | UnaryOp: UnaryOp = { 205 | "-" => UnaryOp::Neg, 206 | "!" => UnaryOp::LNot, 207 | } 208 | 209 | MulOp: MulOp = { 210 | "*" => MulOp::Mul, 211 | "/" => MulOp::Div, 212 | "%" => MulOp::Mod, 213 | } 214 | 215 | AddOp: AddOp = { 216 | "+" => AddOp::Add, 217 | "-" => AddOp::Sub, 218 | } 219 | 220 | RelOp: RelOp = { 221 | "<" => RelOp::Lt, 222 | ">" => RelOp::Gt, 223 | "<=" => RelOp::Le, 224 | ">=" => RelOp::Ge, 225 | } 226 | 227 | EqOp: EqOp = { 228 | "==" => EqOp::Eq, 229 | "!=" => EqOp::Neq, 230 | } 231 | --------------------------------------------------------------------------------