├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── dorian-ast ├── Cargo.toml └── src │ ├── backend.rs │ ├── block │ ├── builder.rs │ ├── mod.rs │ └── stmt.rs │ ├── function.rs │ ├── global.rs │ ├── lib.rs │ ├── module.rs │ ├── structure.rs │ ├── ty │ ├── convert.rs │ ├── mod.rs │ └── util.rs │ └── val │ ├── convert.rs │ ├── mod.rs │ └── util.rs ├── dorian-cranelift ├── Cargo.toml └── src │ ├── cl.rs │ ├── lib.rs │ ├── scope.rs │ └── ty.rs ├── dorian-llvm ├── Cargo.toml └── src │ ├── lib.rs │ ├── llvm.rs │ ├── scope.rs │ ├── ty.rs │ └── val.rs ├── examples ├── iterative_fib.rs └── recursive_fib.rs └── src └── lib.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea 2 | /target 3 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "allocator-api2" 7 | version = "0.2.21" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" 10 | 11 | [[package]] 12 | name = "anyhow" 13 | version = "1.0.98" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" 16 | 17 | [[package]] 18 | name = "arbitrary" 19 | version = "1.4.1" 20 | source = "registry+https://github.com/rust-lang/crates.io-index" 21 | checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" 22 | 23 | [[package]] 24 | name = "bumpalo" 25 | version = "3.18.1" 26 | source = "registry+https://github.com/rust-lang/crates.io-index" 27 | checksum = "793db76d6187cd04dff33004d8e6c9cc4e05cd330500379d2394209271b4aeee" 28 | dependencies = [ 29 | "allocator-api2", 30 | ] 31 | 32 | [[package]] 33 | name = "cc" 34 | version = "1.2.26" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | checksum = "956a5e21988b87f372569b66183b78babf23ebc2e744b733e4350a752c4dafac" 37 | dependencies = [ 38 | "shlex", 39 | ] 40 | 41 | [[package]] 42 | name = "cranelift" 43 | version = "0.120.0" 44 | source = "registry+https://github.com/rust-lang/crates.io-index" 45 | checksum = "05e20b81562751d4721684fde9fb8134d2ed33f44c38dde55ca787e5224e1a66" 46 | dependencies = [ 47 | "cranelift-codegen", 48 | "cranelift-frontend", 49 | "cranelift-module", 50 | ] 51 | 52 | [[package]] 53 | name = "cranelift-assembler-x64" 54 | version = "0.120.0" 55 | source = "registry+https://github.com/rust-lang/crates.io-index" 56 | checksum = "9ff8e35182c7372df00447cb90a04e584e032c42b9b9b6e8c50ddaaf0d7900d5" 57 | dependencies = [ 58 | "cranelift-assembler-x64-meta", 59 | ] 60 | 61 | [[package]] 62 | name = "cranelift-assembler-x64-meta" 63 | version = "0.120.0" 64 | source = "registry+https://github.com/rust-lang/crates.io-index" 65 | checksum = "14220f9c2698015c3b94dc6b84ae045c1c45509ddc406e43c6139252757fdb7a" 66 | dependencies = [ 67 | "cranelift-srcgen", 68 | ] 69 | 70 | [[package]] 71 | name = "cranelift-bforest" 72 | version = "0.120.0" 73 | source = "registry+https://github.com/rust-lang/crates.io-index" 74 | checksum = "d372ef2777ceefd75829e1390211ac240e9196bc60699218f7ea2419038288ee" 75 | dependencies = [ 76 | "cranelift-entity", 77 | ] 78 | 79 | [[package]] 80 | name = "cranelift-bitset" 81 | version = "0.120.0" 82 | source = "registry+https://github.com/rust-lang/crates.io-index" 83 | checksum = "56323783e423818fa89ce8078e90a3913d2a6e0810399bfce8ebd7ee87baa81f" 84 | 85 | [[package]] 86 | name = "cranelift-codegen" 87 | version = "0.120.0" 88 | source = "registry+https://github.com/rust-lang/crates.io-index" 89 | checksum = "74ffb780aab6186c6e9ba26519654b1ac55a09c0a866f6088a4efbbd84da68ed" 90 | dependencies = [ 91 | "bumpalo", 92 | "cranelift-assembler-x64", 93 | "cranelift-bforest", 94 | "cranelift-bitset", 95 | "cranelift-codegen-meta", 96 | "cranelift-codegen-shared", 97 | "cranelift-control", 98 | "cranelift-entity", 99 | "cranelift-isle", 100 | "gimli", 101 | "hashbrown", 102 | "log", 103 | "regalloc2", 104 | "rustc-hash", 105 | "serde", 106 | "smallvec", 107 | "target-lexicon", 108 | ] 109 | 110 | [[package]] 111 | name = "cranelift-codegen-meta" 112 | version = "0.120.0" 113 | source = "registry+https://github.com/rust-lang/crates.io-index" 114 | checksum = "c23ef13814d3b39c869650d5961128cbbecad83fbdff4e6836a03ecf6862d7ed" 115 | dependencies = [ 116 | "cranelift-assembler-x64-meta", 117 | "cranelift-codegen-shared", 118 | "cranelift-srcgen", 119 | ] 120 | 121 | [[package]] 122 | name = "cranelift-codegen-shared" 123 | version = "0.120.0" 124 | source = "registry+https://github.com/rust-lang/crates.io-index" 125 | checksum = "b9f623300657679f847803ce80811454bfff89cea4f6bf684be5c468d4a73631" 126 | 127 | [[package]] 128 | name = "cranelift-control" 129 | version = "0.120.0" 130 | source = "registry+https://github.com/rust-lang/crates.io-index" 131 | checksum = "31f4168af69989aa6b91fab46799ed4df6096f3209f4a6c8fb4358f49c60188f" 132 | dependencies = [ 133 | "arbitrary", 134 | ] 135 | 136 | [[package]] 137 | name = "cranelift-entity" 138 | version = "0.120.0" 139 | source = "registry+https://github.com/rust-lang/crates.io-index" 140 | checksum = "ca6fa9bae1c8de26d71ac2162f069447610fd91e7780cb480ee0d76ac81eabb8" 141 | dependencies = [ 142 | "cranelift-bitset", 143 | ] 144 | 145 | [[package]] 146 | name = "cranelift-frontend" 147 | version = "0.120.0" 148 | source = "registry+https://github.com/rust-lang/crates.io-index" 149 | checksum = "b8219205608aa0b0e6769b580284a7e055c7e0c323c1041cde7ca078add3e412" 150 | dependencies = [ 151 | "cranelift-codegen", 152 | "log", 153 | "smallvec", 154 | "target-lexicon", 155 | ] 156 | 157 | [[package]] 158 | name = "cranelift-isle" 159 | version = "0.120.0" 160 | source = "registry+https://github.com/rust-lang/crates.io-index" 161 | checksum = "588d0c5964f10860b04043e55aab26d7f7a206b0fd4f10c5260e8aa5773832bd" 162 | 163 | [[package]] 164 | name = "cranelift-module" 165 | version = "0.120.0" 166 | source = "registry+https://github.com/rust-lang/crates.io-index" 167 | checksum = "68a03c057d8a992e06596c871341e446af43ff9224f941e5b8adea39137a5391" 168 | dependencies = [ 169 | "anyhow", 170 | "cranelift-codegen", 171 | "cranelift-control", 172 | ] 173 | 174 | [[package]] 175 | name = "cranelift-srcgen" 176 | version = "0.120.0" 177 | source = "registry+https://github.com/rust-lang/crates.io-index" 178 | checksum = "85256fac1519a7d25a040c1d850fba67478f3f021ad5fdf738ba4425ee862dbf" 179 | 180 | [[package]] 181 | name = "dorian" 182 | version = "0.1.0" 183 | dependencies = [ 184 | "dorian", 185 | "dorian-ast", 186 | "dorian-cranelift", 187 | "dorian-llvm", 188 | "inkwell", 189 | ] 190 | 191 | [[package]] 192 | name = "dorian-ast" 193 | version = "0.1.0" 194 | 195 | [[package]] 196 | name = "dorian-cranelift" 197 | version = "0.1.0" 198 | dependencies = [ 199 | "cranelift", 200 | "dorian-ast", 201 | "target-lexicon", 202 | ] 203 | 204 | [[package]] 205 | name = "dorian-llvm" 206 | version = "0.1.0" 207 | dependencies = [ 208 | "dorian-ast", 209 | "inkwell", 210 | ] 211 | 212 | [[package]] 213 | name = "either" 214 | version = "1.15.0" 215 | source = "registry+https://github.com/rust-lang/crates.io-index" 216 | checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" 217 | 218 | [[package]] 219 | name = "equivalent" 220 | version = "1.0.2" 221 | source = "registry+https://github.com/rust-lang/crates.io-index" 222 | checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" 223 | 224 | [[package]] 225 | name = "fallible-iterator" 226 | version = "0.3.0" 227 | source = "registry+https://github.com/rust-lang/crates.io-index" 228 | checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" 229 | 230 | [[package]] 231 | name = "gimli" 232 | version = "0.31.1" 233 | source = "registry+https://github.com/rust-lang/crates.io-index" 234 | checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" 235 | dependencies = [ 236 | "fallible-iterator", 237 | "indexmap", 238 | "stable_deref_trait", 239 | ] 240 | 241 | [[package]] 242 | name = "hashbrown" 243 | version = "0.15.4" 244 | source = "registry+https://github.com/rust-lang/crates.io-index" 245 | checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" 246 | 247 | [[package]] 248 | name = "indexmap" 249 | version = "2.9.0" 250 | source = "registry+https://github.com/rust-lang/crates.io-index" 251 | checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" 252 | dependencies = [ 253 | "equivalent", 254 | "hashbrown", 255 | ] 256 | 257 | [[package]] 258 | name = "inkwell" 259 | version = "0.6.0" 260 | source = "registry+https://github.com/rust-lang/crates.io-index" 261 | checksum = "e67349bd7578d4afebbe15eaa642a80b884e8623db74b1716611b131feb1deef" 262 | dependencies = [ 263 | "either", 264 | "inkwell_internals", 265 | "libc", 266 | "llvm-sys", 267 | "once_cell", 268 | "thiserror", 269 | ] 270 | 271 | [[package]] 272 | name = "inkwell_internals" 273 | version = "0.11.0" 274 | source = "registry+https://github.com/rust-lang/crates.io-index" 275 | checksum = "f365c8de536236cfdebd0ba2130de22acefed18b1fb99c32783b3840aec5fb46" 276 | dependencies = [ 277 | "proc-macro2", 278 | "quote", 279 | "syn", 280 | ] 281 | 282 | [[package]] 283 | name = "lazy_static" 284 | version = "1.5.0" 285 | source = "registry+https://github.com/rust-lang/crates.io-index" 286 | checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" 287 | 288 | [[package]] 289 | name = "libc" 290 | version = "0.2.172" 291 | source = "registry+https://github.com/rust-lang/crates.io-index" 292 | checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" 293 | 294 | [[package]] 295 | name = "llvm-sys" 296 | version = "181.2.0" 297 | source = "registry+https://github.com/rust-lang/crates.io-index" 298 | checksum = "d320f9d2723c97d4b78f9190a61ed25cc7cfbe456668c08e6e7dd8e50ceb8500" 299 | dependencies = [ 300 | "anyhow", 301 | "cc", 302 | "lazy_static", 303 | "libc", 304 | "regex-lite", 305 | "semver", 306 | ] 307 | 308 | [[package]] 309 | name = "log" 310 | version = "0.4.27" 311 | source = "registry+https://github.com/rust-lang/crates.io-index" 312 | checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" 313 | 314 | [[package]] 315 | name = "once_cell" 316 | version = "1.21.3" 317 | source = "registry+https://github.com/rust-lang/crates.io-index" 318 | checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" 319 | 320 | [[package]] 321 | name = "proc-macro2" 322 | version = "1.0.95" 323 | source = "registry+https://github.com/rust-lang/crates.io-index" 324 | checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" 325 | dependencies = [ 326 | "unicode-ident", 327 | ] 328 | 329 | [[package]] 330 | name = "quote" 331 | version = "1.0.40" 332 | source = "registry+https://github.com/rust-lang/crates.io-index" 333 | checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" 334 | dependencies = [ 335 | "proc-macro2", 336 | ] 337 | 338 | [[package]] 339 | name = "regalloc2" 340 | version = "0.12.2" 341 | source = "registry+https://github.com/rust-lang/crates.io-index" 342 | checksum = "5216b1837de2149f8bc8e6d5f88a9326b63b8c836ed58ce4a0a29ec736a59734" 343 | dependencies = [ 344 | "allocator-api2", 345 | "bumpalo", 346 | "hashbrown", 347 | "log", 348 | "rustc-hash", 349 | "smallvec", 350 | ] 351 | 352 | [[package]] 353 | name = "regex-lite" 354 | version = "0.1.6" 355 | source = "registry+https://github.com/rust-lang/crates.io-index" 356 | checksum = "53a49587ad06b26609c52e423de037e7f57f20d53535d66e08c695f347df952a" 357 | 358 | [[package]] 359 | name = "rustc-hash" 360 | version = "2.1.1" 361 | source = "registry+https://github.com/rust-lang/crates.io-index" 362 | checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" 363 | 364 | [[package]] 365 | name = "semver" 366 | version = "1.0.26" 367 | source = "registry+https://github.com/rust-lang/crates.io-index" 368 | checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" 369 | 370 | [[package]] 371 | name = "serde" 372 | version = "1.0.219" 373 | source = "registry+https://github.com/rust-lang/crates.io-index" 374 | checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" 375 | dependencies = [ 376 | "serde_derive", 377 | ] 378 | 379 | [[package]] 380 | name = "serde_derive" 381 | version = "1.0.219" 382 | source = "registry+https://github.com/rust-lang/crates.io-index" 383 | checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" 384 | dependencies = [ 385 | "proc-macro2", 386 | "quote", 387 | "syn", 388 | ] 389 | 390 | [[package]] 391 | name = "shlex" 392 | version = "1.3.0" 393 | source = "registry+https://github.com/rust-lang/crates.io-index" 394 | checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 395 | 396 | [[package]] 397 | name = "smallvec" 398 | version = "1.15.1" 399 | source = "registry+https://github.com/rust-lang/crates.io-index" 400 | checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" 401 | 402 | [[package]] 403 | name = "stable_deref_trait" 404 | version = "1.2.0" 405 | source = "registry+https://github.com/rust-lang/crates.io-index" 406 | checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" 407 | 408 | [[package]] 409 | name = "syn" 410 | version = "2.0.101" 411 | source = "registry+https://github.com/rust-lang/crates.io-index" 412 | checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" 413 | dependencies = [ 414 | "proc-macro2", 415 | "quote", 416 | "unicode-ident", 417 | ] 418 | 419 | [[package]] 420 | name = "target-lexicon" 421 | version = "0.13.2" 422 | source = "registry+https://github.com/rust-lang/crates.io-index" 423 | checksum = "e502f78cdbb8ba4718f566c418c52bc729126ffd16baee5baa718cf25dd5a69a" 424 | 425 | [[package]] 426 | name = "thiserror" 427 | version = "1.0.69" 428 | source = "registry+https://github.com/rust-lang/crates.io-index" 429 | checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" 430 | dependencies = [ 431 | "thiserror-impl", 432 | ] 433 | 434 | [[package]] 435 | name = "thiserror-impl" 436 | version = "1.0.69" 437 | source = "registry+https://github.com/rust-lang/crates.io-index" 438 | checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" 439 | dependencies = [ 440 | "proc-macro2", 441 | "quote", 442 | "syn", 443 | ] 444 | 445 | [[package]] 446 | name = "unicode-ident" 447 | version = "1.0.18" 448 | source = "registry+https://github.com/rust-lang/crates.io-index" 449 | checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" 450 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dorian" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | [workspace] 7 | members = ["dorian-ast", "dorian-cranelift", "dorian-llvm"] 8 | 9 | [workspace.dependencies] 10 | inkwell = { version = "0.6.0", features = ["llvm18-1"] } 11 | cranelift = { version = "0.120.0", features = ["frontend", "module"] } 12 | 13 | [features] 14 | default = [] 15 | cranelift = ["dep:dorian-cranelift"] 16 | llvm = ["dep:dorian-llvm"] 17 | 18 | [dependencies] 19 | dorian-ast = { path = "dorian-ast" } 20 | dorian-cranelift = { path = "dorian-cranelift", optional = true } 21 | dorian-llvm = { path = "dorian-llvm", optional = true } 22 | 23 | [dev-dependencies] 24 | dorian = { path = ".", features = ["cranelift", "llvm"] } 25 | inkwell.workspace = true 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Peter Deverdzic 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # dorian 2 | 3 | Dorian is an intuitive high-level abstraction for declaring imperative programs that can be compiled or interpreted 4 | using [LLVM](https://llvm.org) (via [Inkwell](https://github.com/TheDan64/inkwell)) and 5 | [Cranelift](https://cranelift.dev). 6 | 7 | ## Status 8 | 9 | Dorian is in an early stage of development. The API is unstable and may change frequently. Moreover, the library is 10 | largely incomplete and unusable for anything other than [experimentation](/recursive_fib.rs). 11 | 12 | If you'd like to use Dorian as a Cargo dependency, please do not use the published `dorian` crate from Cargo. 13 | Instead, add the following to your `Cargo.toml`: 14 | 15 | ```toml 16 | dorian = { git = "https://github.com/peterhenryd/dorian", features = ["llvm", "cranelift"] } 17 | ``` 18 | 19 | Once Dorian has basic functionality and supports compilation to both LLVM and Cranelift, it will be published to Cargo. 20 | As of now, the Cranelift backend is not yet implemented, and the LLVM backend is only partially implemented. 21 | 22 | Dorian supports LLVM 18 as that is the latest version supported by Inkwell. In the future, support may be added for 23 | earlier versions of LLVM that are supported by Inkwell. 24 | 25 | ## License 26 | 27 | Dorian is licensed under the [MIT License](LICENSE). 28 | -------------------------------------------------------------------------------- /dorian-ast/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dorian-ast" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | [dependencies] 7 | -------------------------------------------------------------------------------- /dorian-ast/src/backend.rs: -------------------------------------------------------------------------------- 1 | use crate::module::Module; 2 | 3 | pub trait Backend { 4 | type CompiledModule<'ctx> 5 | where 6 | Self: 'ctx; 7 | 8 | fn compile_module<'ctx>(&'ctx mut self, ast_module: &Module) -> Self::CompiledModule<'ctx>; 9 | } 10 | -------------------------------------------------------------------------------- /dorian-ast/src/block/builder.rs: -------------------------------------------------------------------------------- 1 | use std::borrow::Cow; 2 | use crate::block::Block; 3 | use crate::block::stmt::{AssignStmt, BindStmt, IfElse, IfStmt, ReturnStmt, Stmt, WhileStmt}; 4 | use crate::val::{Value, Var}; 5 | 6 | #[derive(Debug, Clone, PartialEq)] 7 | pub struct BlockBuilder<'s> { 8 | stmts: Vec>, 9 | } 10 | 11 | impl<'s> BlockBuilder<'s> { 12 | pub fn new() -> Self { 13 | Self { stmts: vec![] } 14 | } 15 | 16 | pub fn if_then( 17 | &mut self, 18 | condition: Value<'s>, 19 | build: impl FnOnce(&mut BlockBuilder), 20 | ) -> IfStmtBuilder<'s, '_> { 21 | let mut then_block = Block::build(); 22 | build(&mut then_block); 23 | 24 | IfStmtBuilder { 25 | parent: Parent::Block(self), 26 | stmt: Some(IfStmt { 27 | condition: condition.into(), 28 | then_block: then_block.finish(), 29 | if_else: None, 30 | }), 31 | } 32 | } 33 | 34 | pub fn loop_while( 35 | &mut self, 36 | condition: Value<'s>, 37 | build: impl FnOnce(&mut BlockBuilder), 38 | ) { 39 | let mut loop_block = Block::build(); 40 | build(&mut loop_block); 41 | 42 | self.stmts.push(Stmt::While(WhileStmt { 43 | condition: condition.into(), 44 | loop_block: loop_block.finish(), 45 | })); 46 | } 47 | 48 | pub fn ret(&mut self, values: impl Into>>) { 49 | self.stmts 50 | .push(Stmt::Return(ReturnStmt { values: values.into() })); 51 | } 52 | 53 | pub fn bind(&mut self, name: impl Into>, value: Value<'s>) { 54 | let name = name.into(); 55 | self.stmts.push(Stmt::Bind(BindStmt { 56 | name: name.clone(), 57 | value: value.into(), 58 | })); 59 | } 60 | 61 | pub fn assign(&mut self, var: Var<'s>, value: Value<'s>) { 62 | self.stmts.push(Stmt::Assign(AssignStmt { var, value })); 63 | } 64 | 65 | pub fn finish(self) -> Block<'s> { 66 | Block { stmts: self.stmts } 67 | } 68 | } 69 | 70 | enum Parent<'s, 'p> { 71 | Block(&'p mut BlockBuilder<'s>), 72 | IfStmt(&'p mut IfStmtBuilder<'s, 'p>), 73 | } 74 | 75 | impl<'s> Parent<'s, '_> { 76 | fn insert(&mut self, stmt: IfStmt<'s>) { 77 | match self { 78 | Parent::Block(builder) => { 79 | builder.stmts.push(Stmt::If(stmt)); 80 | } 81 | Parent::IfStmt(builder) => { 82 | builder.stmt.as_mut().unwrap().if_else = Some(IfElse::If(Box::new(stmt))) 83 | } 84 | } 85 | } 86 | } 87 | 88 | pub struct IfStmtBuilder<'s, 'p> { 89 | parent: Parent<'s, 'p>, 90 | stmt: Option>, 91 | } 92 | 93 | impl<'s, 'p> IfStmtBuilder<'s, 'p> { 94 | pub fn else_if<'b: 'p>( 95 | &'b mut self, 96 | condition: impl Into>, 97 | then: impl FnOnce(&mut BlockBuilder), 98 | ) -> IfStmtBuilder<'s, 'b> { 99 | let mut then_block = Block::build(); 100 | then(&mut then_block); 101 | 102 | IfStmtBuilder { 103 | parent: Parent::IfStmt(self), 104 | stmt: Some(IfStmt { 105 | condition: condition.into(), 106 | then_block: then_block.finish(), 107 | if_else: None, 108 | }), 109 | } 110 | } 111 | 112 | pub fn or_else(mut self, else_block: impl FnOnce(&mut BlockBuilder)) { 113 | let mut block = BlockBuilder::new(); 114 | else_block(&mut block); 115 | 116 | self.stmt.as_mut().unwrap().if_else = Some(IfElse::Else(block.finish())); 117 | } 118 | } 119 | 120 | impl Drop for IfStmtBuilder<'_, '_> { 121 | fn drop(&mut self) { 122 | if let Some(stmt) = self.stmt.take() { 123 | self.parent.insert(stmt); 124 | } 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /dorian-ast/src/block/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::block::builder::BlockBuilder; 2 | use stmt::Stmt; 3 | 4 | pub mod builder; 5 | pub mod stmt; 6 | 7 | #[derive(Debug, Clone, PartialEq)] 8 | pub struct Block<'s> { 9 | pub stmts: Vec>, 10 | } 11 | 12 | impl<'s> Block<'s> { 13 | pub fn new() -> Self { 14 | Block { stmts: Vec::new() } 15 | } 16 | 17 | pub fn build() -> BlockBuilder<'s> { 18 | BlockBuilder::new() 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /dorian-ast/src/block/stmt.rs: -------------------------------------------------------------------------------- 1 | use std::borrow::Cow; 2 | use crate::block::Block; 3 | use crate::val::{Value, Var}; 4 | 5 | /* 6 | List of LLVM builder methods that are either implemented ([x]), partially implemented ([/]), or not implemented ([ ]), 7 | or not planned to be implemented (NP). 8 | 9 | [x] build_return 10 | NP build_aggregate_return 11 | [x] build_call 12 | NP build_direct_call 13 | [ ] build_direct_call_with_operand_bundles 14 | [ ] build_indirect_call 15 | [ ] build_indirect_call_with_operand_bundles 16 | [ ] build_call_help 17 | [ ] build_call_with_operand_bundles_help 18 | [ ] build_invoke 19 | [ ] build_direct_invoke 20 | [ ] build_indirect_invoke 21 | [ ] build_invoke_help 22 | [ ] build_landing_pad 23 | [ ] build_resume 24 | [ ] build_gep 25 | [ ] build_in_bounds_gep 26 | [ ] build_struct_gep 27 | [ ] build_ptr_diff 28 | [ ] build_phi 29 | [/] build_store 30 | [/] build_load 31 | [/] build_alloca 32 | [ ] build_array_alloca 33 | [ ] build_memcpy 34 | [ ] build_memmove 35 | [ ] build_memset 36 | [ ] build_malloc 37 | [ ] build_array_malloc 38 | [ ] build_free 39 | [x] built_int_unsigned_div 40 | [x] build_int_signed_div 41 | [ ] built_int_exact_signed_div 42 | [x] build_int_unsigned_rem 43 | [x] build_int_signed_rem 44 | [ ] build_int_s_extend 45 | [ ] build_address_space_cast 46 | [ ] build_bit_cast 47 | [ ] built_int_s_extend_or_bit_cast 48 | [ ] build_int_z_extend 49 | [ ] build_int_z_extend_or_bit_cast 50 | [ ] build_int_truncate 51 | [ ] build_int_truncate_or_bit_cast 52 | [x] build_float_rem 53 | [ ] build_float_to_unsigned_int 54 | [ ] build_float_to_signed_int 55 | [ ] build_unsigned_int_to_float 56 | [ ] build_signed_int_to_float 57 | [ ] build_float_trunc 58 | [ ] built_float_ext 59 | [ ] build_float_cast 60 | [ ] build_int_cast 61 | [ ] built_int_cast_sign_flag 62 | [x] build_float_div 63 | [x] build_int_nsw_add 64 | [x] build_int_nuw_add 65 | [x] built_float_add 66 | [x] build_xor 67 | [x] build_add 68 | [x] build_or 69 | [x] build_left_shift 70 | [x] build_right_shift 71 | [x] build_int_sub 72 | [x] build_int_nsw_sub 73 | [x] build_int_nuw_sub 74 | [x] build_float_sub 75 | [x] build_int_mul 76 | [x] build_int_nsw_mul 77 | [x] build_int_nuw_mul 78 | [x] build_float_mul 79 | [ ] build_binop 80 | [ ] build_cast 81 | [ ] build_pointer_cast 82 | [x] build_int_compare 83 | [x] build_float_compare 84 | [/] build_unconditional_branch 85 | [/] build_conditional_branch 86 | [ ] build_indirect_branch 87 | [x] build_int_neg 88 | [x] build_int_nsw_neg 89 | [x] build_int_nuw_neg 90 | [x] build_float_neg 91 | [x] build_not 92 | [ ] build_extract_value 93 | [ ] build_insert_value 94 | [ ] build_extract_element 95 | [ ] build_insert_element 96 | [ ] build_unreachable 97 | [ ] build_fence 98 | [ ] build_is_null 99 | [ ] build_is_not_null 100 | [ ] build_int_to_ptr 101 | [ ] build_ptr_to_int 102 | [ ] build_switch 103 | [ ] build_select 104 | [ ] build_global_string 105 | [ ] build_global_string_ptr 106 | [ ] build_shuffle_vector 107 | [ ] build_va_arg 108 | [ ] build_atomicrmw 109 | [ ] build_cmpxchg 110 | */ 111 | 112 | #[derive(Debug, Clone, PartialEq)] 113 | pub enum Stmt<'s> { 114 | If(IfStmt<'s>), 115 | While(WhileStmt<'s>), 116 | Return(ReturnStmt<'s>), 117 | Bind(BindStmt<'s>), 118 | Assign(AssignStmt<'s>), 119 | } 120 | 121 | #[derive(Debug, Clone, PartialEq)] 122 | pub struct IfStmt<'s> { 123 | pub condition: Value<'s>, 124 | pub then_block: Block<'s>, 125 | pub if_else: Option>, 126 | } 127 | 128 | #[derive(Debug, Clone, PartialEq)] 129 | pub enum IfElse<'s> { 130 | If(Box>), 131 | Else(Block<'s>), 132 | } 133 | 134 | #[derive(Debug, Clone, PartialEq)] 135 | pub struct WhileStmt<'s> { 136 | pub condition: Value<'s>, 137 | pub loop_block: Block<'s>, 138 | } 139 | 140 | #[derive(Debug, Clone, PartialEq)] 141 | pub struct ReturnStmt<'s> { 142 | pub values: Vec>, 143 | } 144 | 145 | #[derive(Debug, Clone, PartialEq)] 146 | pub struct BindStmt<'s> { 147 | pub name: Cow<'s, str>, 148 | pub value: Value<'s>, 149 | } 150 | 151 | #[derive(Debug, Clone, PartialEq)] 152 | pub struct AssignStmt<'s> { 153 | pub var: Var<'s>, 154 | pub value: Value<'s>, 155 | } -------------------------------------------------------------------------------- /dorian-ast/src/function.rs: -------------------------------------------------------------------------------- 1 | use std::borrow::Cow; 2 | use crate::block::Block; 3 | use crate::block::builder::BlockBuilder; 4 | use crate::ty::Type; 5 | 6 | #[derive(Debug, Clone, PartialEq)] 7 | pub struct Function<'s> { 8 | pub name: Cow<'s, str>, 9 | pub signature: Signature, 10 | pub body: Block<'s>, 11 | } 12 | 13 | impl<'s> Function<'s> { 14 | pub fn new(name: impl Into>) -> Self { 15 | Function { 16 | name: name.into(), 17 | signature: Signature { 18 | input: Vec::new(), 19 | output: Vec::new(), 20 | }, 21 | body: Block::new(), 22 | } 23 | } 24 | 25 | pub fn add_input(mut self, param: Type) -> Self { 26 | self.signature.input.push(param); 27 | self 28 | } 29 | 30 | pub fn add_output(mut self, output: Type) -> Self { 31 | self.signature.output.push(output); 32 | self 33 | } 34 | 35 | pub fn with_block(mut self, block: Block<'s>) -> Self { 36 | self.body = block; 37 | self 38 | } 39 | 40 | pub fn build_block(self, f: impl FnOnce(&mut BlockBuilder)) -> Self { 41 | let mut builder = BlockBuilder::new(); 42 | f(&mut builder); 43 | 44 | self.with_block(builder.finish()) 45 | } 46 | } 47 | 48 | #[derive(Debug, Clone, Eq, PartialEq, Hash)] 49 | pub struct Signature { 50 | pub input: Vec, 51 | pub output: Vec, 52 | } -------------------------------------------------------------------------------- /dorian-ast/src/global.rs: -------------------------------------------------------------------------------- 1 | use std::borrow::Cow; 2 | use crate::ty::Type; 3 | use crate::val::Value; 4 | 5 | #[derive(Debug, Clone, PartialEq)] 6 | pub struct Global<'s> { 7 | pub name: Cow<'s, str>, 8 | pub ty: Type, 9 | pub value: Option>, 10 | } 11 | -------------------------------------------------------------------------------- /dorian-ast/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod backend; 2 | pub mod block; 3 | pub mod function; 4 | pub mod global; 5 | pub mod module; 6 | pub mod structure; 7 | pub mod ty; 8 | pub mod val; 9 | -------------------------------------------------------------------------------- /dorian-ast/src/module.rs: -------------------------------------------------------------------------------- 1 | use std::borrow::Cow; 2 | use crate::function::Function; 3 | use crate::global::Global; 4 | use crate::structure::Struct; 5 | 6 | #[derive(Debug, Clone, PartialEq)] 7 | pub struct Module<'s> { 8 | pub name: Cow<'s, str>, 9 | pub structs: Vec>, 10 | pub globals: Vec>, 11 | pub functions: Vec>, 12 | } 13 | 14 | impl<'s> Module<'s> { 15 | pub fn new(name: impl Into>) -> Self { 16 | Module { 17 | name: name.into(), 18 | structs: Vec::new(), 19 | globals: Vec::new(), 20 | functions: Vec::new(), 21 | } 22 | } 23 | 24 | pub fn add_struct(&mut self, value: Struct<'s>) { 25 | self.structs.push(value); 26 | } 27 | 28 | pub fn add_global(&mut self, value: Global<'s>) { 29 | self.globals.push(value); 30 | } 31 | 32 | pub fn add_function(&mut self, value: Function<'s>) { 33 | self.functions.push(value); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /dorian-ast/src/structure.rs: -------------------------------------------------------------------------------- 1 | use std::borrow::Cow; 2 | use crate::ty::Type; 3 | 4 | #[derive(Debug, Clone, Eq, PartialEq, Hash)] 5 | pub struct Struct<'s> { 6 | pub name: Cow<'s, str>, 7 | pub fields: Vec, 8 | } 9 | -------------------------------------------------------------------------------- /dorian-ast/src/ty/convert.rs: -------------------------------------------------------------------------------- 1 | use crate::ty::{BoolType, Type, FloatType, IntType, NumType, PtrType, ScalarType, VectorType}; 2 | 3 | // impl From<...> for Type 4 | 5 | impl From for Type { 6 | fn from(ty: ScalarType) -> Self { 7 | Type::Scalar(ty) 8 | } 9 | } 10 | 11 | impl From for Type { 12 | fn from(ty: NumType) -> Self { 13 | Type::Scalar(ty.into()) 14 | } 15 | } 16 | 17 | impl From for Type { 18 | fn from(ty: IntType) -> Self { 19 | Type::Scalar(ty.into()) 20 | } 21 | } 22 | 23 | impl From for Type { 24 | fn from(ty: FloatType) -> Self { 25 | Type::Scalar(ty.into()) 26 | } 27 | } 28 | 29 | impl From for Type { 30 | fn from(ty: BoolType) -> Self { 31 | Type::Scalar(ty.into()) 32 | } 33 | } 34 | 35 | impl From for Type { 36 | fn from(ty: PtrType) -> Self { 37 | Type::Scalar(ty.into()) 38 | } 39 | } 40 | 41 | impl From for Type { 42 | fn from(ty: VectorType) -> Self { 43 | Type::Vector(ty) 44 | } 45 | } 46 | 47 | // impl From<...> for ScalarType 48 | 49 | impl From for ScalarType { 50 | fn from(ty: NumType) -> Self { 51 | ScalarType::Num(ty) 52 | } 53 | } 54 | 55 | impl From for ScalarType { 56 | fn from(ty: IntType) -> Self { 57 | ScalarType::Num(ty.into()) 58 | } 59 | } 60 | 61 | impl From for ScalarType { 62 | fn from(ty: FloatType) -> Self { 63 | ScalarType::Num(ty.into()) 64 | } 65 | } 66 | 67 | impl From for ScalarType { 68 | fn from(ty: BoolType) -> Self { 69 | ScalarType::Bool(ty) 70 | } 71 | } 72 | 73 | impl From for ScalarType { 74 | fn from(ty: PtrType) -> Self { 75 | ScalarType::Ptr(ty) 76 | } 77 | } 78 | 79 | // impl From<...> for NumType 80 | 81 | impl From for NumType { 82 | fn from(ty: IntType) -> Self { 83 | NumType::Int(ty) 84 | } 85 | } 86 | 87 | impl From for NumType { 88 | fn from(ty: FloatType) -> Self { 89 | NumType::Float(ty) 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /dorian-ast/src/ty/mod.rs: -------------------------------------------------------------------------------- 1 | mod convert; 2 | pub mod util; 3 | 4 | type Bool = bool; 5 | 6 | #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] 7 | pub enum Type { 8 | Scalar(ScalarType), 9 | Vector(VectorType), 10 | } 11 | 12 | impl Type { 13 | pub fn get_signage(&self) -> Option { 14 | match self { 15 | Type::Scalar(ScalarType::Num(NumType::Int(IntType { signed, .. }))) => { 16 | Some(*signed) 17 | } 18 | _ => None, 19 | } 20 | } 21 | } 22 | 23 | #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] 24 | pub enum ScalarType { 25 | Num(NumType), 26 | Bool(BoolType), 27 | Ptr(PtrType), 28 | } 29 | 30 | #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] 31 | pub enum NumType { 32 | Int(IntType), 33 | Float(FloatType), 34 | } 35 | 36 | #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] 37 | pub struct IntType { 38 | pub width: IntWidth, 39 | pub signed: bool, 40 | } 41 | 42 | impl IntType { 43 | pub const S8: Self = IntType { 44 | width: IntWidth::I8, 45 | signed: true, 46 | }; 47 | pub const S16: Self = IntType { 48 | width: IntWidth::I16, 49 | signed: true, 50 | }; 51 | pub const S32: Self = IntType { 52 | width: IntWidth::I32, 53 | signed: true, 54 | }; 55 | pub const S64: Self = IntType { 56 | width: IntWidth::I64, 57 | signed: true, 58 | }; 59 | pub const S128: Self = IntType { 60 | width: IntWidth::I128, 61 | signed: true, 62 | }; 63 | pub const U8: Self = IntType { 64 | width: IntWidth::I8, 65 | signed: false, 66 | }; 67 | pub const U16: Self = IntType { 68 | width: IntWidth::I16, 69 | signed: false, 70 | }; 71 | pub const U32: Self = IntType { 72 | width: IntWidth::I32, 73 | signed: false, 74 | }; 75 | pub const U64: Self = IntType { 76 | width: IntWidth::I64, 77 | signed: false, 78 | }; 79 | pub const U128: Self = IntType { 80 | width: IntWidth::I128, 81 | signed: false, 82 | }; 83 | } 84 | 85 | #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] 86 | pub enum IntWidth { 87 | I8, 88 | I16, 89 | I32, 90 | I64, 91 | I128, 92 | } 93 | 94 | #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] 95 | pub enum FloatType { 96 | F16, 97 | F32, 98 | F64, 99 | F128, 100 | } 101 | 102 | #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] 103 | pub struct BoolType; 104 | 105 | #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] 106 | pub struct PtrType; 107 | 108 | #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] 109 | pub struct VectorType { 110 | pub elem: ScalarType, 111 | pub len: u32, 112 | } 113 | 114 | #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] 115 | pub struct VoidType; -------------------------------------------------------------------------------- /dorian-ast/src/ty/util.rs: -------------------------------------------------------------------------------- 1 | use crate::ty::{ 2 | BoolType, FloatType, IntType, PtrType, ScalarType, VectorType, VoidType, 3 | }; 4 | 5 | // Signed integer type utility functions 6 | 7 | pub fn s8>() -> T { 8 | T::from(IntType::S8) 9 | } 10 | 11 | pub fn s16>() -> T { 12 | T::from(IntType::S16) 13 | } 14 | 15 | pub fn s32>() -> T { 16 | T::from(IntType::S32) 17 | } 18 | 19 | pub fn s64>() -> T { 20 | T::from(IntType::S64) 21 | } 22 | 23 | pub fn s128>() -> T { 24 | T::from(IntType::S128) 25 | } 26 | 27 | // Unsigned integer type utility functions 28 | 29 | pub fn u8>() -> T { 30 | T::from(IntType::U8) 31 | } 32 | 33 | pub fn u16>() -> T { 34 | T::from(IntType::U16) 35 | } 36 | 37 | pub fn u32>() -> T { 38 | T::from(IntType::U32) 39 | } 40 | 41 | pub fn u64>() -> T { 42 | T::from(IntType::U64) 43 | } 44 | 45 | pub fn u128>() -> T { 46 | T::from(IntType::U128) 47 | } 48 | 49 | // Floating-point type utility functions 50 | 51 | pub fn f16>() -> T { 52 | T::from(FloatType::F16) 53 | } 54 | 55 | pub fn f32>() -> T { 56 | T::from(FloatType::F32) 57 | } 58 | 59 | pub fn f64>() -> T { 60 | T::from(FloatType::F64) 61 | } 62 | 63 | pub fn f128>() -> T { 64 | T::from(FloatType::F128) 65 | } 66 | 67 | // Miscellaneous type utility functions 68 | 69 | pub fn bool>() -> T { 70 | T::from(BoolType) 71 | } 72 | 73 | pub fn ptr>() -> T { 74 | T::from(PtrType) 75 | } 76 | 77 | pub fn vector>(elem: impl Into, len: u32) -> T { 78 | T::from(VectorType { 79 | elem: elem.into(), 80 | len, 81 | }) 82 | } 83 | 84 | pub fn void>() -> T { 85 | T::from(VoidType) 86 | } 87 | -------------------------------------------------------------------------------- /dorian-ast/src/val/convert.rs: -------------------------------------------------------------------------------- 1 | use crate::val::{ 2 | Arg, Bin, Call, ContextValue, Expr, Float, Int, Lit, Num, SignedInt, Una, UnsignedInt, Value, 3 | Var, 4 | }; 5 | 6 | // impl From<...> for Value 7 | 8 | impl<'s> From> for Value<'s> { 9 | fn from(value: ContextValue<'s>) -> Self { 10 | Value::Context(value) 11 | } 12 | } 13 | 14 | impl<'s> From for Value<'s> { 15 | fn from(value: Arg) -> Self { 16 | Value::Context(value.into()) 17 | } 18 | } 19 | 20 | impl<'s> From> for Value<'s> { 21 | fn from(value: Var<'s>) -> Self { 22 | Value::Context(value.into()) 23 | } 24 | } 25 | 26 | impl<'s> From> for Value<'s> { 27 | fn from(value: Expr<'s>) -> Self { 28 | Value::Expr(Box::new(value)) 29 | } 30 | } 31 | 32 | impl<'s> From> for Value<'s> { 33 | fn from(value: Bin<'s>) -> Self { 34 | Value::Expr(Box::new(value.into())) 35 | } 36 | } 37 | 38 | impl<'s> From> for Value<'s> { 39 | fn from(value: Una<'s>) -> Self { 40 | Value::Expr(Box::new(value.into())) 41 | } 42 | } 43 | 44 | impl From for Value<'_> { 45 | fn from(value: Lit) -> Self { 46 | Value::Lit(value) 47 | } 48 | } 49 | 50 | impl From for Value<'_> { 51 | fn from(value: Num) -> Self { 52 | Value::Lit(value.into()) 53 | } 54 | } 55 | 56 | impl From for Value<'_> { 57 | fn from(value: Int) -> Self { 58 | Value::Lit(value.into()) 59 | } 60 | } 61 | 62 | impl From for Value<'_> { 63 | fn from(value: SignedInt) -> Self { 64 | Value::Lit(value.into()) 65 | } 66 | } 67 | 68 | impl From for Value<'_> { 69 | fn from(value: i8) -> Self { 70 | Value::Lit(value.into()) 71 | } 72 | } 73 | 74 | impl From for Value<'_> { 75 | fn from(value: i16) -> Self { 76 | Value::Lit(value.into()) 77 | } 78 | } 79 | 80 | impl From for Value<'_> { 81 | fn from(value: i32) -> Self { 82 | Value::Lit(value.into()) 83 | } 84 | } 85 | 86 | impl From for Value<'_> { 87 | fn from(value: i64) -> Self { 88 | Value::Lit(value.into()) 89 | } 90 | } 91 | 92 | impl From for Value<'_> { 93 | fn from(value: i128) -> Self { 94 | Value::Lit(value.into()) 95 | } 96 | } 97 | 98 | impl<'s> From for Value<'s> { 99 | fn from(value: UnsignedInt) -> Self { 100 | Value::Lit(value.into()) 101 | } 102 | } 103 | 104 | impl From for Value<'_> { 105 | fn from(value: u8) -> Self { 106 | Value::Lit(value.into()) 107 | } 108 | } 109 | 110 | impl From for Value<'_> { 111 | fn from(value: u16) -> Self { 112 | Value::Lit(value.into()) 113 | } 114 | } 115 | 116 | impl From for Value<'_> { 117 | fn from(value: u32) -> Self { 118 | Value::Lit(value.into()) 119 | } 120 | } 121 | 122 | impl From for Value<'_> { 123 | fn from(value: u64) -> Self { 124 | Value::Lit(value.into()) 125 | } 126 | } 127 | 128 | impl From for Value<'_> { 129 | fn from(value: u128) -> Self { 130 | Value::Lit(value.into()) 131 | } 132 | } 133 | 134 | impl<'s> From for Value<'s> { 135 | fn from(value: Float) -> Self { 136 | Value::Lit(value.into()) 137 | } 138 | } 139 | 140 | impl<'s> From for Value<'s> { 141 | fn from(value: f32) -> Self { 142 | Value::Lit(value.into()) 143 | } 144 | } 145 | 146 | impl<'s> From for Value<'s> { 147 | fn from(value: f64) -> Self { 148 | Value::Lit(value.into()) 149 | } 150 | } 151 | 152 | impl<'s> From for Value<'s> { 153 | fn from(value: bool) -> Self { 154 | Value::Lit(value.into()) 155 | } 156 | } 157 | 158 | impl<'s> From> for Value<'s> { 159 | fn from(value: Call<'s>) -> Self { 160 | Value::Call(value) 161 | } 162 | } 163 | 164 | // impl From<...> for ContextValue 165 | 166 | impl From for ContextValue<'_> { 167 | fn from(value: Arg) -> Self { 168 | ContextValue::Arg(value) 169 | } 170 | } 171 | 172 | impl<'s> From> for ContextValue<'s> { 173 | fn from(value: Var<'s>) -> Self { 174 | ContextValue::Var(value) 175 | } 176 | } 177 | 178 | // impl From<...> for Expr 179 | 180 | impl<'s> From> for Expr<'s> { 181 | fn from(value: Bin<'s>) -> Self { 182 | Expr::Bin(value) 183 | } 184 | } 185 | 186 | impl<'s> From> for Expr<'s> { 187 | fn from(value: Una<'s>) -> Self { 188 | Expr::Una(value) 189 | } 190 | } 191 | 192 | // impl From<...> for Lit 193 | 194 | impl<'s> From for Lit { 195 | fn from(value: Num) -> Self { 196 | Lit::Num(value) 197 | } 198 | } 199 | 200 | impl From for Lit { 201 | fn from(value: Int) -> Self { 202 | Lit::Num(value.into()) 203 | } 204 | } 205 | 206 | impl From for Lit { 207 | fn from(value: SignedInt) -> Self { 208 | Lit::Num(value.into()) 209 | } 210 | } 211 | 212 | impl From for Lit { 213 | fn from(value: i8) -> Self { 214 | Lit::Num(value.into()) 215 | } 216 | } 217 | 218 | impl From for Lit { 219 | fn from(value: i16) -> Self { 220 | Lit::Num(value.into()) 221 | } 222 | } 223 | 224 | impl From for Lit { 225 | fn from(value: i32) -> Self { 226 | Lit::Num(value.into()) 227 | } 228 | } 229 | 230 | impl From for Lit { 231 | fn from(value: i64) -> Self { 232 | Lit::Num(value.into()) 233 | } 234 | } 235 | 236 | impl From for Lit { 237 | fn from(value: i128) -> Self { 238 | Lit::Num(value.into()) 239 | } 240 | } 241 | 242 | impl<'s> From for Lit { 243 | fn from(value: UnsignedInt) -> Self { 244 | Lit::Num(value.into()) 245 | } 246 | } 247 | 248 | impl From for Lit { 249 | fn from(value: u8) -> Self { 250 | Lit::Num(value.into()) 251 | } 252 | } 253 | 254 | impl From for Lit { 255 | fn from(value: u16) -> Self { 256 | Lit::Num(value.into()) 257 | } 258 | } 259 | 260 | impl From for Lit { 261 | fn from(value: u32) -> Self { 262 | Lit::Num(value.into()) 263 | } 264 | } 265 | 266 | impl From for Lit { 267 | fn from(value: u64) -> Self { 268 | Lit::Num(value.into()) 269 | } 270 | } 271 | 272 | impl From for Lit { 273 | fn from(value: u128) -> Self { 274 | Lit::Num(value.into()) 275 | } 276 | } 277 | 278 | impl<'s> From for Lit { 279 | fn from(value: Float) -> Self { 280 | Lit::Num(value.into()) 281 | } 282 | } 283 | 284 | impl<'s> From for Lit { 285 | fn from(value: f32) -> Self { 286 | Lit::Num(value.into()) 287 | } 288 | } 289 | 290 | impl<'s> From for Lit { 291 | fn from(value: f64) -> Self { 292 | Lit::Num(value.into()) 293 | } 294 | } 295 | 296 | impl<'s> From for Lit { 297 | fn from(value: bool) -> Self { 298 | Lit::Bool(value) 299 | } 300 | } 301 | 302 | // impl From<...> for Num 303 | 304 | impl<'s> From for Num { 305 | fn from(value: Int) -> Self { 306 | Num::Int(value) 307 | } 308 | } 309 | 310 | impl<'s> From for Num { 311 | fn from(value: SignedInt) -> Self { 312 | Num::Int(value.into()) 313 | } 314 | } 315 | 316 | impl From for Num { 317 | fn from(value: i8) -> Self { 318 | Num::Int(value.into()) 319 | } 320 | } 321 | 322 | impl From for Num { 323 | fn from(value: i16) -> Self { 324 | Num::Int(value.into()) 325 | } 326 | } 327 | 328 | impl From for Num { 329 | fn from(value: i32) -> Self { 330 | Num::Int(value.into()) 331 | } 332 | } 333 | 334 | impl From for Num { 335 | fn from(value: i64) -> Self { 336 | Num::Int(value.into()) 337 | } 338 | } 339 | 340 | impl From for Num { 341 | fn from(value: i128) -> Self { 342 | Num::Int(value.into()) 343 | } 344 | } 345 | 346 | impl<'s> From for Num { 347 | fn from(value: UnsignedInt) -> Self { 348 | Num::Int(value.into()) 349 | } 350 | } 351 | 352 | impl From for Num { 353 | fn from(value: u8) -> Self { 354 | Num::Int(value.into()) 355 | } 356 | } 357 | 358 | impl From for Num { 359 | fn from(value: u16) -> Self { 360 | Num::Int(value.into()) 361 | } 362 | } 363 | 364 | impl From for Num { 365 | fn from(value: u32) -> Self { 366 | Num::Int(value.into()) 367 | } 368 | } 369 | 370 | impl From for Num { 371 | fn from(value: u64) -> Self { 372 | Num::Int(value.into()) 373 | } 374 | } 375 | 376 | impl From for Num { 377 | fn from(value: u128) -> Self { 378 | Num::Int(value.into()) 379 | } 380 | } 381 | 382 | impl<'s> From for Num { 383 | fn from(value: Float) -> Self { 384 | Num::Float(value) 385 | } 386 | } 387 | 388 | impl<'s> From for Num { 389 | fn from(value: f32) -> Self { 390 | Num::Float(value.into()) 391 | } 392 | } 393 | 394 | impl<'s> From for Num { 395 | fn from(value: f64) -> Self { 396 | Num::Float(value.into()) 397 | } 398 | } 399 | 400 | // impl From<...> for Int 401 | 402 | impl<'s> From for Int { 403 | fn from(value: SignedInt) -> Self { 404 | Int::Signed(value) 405 | } 406 | } 407 | 408 | impl From for Int { 409 | fn from(value: i8) -> Self { 410 | Int::Signed(value.into()) 411 | } 412 | } 413 | 414 | impl From for Int { 415 | fn from(value: i16) -> Self { 416 | Int::Signed(value.into()) 417 | } 418 | } 419 | 420 | impl From for Int { 421 | fn from(value: i32) -> Self { 422 | Int::Signed(value.into()) 423 | } 424 | } 425 | 426 | impl From for Int { 427 | fn from(value: i64) -> Self { 428 | Int::Signed(value.into()) 429 | } 430 | } 431 | 432 | impl From for Int { 433 | fn from(value: i128) -> Self { 434 | Int::Signed(value.into()) 435 | } 436 | } 437 | 438 | impl<'s> From for Int { 439 | fn from(value: UnsignedInt) -> Self { 440 | Int::Unsigned(value) 441 | } 442 | } 443 | 444 | impl From for Int { 445 | fn from(value: u8) -> Self { 446 | Int::Unsigned(value.into()) 447 | } 448 | } 449 | 450 | impl From for Int { 451 | fn from(value: u16) -> Self { 452 | Int::Unsigned(value.into()) 453 | } 454 | } 455 | 456 | impl From for Int { 457 | fn from(value: u32) -> Self { 458 | Int::Unsigned(value.into()) 459 | } 460 | } 461 | 462 | impl From for Int { 463 | fn from(value: u64) -> Self { 464 | Int::Unsigned(value.into()) 465 | } 466 | } 467 | 468 | impl From for Int { 469 | fn from(value: u128) -> Self { 470 | Int::Unsigned(value.into()) 471 | } 472 | } 473 | 474 | // impl From<...> for Float 475 | 476 | impl<'s> From for Float { 477 | fn from(value: f32) -> Self { 478 | Float::F32(value) 479 | } 480 | } 481 | 482 | impl<'s> From for Float { 483 | fn from(value: f64) -> Self { 484 | Float::F64(value) 485 | } 486 | } 487 | 488 | // impl From<...> for SignedInt 489 | 490 | impl From for SignedInt { 491 | fn from(value: i8) -> Self { 492 | SignedInt::B8(value) 493 | } 494 | } 495 | 496 | impl From for SignedInt { 497 | fn from(value: i16) -> Self { 498 | SignedInt::B16(value) 499 | } 500 | } 501 | 502 | impl From for SignedInt { 503 | fn from(value: i32) -> Self { 504 | SignedInt::B32(value) 505 | } 506 | } 507 | 508 | impl From for SignedInt { 509 | fn from(value: i64) -> Self { 510 | SignedInt::B64(value) 511 | } 512 | } 513 | 514 | impl From for SignedInt { 515 | fn from(value: i128) -> Self { 516 | SignedInt::B128(value) 517 | } 518 | } 519 | 520 | // impl From<...> for UnsignedInt 521 | 522 | impl From for UnsignedInt { 523 | fn from(value: u8) -> Self { 524 | UnsignedInt::U8(value) 525 | } 526 | } 527 | 528 | impl From for UnsignedInt { 529 | fn from(value: u16) -> Self { 530 | UnsignedInt::U16(value) 531 | } 532 | } 533 | 534 | impl From for UnsignedInt { 535 | fn from(value: u32) -> Self { 536 | UnsignedInt::U32(value) 537 | } 538 | } 539 | 540 | impl From for UnsignedInt { 541 | fn from(value: u64) -> Self { 542 | UnsignedInt::U64(value) 543 | } 544 | } 545 | 546 | impl From for UnsignedInt { 547 | fn from(value: u128) -> Self { 548 | UnsignedInt::U128(value) 549 | } 550 | } 551 | -------------------------------------------------------------------------------- /dorian-ast/src/val/mod.rs: -------------------------------------------------------------------------------- 1 | mod convert; 2 | pub mod util; 3 | 4 | use std::borrow::Cow; 5 | 6 | #[derive(Debug, Clone, PartialEq)] 7 | pub enum Value<'s> { 8 | Context(ContextValue<'s>), 9 | Expr(Box>), 10 | Lit(Lit), 11 | Call(Call<'s>), 12 | } 13 | 14 | #[derive(Debug, Clone, PartialEq)] 15 | pub enum ContextValue<'s> { 16 | Arg(Arg), 17 | Var(Var<'s>), 18 | } 19 | 20 | #[derive(Debug, Clone, Eq, PartialEq, Hash)] 21 | pub struct Arg { 22 | pub param_index: u32, 23 | } 24 | 25 | #[derive(Debug, Clone, PartialEq)] 26 | pub struct Var<'s> { 27 | pub name: Cow<'s, str>, 28 | } 29 | 30 | #[derive(Debug, Clone, PartialEq)] 31 | pub enum Expr<'s> { 32 | Bin(Bin<'s>), 33 | Una(Una<'s>), 34 | } 35 | 36 | #[derive(Debug, Clone, PartialEq)] 37 | pub struct Bin<'s> { 38 | pub lhs: Value<'s>, 39 | pub rhs: Value<'s>, 40 | pub op: BinOp, 41 | pub no_wrap: bool, 42 | } 43 | 44 | #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] 45 | pub enum BinOp { 46 | // Arithmetic operators 47 | Add, 48 | Sub, 49 | Mul, 50 | Div, 51 | Rem, 52 | // Short-circuit logical operators 53 | And, 54 | Or, 55 | // Bitwise operators 56 | BitAnd, 57 | BitOr, 58 | BitXor, 59 | Shl, 60 | Shr, 61 | // Comparison operators 62 | Eq, 63 | Ne, 64 | Lt, 65 | Gt, 66 | Le, 67 | Ge, 68 | } 69 | 70 | #[derive(Debug, Clone, PartialEq)] 71 | pub struct Una<'s> { 72 | pub operand: Value<'s>, 73 | pub op: UnaOp, 74 | pub no_wrap: bool, 75 | } 76 | 77 | #[derive(Debug, Copy, Clone, PartialEq)] 78 | pub enum UnaOp { 79 | Neg, 80 | Not, 81 | } 82 | 83 | #[derive(Debug, Copy, Clone, PartialEq)] 84 | pub enum Lit { 85 | Num(Num), 86 | Bool(bool), 87 | } 88 | 89 | #[derive(Debug, Copy, Clone, PartialEq)] 90 | pub enum Num { 91 | Int(Int), 92 | Float(Float), 93 | } 94 | 95 | #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] 96 | pub enum Int { 97 | Signed(SignedInt), 98 | Unsigned(UnsignedInt), 99 | } 100 | 101 | #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] 102 | pub enum SignedInt { 103 | B8(i8), 104 | B16(i16), 105 | B32(i32), 106 | B64(i64), 107 | B128(i128), 108 | } 109 | 110 | #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] 111 | pub enum UnsignedInt { 112 | U8(u8), 113 | U16(u16), 114 | U32(u32), 115 | U64(u64), 116 | U128(u128), 117 | } 118 | 119 | impl UnsignedInt { 120 | pub fn cast_signed(self) -> SignedInt { 121 | match self { 122 | UnsignedInt::U8(x) => SignedInt::B8(x as i8), 123 | UnsignedInt::U16(x) => SignedInt::B16(x as i16), 124 | UnsignedInt::U32(x) => SignedInt::B32(x as i32), 125 | UnsignedInt::U64(x) => SignedInt::B64(x as i64), 126 | UnsignedInt::U128(x) => SignedInt::B128(x as i128), 127 | } 128 | } 129 | } 130 | 131 | #[derive(Debug, Copy, Clone, PartialEq)] 132 | pub enum Float { 133 | F32(f32), 134 | F64(f64), 135 | } 136 | 137 | #[derive(Debug, Clone, PartialEq)] 138 | pub struct Call<'s> { 139 | pub function_name: Cow<'s, str>, 140 | pub args: Vec>, 141 | } 142 | -------------------------------------------------------------------------------- /dorian-ast/src/val/util.rs: -------------------------------------------------------------------------------- 1 | use std::borrow::Cow; 2 | use crate::val::{Arg, Bin, BinOp, Call, Lit, Una, UnaOp, Value, Var}; 3 | 4 | macro_rules! bin_op { 5 | ($($function:ident => $variant:ident),+ $(,)?) => { 6 | $( 7 | pub fn $function<'s, T: From>>(lhs: Value<'s>, rhs: Value<'s>) -> T { 8 | T::from(Bin { 9 | lhs, 10 | rhs, 11 | op: BinOp::$variant, 12 | no_wrap: false, 13 | }) 14 | } 15 | )+ 16 | }; 17 | } 18 | 19 | bin_op! { 20 | add => Add, 21 | sub => Sub, 22 | mul => Mul, 23 | div => Div, 24 | rem => Rem, 25 | and => And, 26 | or => Or, 27 | bit_and => BitAnd, 28 | bit_or => BitOr, 29 | bit_xor => BitXor, 30 | shl => Shl, 31 | shr => Shr, 32 | eq => Eq, 33 | ne => Ne, 34 | lt => Lt, 35 | gt => Gt, 36 | le => Le, 37 | ge => Ge, 38 | } 39 | 40 | pub fn neg<'s, T: From>>(value: Value<'s>) -> T { 41 | T::from(Una { 42 | operand: value, 43 | op: UnaOp::Neg, 44 | no_wrap: false, 45 | }) 46 | } 47 | 48 | pub fn not<'s, T: From>>(value: Value<'s>) -> T { 49 | T::from(Una { 50 | operand: value, 51 | op: UnaOp::Not, 52 | no_wrap: false, 53 | }) 54 | } 55 | 56 | pub fn arg>(param_index: u32) -> T { 57 | T::from(Arg { param_index }) 58 | } 59 | 60 | pub fn var<'s, T: From>>(name: impl Into>) -> T { 61 | T::from(Var { 62 | name: name.into(), 63 | }) 64 | } 65 | 66 | pub fn lit, U: From>(value: T) -> U { 67 | U::from(value.into()) 68 | } 69 | 70 | pub fn call<'s, T: From>>(function_name: impl Into>, args: impl Into>>) -> T { 71 | T::from(Call { 72 | function_name: function_name.into(), 73 | args: args.into(), 74 | }) 75 | } 76 | -------------------------------------------------------------------------------- /dorian-cranelift/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dorian-cranelift" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | [dependencies] 7 | cranelift.workspace = true 8 | dorian-ast = { path = "../dorian-ast" } 9 | target-lexicon = "0.13.2" 10 | -------------------------------------------------------------------------------- /dorian-cranelift/src/cl.rs: -------------------------------------------------------------------------------- 1 | pub use cranelift::{ 2 | codegen::{ 3 | ir::{ 4 | types::*, 5 | AbiParam, 6 | Value as Scalar, 7 | Inst, 8 | Function, 9 | FuncRef, 10 | InstBuilder, 11 | UserExternalName, 12 | ExtFuncData, 13 | ExternalName, 14 | Signature, 15 | UserFuncName, 16 | condcodes::{ 17 | FloatCC as FloatCmpOp, 18 | IntCC as IntCmpOp, 19 | }, 20 | }, 21 | isa::{ 22 | CallConv, 23 | } 24 | }, 25 | frontend::{ 26 | FunctionBuilder, 27 | FunctionBuilderContext, 28 | }, 29 | module::{ 30 | ModuleDeclarations as Module, 31 | Linkage, 32 | FuncId, 33 | FuncOrDataId, 34 | }, 35 | }; 36 | 37 | pub struct Value { 38 | pub raw: ValueItem, 39 | pub signage: Option, 40 | } 41 | 42 | pub enum ValueItem { 43 | Scalar(Scalar), 44 | Variable(Inst) 45 | } 46 | 47 | impl ValueItem { 48 | pub(crate) fn to_scalar(&self) -> Option { 49 | match self { 50 | Self::Scalar(scalar) => Some(*scalar), 51 | _ => None, 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /dorian-cranelift/src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate dorian_ast as ast; 2 | mod ty; 3 | mod cl; 4 | mod scope; 5 | 6 | use ast::backend::Backend; 7 | use ast::function::Function; 8 | use ast::global::Global; 9 | use ast::module::Module; 10 | use ast::structure::Struct; 11 | use target_lexicon::Triple; 12 | 13 | pub struct Cranelift { 14 | module: cl::Module, 15 | triple: Triple, 16 | context: cl::FunctionBuilderContext, 17 | function: Option, 18 | namespace: u32, 19 | } 20 | 21 | impl Cranelift { 22 | pub fn new() -> Self { 23 | Self { 24 | module: cl::Module::default(), 25 | triple: Triple::host(), 26 | context: cl::FunctionBuilderContext::new(), 27 | function: None, 28 | namespace: 0, 29 | } 30 | } 31 | 32 | fn compile_struct<'ctx>(&'ctx self, _: &Struct) { 33 | todo!("Structs are not implemented in the Cranelift backend yet"); 34 | } 35 | 36 | fn compile_global<'ctx>(&'ctx self, _: &Global) { 37 | todo!("Globals are not implemented in the Cranelift backend yet"); 38 | } 39 | 40 | fn compile_function<'ctx>(&'ctx mut self, ast_function: &Function) { 41 | let signature = self.compile_signature(&ast_function.signature); 42 | let (func_id, _) = self.module.declare_function(&ast_function.name, cl::Linkage::Export, &signature).unwrap(); 43 | 44 | let mut scope = self.create_scope(func_id, signature); 45 | scope.compile_body(&ast_function.body); 46 | scope.finish(); 47 | } 48 | } 49 | 50 | impl Backend for Cranelift { 51 | type CompiledModule<'ctx> = (); 52 | 53 | fn compile_module<'ctx>(&'ctx mut self, ast_module: &Module) -> Self::CompiledModule<'ctx> { 54 | for ast_struct in &ast_module.structs { 55 | self.compile_struct(ast_struct); 56 | } 57 | 58 | for ast_global in &ast_module.globals { 59 | self.compile_global(ast_global); 60 | } 61 | 62 | for ast_function in &ast_module.functions { 63 | self.compile_function(ast_function); 64 | } 65 | 66 | self.namespace += 1; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /dorian-cranelift/src/scope.rs: -------------------------------------------------------------------------------- 1 | use crate::cl::{FuncOrDataId, InstBuilder}; 2 | use crate::{cl, Cranelift}; 3 | use ast::block::stmt::Stmt; 4 | use ast::block::Block; 5 | use ast::val::{Bin, BinOp, Call, ContextValue, Expr, Float, Int, Lit, Num, SignedInt, Una, UnaOp, Value}; 6 | use std::collections::HashMap; 7 | 8 | pub(crate) struct Scope<'ctx> { 9 | namespace: u32, 10 | module: &'ctx cl::Module, 11 | builder: cl::FunctionBuilder<'ctx>, 12 | imported_functions: HashMap, 13 | } 14 | 15 | impl Scope<'_> { 16 | pub(crate) fn compile_body(&mut self, body: &Block) { 17 | let block = self.builder.create_block(); 18 | self.builder.switch_to_block(block); 19 | self.builder.append_block_params_for_function_params(block); 20 | 21 | for stmt in &body.stmts { 22 | self.compile_stmt(stmt); 23 | } 24 | } 25 | 26 | fn compile_stmt(&mut self, stmt: &Stmt) { 27 | match stmt { 28 | Stmt::If(_) => todo!("If statements are not yet implemented"), 29 | Stmt::While(_) => todo!("While statements are not yet implemented"), 30 | Stmt::Return(x) => { 31 | let values = x.values.iter() 32 | .map(|x| self.compile_value(x).raw.to_scalar().unwrap()) 33 | .collect::>(); 34 | self.builder.ins().return_(&values); 35 | } 36 | Stmt::Bind(_) => todo!("Bind statements are not yet implemented"), 37 | Stmt::Assign(_) => todo!("Assign statements are not yet implemented"), 38 | } 39 | } 40 | 41 | fn compile_value(&mut self, value: &Value) -> cl::Value { 42 | match value { 43 | Value::Context(x) => self.compile_context_value(x), 44 | Value::Expr(x) => self.compile_expr(x), 45 | Value::Lit(x) => self.compile_lit(x), 46 | Value::Call(x) => self.compile_call(x), 47 | } 48 | } 49 | 50 | fn compile_context_value(&mut self, value: &ContextValue) -> cl::Value { 51 | todo!("Context values are not yet implemented: {:?}", value); 52 | } 53 | 54 | fn compile_expr(&mut self, value: &Expr) -> cl::Value { 55 | match value { 56 | Expr::Bin(x) => self.compile_bin(x), 57 | Expr::Una(x) => self.compile_una(x), 58 | } 59 | } 60 | 61 | fn compile_bin(&mut self, value: &Bin) -> cl::Value { 62 | let (lhs, rhs) = (self.compile_value(&value.lhs), self.compile_value(&value.rhs)); 63 | let signed = lhs.signage.unwrap_or(false); 64 | let (lhs, rhs) = (lhs.raw.to_scalar().unwrap(), rhs.raw.to_scalar().unwrap()); 65 | 66 | let ty = self.builder.func.dfg.value_type(lhs); 67 | if ty.is_int() { 68 | self.compile_int_bin(value.op, signed, lhs, rhs) 69 | } else if ty.is_float() { 70 | self.compile_float_bin(value.op, lhs, rhs) 71 | } else { 72 | panic!("Type does not support binary operations: {:?}", ty); 73 | } 74 | } 75 | 76 | fn compile_int_bin( 77 | &mut self, 78 | op: BinOp, 79 | signed: bool, 80 | lhs: cl::Scalar, 81 | rhs: cl::Scalar, 82 | ) -> cl::Value { 83 | let ty = self.builder.func.dfg.value_type(rhs); 84 | if !ty.is_int() { 85 | panic!( 86 | "Right-hand value type of binary operation does not match left-hand type which is an integer" 87 | ); 88 | } 89 | 90 | let scalar = match op { 91 | BinOp::Add => self.builder.ins().iadd(lhs, rhs), 92 | BinOp::Sub => self.builder.ins().isub(lhs, rhs), 93 | BinOp::Mul => self.builder.ins().imul(lhs, rhs), 94 | BinOp::Div if signed => self.builder.ins().sdiv(lhs, rhs), 95 | BinOp::Div => self.builder.ins().udiv(lhs, rhs), 96 | BinOp::Rem if signed => self.builder.ins().srem(lhs, rhs), 97 | BinOp::Rem => self.builder.ins().urem(lhs, rhs), 98 | BinOp::And | BinOp::BitAnd => self.builder.ins().band(lhs, rhs), 99 | BinOp::Or | BinOp::BitOr => self.builder.ins().bor(lhs, rhs), 100 | BinOp::BitXor => self.builder.ins().bxor(lhs, rhs), 101 | BinOp::Shl => self.builder.ins().ishl(lhs, rhs), 102 | BinOp::Shr if signed => self.builder.ins().sshr(lhs, rhs), 103 | BinOp::Shr => self.builder.ins().ushr(lhs, rhs), 104 | BinOp::Eq => self.builder.ins().icmp(cl::IntCmpOp::Equal, lhs, rhs), 105 | BinOp::Ne => self.builder.ins().icmp(cl::IntCmpOp::NotEqual, lhs, rhs), 106 | BinOp::Lt if signed => self.builder.ins().icmp(cl::IntCmpOp::SignedLessThan, lhs, rhs), 107 | BinOp::Lt => self.builder.ins().icmp(cl::IntCmpOp::UnsignedLessThan, lhs, rhs), 108 | BinOp::Gt if signed => self.builder.ins().icmp(cl::IntCmpOp::SignedGreaterThan, lhs, rhs), 109 | BinOp::Gt => self.builder.ins().icmp(cl::IntCmpOp::UnsignedGreaterThan, lhs, rhs), 110 | BinOp::Le if signed => self.builder.ins().icmp(cl::IntCmpOp::SignedLessThanOrEqual, lhs, rhs), 111 | BinOp::Le => self.builder.ins().icmp(cl::IntCmpOp::UnsignedLessThanOrEqual, lhs, rhs), 112 | BinOp::Ge if signed => self.builder.ins().icmp(cl::IntCmpOp::SignedGreaterThanOrEqual, lhs, rhs), 113 | BinOp::Ge => self.builder.ins().icmp(cl::IntCmpOp::UnsignedGreaterThanOrEqual, lhs, rhs), 114 | }; 115 | 116 | cl::Value { 117 | raw: cl::ValueItem::Scalar(scalar), 118 | signage: Some(signed), 119 | } 120 | } 121 | 122 | fn compile_float_bin( 123 | &mut self, 124 | op: BinOp, 125 | lhs: cl::Scalar, 126 | rhs: cl::Scalar, 127 | ) -> cl::Value { 128 | let ty = self.builder.func.dfg.value_type(lhs); 129 | if !ty.is_float() { 130 | panic!( 131 | "Right-hand value type of binary operation does not match left-hand type which is a float" 132 | ); 133 | } 134 | 135 | let scalar = match op { 136 | BinOp::Add => self.builder.ins().fadd(lhs, rhs), 137 | BinOp::Sub => self.builder.ins().fsub(lhs, rhs), 138 | BinOp::Mul => self.builder.ins().fmul(lhs, rhs), 139 | BinOp::Div => self.builder.ins().fdiv(lhs, rhs), 140 | BinOp::Eq => self.builder.ins().fcmp(cl::FloatCmpOp::Equal, lhs, rhs), 141 | BinOp::Ne => self.builder.ins().fcmp(cl::FloatCmpOp::NotEqual, lhs, rhs), 142 | BinOp::Lt => self.builder.ins().fcmp(cl::FloatCmpOp::LessThan, lhs, rhs), 143 | BinOp::Gt => self.builder.ins().fcmp(cl::FloatCmpOp::GreaterThan, lhs, rhs), 144 | BinOp::Le => self.builder.ins().fcmp(cl::FloatCmpOp::LessThanOrEqual, lhs, rhs), 145 | BinOp::Ge => self.builder.ins().fcmp(cl::FloatCmpOp::GreaterThanOrEqual, lhs, rhs), 146 | _ => panic!("Unsupported float operation: {:?}", op), 147 | }; 148 | 149 | cl::Value { 150 | raw: cl::ValueItem::Scalar(scalar), 151 | signage: None, 152 | } 153 | } 154 | 155 | fn compile_una(&mut self, value: &Una) -> cl::Value { 156 | let operand = self.compile_value(&value.operand); 157 | let signed = operand.signage.unwrap_or(false); 158 | 159 | let ty = self.builder.func.dfg.value_type(operand.raw.to_scalar().unwrap()); 160 | if ty.is_int() { 161 | self.compile_int_una(value.op, signed, operand.raw.to_scalar().unwrap()) 162 | } else if ty.is_float() { 163 | self.compile_float_una(value.op, operand.raw.to_scalar().unwrap()) 164 | } else { 165 | panic!("Value does not support unary operations: {:?}", ty); 166 | } 167 | } 168 | 169 | fn compile_int_una( 170 | &mut self, 171 | op: UnaOp, 172 | signed: bool, 173 | operand: cl::Scalar, 174 | ) -> cl::Value { 175 | let raw = match op { 176 | UnaOp::Neg => self.builder.ins().ineg(operand), 177 | UnaOp::Not => self.builder.ins().bnot(operand), 178 | }; 179 | 180 | cl::Value { 181 | raw: cl::ValueItem::Scalar(raw), 182 | signage: Some(signed), 183 | } 184 | } 185 | 186 | fn compile_float_una<'ctx>( 187 | &mut self, 188 | op: UnaOp, 189 | operand: cl::Scalar, 190 | ) -> cl::Value { 191 | match op { 192 | UnaOp::Neg => cl::Value { 193 | raw: cl::ValueItem::Scalar(self.builder.ins().fneg(operand)), 194 | signage: None, 195 | }, 196 | UnaOp::Not => panic!("Not operation is not supported for float values"), 197 | } 198 | } 199 | 200 | fn compile_lit(&mut self, value: &Lit) -> cl::Value { 201 | match value { 202 | Lit::Num(x) => match x { 203 | Num::Int(x) => { 204 | let (integer, signed) = match *x { 205 | Int::Signed(x) => (x, true), 206 | Int::Unsigned(x) => (x.cast_signed(), false), 207 | }; 208 | 209 | let raw = match integer { 210 | SignedInt::B8(x) => self.builder.ins().iconst(cl::I8, x as i64), 211 | SignedInt::B16(x) => self.builder.ins().iconst(cl::I16, x as i64), 212 | SignedInt::B32(x) => self.builder.ins().iconst(cl::I32, x as i64), 213 | SignedInt::B64(x) => self.builder.ins().iconst(cl::I64, x), 214 | SignedInt::B128(x) => { 215 | let (lo, hi) = (x as i64, (x >> 64) as i64); 216 | let lo = self.builder.ins().iconst(cl::I64, lo); 217 | let hi = self.builder.ins().iconst(cl::I64, hi); 218 | self.builder.ins().iconcat(lo, hi) 219 | }, 220 | }; 221 | 222 | cl::Value { 223 | raw: cl::ValueItem::Scalar(raw), 224 | signage: Some(signed), 225 | } 226 | } 227 | Num::Float(x) => cl::Value { 228 | raw: cl::ValueItem::Scalar(match x { 229 | &Float::F32(x) => self.builder.ins().f32const(x), 230 | &Float::F64(x) => self.builder.ins().f64const(x), 231 | }), 232 | signage: None, 233 | } 234 | } 235 | &Lit::Bool(x) => cl::Value { 236 | raw: cl::ValueItem::Scalar(self.builder.ins().iconst(cl::I8, x as i64)), 237 | signage: Some(false), 238 | }, 239 | } 240 | } 241 | 242 | fn compile_call(&mut self, value: &Call) -> cl::Value { 243 | let func_ref = self.get_func_ref(&value.function_name).unwrap(); 244 | 245 | let mut values = vec![]; 246 | for arg in &value.args { 247 | let value = self.compile_value(arg); 248 | self.flatten_value(value, &mut values); 249 | } 250 | 251 | cl::Value { 252 | raw: cl::ValueItem::Variable(self.builder.ins().call(func_ref, &values)), 253 | // TODO: look up function signature to determine signage 254 | signage: None, 255 | } 256 | } 257 | 258 | fn get_func_ref(&mut self, name: &str) -> Option { 259 | let func_id = self.get_func_id(name)?; 260 | if let Some(func_ref) = self.imported_functions.get(&func_id) { 261 | return Some(*func_ref); 262 | } 263 | 264 | let name = cl::UserExternalName::new(self.namespace, func_id.as_u32()); 265 | let name_ref = self.builder.func.declare_imported_user_function(name); 266 | 267 | let signature = &self.module.get_function_decl(func_id).signature; 268 | let sig_ref = self.builder.func.import_signature(signature.clone()); 269 | 270 | let data = cl::ExtFuncData { 271 | name: cl::ExternalName::User(name_ref), 272 | signature: sig_ref, 273 | colocated: false, 274 | }; 275 | 276 | let func_ref = self.builder.func.import_function(data); 277 | self.imported_functions.insert(func_id, func_ref); 278 | 279 | Some(func_ref) 280 | } 281 | 282 | fn get_func_id(&self, name: &str) -> Option { 283 | match self.module.get_name(name)? { 284 | FuncOrDataId::Func(x) => Some(x), 285 | FuncOrDataId::Data(_) => None, 286 | } 287 | } 288 | 289 | pub fn finish(self) { 290 | self.builder.finalize(); 291 | } 292 | 293 | fn flatten_value(&self, value: cl::Value, values: &mut Vec) { 294 | match value.raw { 295 | cl::ValueItem::Scalar(x) => { 296 | values.push(x); 297 | } 298 | cl::ValueItem::Variable(x) => { 299 | for value in self.builder.inst_results(x) { 300 | values.push(*value); 301 | } 302 | } 303 | } 304 | } 305 | } 306 | 307 | impl Cranelift { 308 | pub(crate) fn create_scope(&mut self, func_id: cl::FuncId, signature: cl::Signature) -> Scope { 309 | let name = cl::UserFuncName::user(self.namespace, func_id.as_u32()); 310 | self.function = Some(cl::Function::with_name_signature(name, signature)); 311 | 312 | Scope { 313 | module: &self.module, 314 | builder: cl::FunctionBuilder::new(self.function.as_mut().unwrap(), &mut self.context), 315 | namespace: self.namespace, 316 | imported_functions: HashMap::new(), 317 | } 318 | } 319 | } -------------------------------------------------------------------------------- /dorian-cranelift/src/ty.rs: -------------------------------------------------------------------------------- 1 | use ast::function::Signature; 2 | use crate::{cl, Cranelift}; 3 | use ast::ty::{Type, FloatType, IntWidth, NumType, ScalarType}; 4 | 5 | impl Cranelift { 6 | pub(crate) fn compile_type(&self, ty: &Type) -> cl::Type { 7 | match ty { 8 | Type::Scalar(x) => self.compile_scalar_type(x), 9 | Type::Vector(x) => self.compile_scalar_type(&x.elem).by(x.len).unwrap(), 10 | } 11 | } 12 | 13 | fn compile_scalar_type(&self, ty: &ScalarType) -> cl::Type { 14 | match ty { 15 | ScalarType::Num(x) => self.compile_num_type(x), 16 | ScalarType::Bool(_) => cl::I8, 17 | ScalarType::Ptr(_) => cl::Type::triple_pointer_type(&self.triple), 18 | } 19 | } 20 | 21 | fn compile_num_type(&self, num: &NumType) -> cl::Type { 22 | match num { 23 | NumType::Int(x) => match x.width { 24 | IntWidth::I8 => cl::I8, 25 | IntWidth::I16 => cl::I16, 26 | IntWidth::I32 => cl::I32, 27 | IntWidth::I64 => cl::I64, 28 | IntWidth::I128 => cl::I128, 29 | } 30 | NumType::Float(x) => match x { 31 | FloatType::F16 => cl::F16, 32 | FloatType::F32 => cl::F32, 33 | FloatType::F64 => cl::F64, 34 | FloatType::F128 => cl::F128, 35 | } 36 | } 37 | } 38 | 39 | pub(crate) fn compile_signature(&self, signature: &Signature) -> cl::Signature { 40 | let params = signature.input.iter() 41 | .map(|x| cl::AbiParam::new(self.compile_type(x))) 42 | .collect(); 43 | let returns = signature.output.iter() 44 | .map(|x| cl::AbiParam::new(self.compile_type(x))) 45 | .collect(); 46 | 47 | cl::Signature { 48 | params, 49 | returns, 50 | call_conv: cl::CallConv::Fast, 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /dorian-llvm/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dorian-llvm" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | [dependencies] 7 | dorian-ast = { path = "../dorian-ast" } 8 | inkwell.workspace = true 9 | -------------------------------------------------------------------------------- /dorian-llvm/src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate dorian_ast as ast; 2 | 3 | use ast::backend::Backend; 4 | use ast::global::Global; 5 | use ast::module::Module; 6 | use ast::structure::Struct; 7 | use crate::scope::Scope; 8 | 9 | mod scope; 10 | mod ty; 11 | mod val; 12 | mod llvm; 13 | 14 | pub struct Llvm { 15 | context: llvm::Context, 16 | signed_attribute: llvm::Attribute, 17 | unsigned_attribute: llvm::Attribute, 18 | } 19 | 20 | impl Llvm { 21 | pub fn new() -> Self { 22 | let context = llvm::Context::create(); 23 | let signed_attribute = context.create_string_attribute("signage", "signed"); 24 | let unsigned_attribute = context.create_string_attribute("signage", "unsigned"); 25 | 26 | Llvm { 27 | context, 28 | signed_attribute, 29 | unsigned_attribute, 30 | } 31 | } 32 | 33 | fn compile_struct<'ctx>(&'ctx self, ast_struct: &Struct) { 34 | let struct_type = self.context.opaque_struct_type(&ast_struct.name); 35 | let fields = ast_struct 36 | .fields 37 | .iter() 38 | .map(|field_type| self.compile_type(field_type)) 39 | .collect::>(); 40 | 41 | struct_type.set_body(&fields, false); 42 | } 43 | 44 | fn compile_global<'ctx>(&'ctx self, ast_global: &Global, module: &llvm::Module<'ctx>) { 45 | let global_type = self.compile_type(&ast_global.ty); 46 | let global = module.add_global(global_type, None, &ast_global.name); 47 | 48 | if let Some(ast_value) = &ast_global.value { 49 | if let Some(value) = self.compile_value(ast_value, Scope::Global) { 50 | global.set_initializer(&value.raw); 51 | } else { 52 | panic!("Attempted to initialize global variable '{}' with a value that could not be compiled in the global scope", ast_global.name); 53 | } 54 | } 55 | 56 | let _ = global; 57 | } 58 | 59 | fn compile_functions<'ctx>(&'ctx self, ast_module: &Module, module: &llvm::Module<'ctx>) { 60 | let mut pairs = Vec::with_capacity(ast_module.functions.len()); 61 | for ast_function in &ast_module.functions { 62 | let function_type = self.compile_signature(&ast_function.signature); 63 | let function = module.add_function(&ast_function.name, function_type, None); 64 | 65 | /* 66 | TODO 67 | if let Some(signed) = ast_function.ty.return_type.get_signage() { 68 | if signed { 69 | function.add_attribute(llvm::AttributeLoc::Return, self.signed_attribute); 70 | } else { 71 | function.add_attribute(llvm::AttributeLoc::Return, self.unsigned_attribute); 72 | } 73 | } 74 | */ 75 | 76 | for (i, param) in ast_function.signature.input.iter().enumerate() { 77 | let Some(signed) = param.get_signage() else { 78 | continue; 79 | }; 80 | 81 | let attribute = if signed { 82 | self.signed_attribute 83 | } else { 84 | self.unsigned_attribute 85 | }; 86 | 87 | function.add_attribute(llvm::AttributeLoc::Param(i as u32), attribute); 88 | } 89 | 90 | pairs.push((ast_function, function)); 91 | } 92 | 93 | for (ast_function, function) in pairs { 94 | self.create_scope(&module, function) 95 | .compile_body(ast_function); 96 | } 97 | } 98 | } 99 | 100 | impl Backend for Llvm { 101 | type CompiledModule<'ctx> = llvm::Module<'ctx>; 102 | 103 | fn compile_module<'ctx>(&'ctx mut self, ast_module: &Module) -> Self::CompiledModule<'ctx> { 104 | let module = self.context.create_module(&ast_module.name); 105 | 106 | for ast_struct in &ast_module.structs { 107 | self.compile_struct(ast_struct); 108 | } 109 | 110 | for ast_global in &ast_module.globals { 111 | self.compile_global(ast_global, &module); 112 | } 113 | 114 | self.compile_functions(ast_module, &module); 115 | 116 | module 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /dorian-llvm/src/llvm.rs: -------------------------------------------------------------------------------- 1 | pub(crate) use inkwell::{ 2 | attributes::{Attribute, AttributeLoc}, 3 | basic_block::BasicBlock as Block, 4 | builder::Builder, 5 | context::Context, 6 | module::Module, 7 | types::{ 8 | BasicMetadataTypeEnum as MetadataType, 9 | BasicTypeEnum as Type, 10 | FloatType, 11 | FunctionType, 12 | IntType, 13 | PointerType, 14 | StringRadix, 15 | VectorType, 16 | VoidType, 17 | }, 18 | values::{ 19 | BasicValueEnum as RawValue, 20 | FloatValue as Float, 21 | FunctionValue as Function, 22 | IntValue as Int 23 | }, 24 | AddressSpace, 25 | FloatPredicate as FloatCmpOp, 26 | IntPredicate as IntCmpOp, 27 | }; 28 | use inkwell::types::{BasicType, StructType}; 29 | 30 | pub(crate) enum ScalarType<'ctx> { 31 | Int(IntType<'ctx>), 32 | Float(FloatType<'ctx>), 33 | Pointer(PointerType<'ctx>), 34 | } 35 | 36 | impl<'ctx> ScalarType<'ctx> { 37 | pub(crate) fn vec_type(&self, size: u32) -> VectorType<'ctx> { 38 | match self { 39 | ScalarType::Int(x) => x.vec_type(size), 40 | ScalarType::Float(x) => x.vec_type(size), 41 | ScalarType::Pointer(x) => x.vec_type(size), 42 | } 43 | } 44 | 45 | pub(crate) fn as_basic_type_enum(&self) -> Type<'ctx> { 46 | match self { 47 | ScalarType::Int(x) => x.as_basic_type_enum(), 48 | ScalarType::Float(x) => x.as_basic_type_enum(), 49 | ScalarType::Pointer(x) => x.as_basic_type_enum(), 50 | } 51 | } 52 | } 53 | 54 | 55 | 56 | // An LLVM value with AST context information. 57 | #[derive(Copy, Clone)] 58 | pub(crate) struct Value<'ctx> { 59 | pub(crate) raw: RawValue<'ctx>, 60 | pub(crate) signage: Option, 61 | } 62 | 63 | impl<'ctx> Value<'ctx> { 64 | pub(crate) fn new(raw: RawValue<'ctx>, signage: Option) -> Self { 65 | Value { raw, signage } 66 | } 67 | } 68 | 69 | pub(crate) enum AggregateType<'ctx> { 70 | Value(Type<'ctx>), 71 | Struct(StructType<'ctx>), 72 | Void(VoidType<'ctx>), 73 | } 74 | 75 | impl<'ctx> AggregateType<'ctx> { 76 | pub(crate) fn fn_type( 77 | &self, 78 | param_types: &[MetadataType<'ctx>], 79 | is_var_arg: bool, 80 | ) -> FunctionType<'ctx> { 81 | match self { 82 | AggregateType::Value(x) => x.fn_type(param_types, is_var_arg), 83 | AggregateType::Struct(x) => x.fn_type(param_types, is_var_arg), 84 | AggregateType::Void(x) => x.fn_type(param_types, is_var_arg), 85 | } 86 | } 87 | } -------------------------------------------------------------------------------- /dorian-llvm/src/scope.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use inkwell::values::BasicValue; 3 | use ast::block::Block; 4 | use ast::block::stmt::{AssignStmt, BindStmt, IfElse, IfStmt, ReturnStmt, Stmt, WhileStmt}; 5 | use ast::function::Function; 6 | use ast::val::Var; 7 | use crate::{llvm, Llvm}; 8 | 9 | impl Llvm { 10 | pub(crate) fn create_scope<'ctx, 'm>( 11 | &'ctx self, 12 | module: &'m llvm::Module<'ctx>, 13 | function: llvm::Function<'ctx>, 14 | ) -> LocalScope<'ctx, 'm> { 15 | LocalScope { 16 | llvm: self, 17 | module, 18 | function, 19 | builder: self.context.create_builder(), 20 | levels: vec![Level::new()], 21 | depth: 0, 22 | } 23 | } 24 | } 25 | 26 | #[derive(Copy, Clone)] 27 | pub(crate) enum Scope<'ctx, 'l, 'm> { 28 | Global, 29 | Local(&'l LocalScope<'ctx, 'm>), 30 | } 31 | 32 | impl<'ctx, 'l, 'm> Scope<'ctx, 'l, 'm> { 33 | pub(crate) fn to_local(self) -> Option<&'l LocalScope<'ctx, 'm>> { 34 | match self { 35 | Scope::Global { .. } => None, 36 | Scope::Local(scope) => Some(scope), 37 | } 38 | } 39 | } 40 | 41 | pub(crate) struct LocalScope<'ctx, 'm> { 42 | pub(crate) llvm: &'ctx Llvm, 43 | pub(crate) module: &'m llvm::Module<'ctx>, 44 | pub(crate) function: llvm::Function<'ctx>, 45 | pub(crate) builder: llvm::Builder<'ctx>, 46 | levels: Vec>, 47 | depth: usize, 48 | } 49 | 50 | impl<'ctx> LocalScope<'ctx, '_> { 51 | pub(crate) fn get_var(&self, var: &Var, load: bool) -> Option> { 52 | // Iterate in reverse to prefer the most recent scope and allow for shadowing 53 | let mut stored_value = None; 54 | for level in self.levels.iter().rev() { 55 | if let Some(x) = level.values.get(var.name.as_ref()) { 56 | stored_value = Some(*x); 57 | break; 58 | } 59 | } 60 | 61 | let stored_value = stored_value?; 62 | if load { 63 | let pointer_value = stored_value.value.raw.into_pointer_value(); 64 | let loaded_value = self.builder.build_load(stored_value.base_type, pointer_value, &var.name).unwrap(); 65 | Some(llvm::Value::new(loaded_value, stored_value.value.signage)) 66 | } else { 67 | Some(stored_value.value) 68 | } 69 | } 70 | 71 | pub(crate) fn compile_body(&mut self, Function { body, .. }: &Function) { 72 | let block = self.append_block(); 73 | self.builder.position_at_end(block); 74 | 75 | for stmt in &body.stmts { 76 | self.compile_stmt(stmt); 77 | } 78 | } 79 | 80 | fn push_level(&mut self) { 81 | self.levels.push(Level::new()); 82 | self.depth += 1; 83 | } 84 | 85 | fn pop_level(&mut self) { 86 | self.levels.pop(); 87 | self.depth -= 1; 88 | } 89 | 90 | fn compile_block(&mut self, block: &Block) -> bool { 91 | self.push_level(); 92 | for stmt in &block.stmts { 93 | if self.compile_stmt(stmt) { 94 | return true; 95 | } 96 | } 97 | self.pop_level(); 98 | false 99 | } 100 | 101 | fn compile_stmt(&mut self, stmt: &Stmt) -> bool { 102 | match stmt { 103 | Stmt::If(x) => self.compile_if_stmt(x), 104 | Stmt::Return(x) => { 105 | self.compile_return_stmt(x); 106 | true 107 | }, 108 | Stmt::While(x) => { 109 | self.compile_while_stmt(x); 110 | false 111 | }, 112 | Stmt::Bind(x) => { 113 | self.compile_bind_stmt(x); 114 | false 115 | }, 116 | Stmt::Assign(x) => { 117 | self.compile_assign_stmt(x); 118 | false 119 | }, 120 | } 121 | } 122 | 123 | fn compile_if_stmt(&mut self, stmt: &IfStmt) -> bool { 124 | let condition = self.llvm.compile_value(&stmt.condition, Scope::Local(self)).unwrap(); 125 | 126 | let then_block = self.append_block(); 127 | let else_block = self.append_block(); 128 | let merge_block = self.append_block(); 129 | 130 | self.builder.build_conditional_branch(condition.raw.into_int_value(), then_block, else_block).unwrap(); 131 | self.builder.position_at_end(then_block); 132 | 133 | let then_terminates = self.compile_block(&stmt.then_block); 134 | if !then_terminates { 135 | self.builder.build_unconditional_branch(merge_block).unwrap(); 136 | } 137 | 138 | self.builder.position_at_end(else_block); 139 | let else_terminates = if let Some(if_else) = &stmt.if_else { 140 | self.compile_if_else(if_else) 141 | } else { 142 | false 143 | }; 144 | 145 | if !else_terminates { 146 | self.builder.build_unconditional_branch(merge_block).unwrap(); 147 | } 148 | 149 | if !then_terminates || !else_terminates { 150 | self.builder.position_at_end(merge_block); 151 | } 152 | 153 | then_terminates && else_terminates 154 | } 155 | 156 | fn compile_if_else(&mut self, if_else: &IfElse) -> bool { 157 | match if_else { 158 | IfElse::If(x) => self.compile_if_stmt(x), 159 | IfElse::Else(x) => self.compile_block(x), 160 | } 161 | } 162 | 163 | fn compile_return_stmt(&mut self, stmt: &ReturnStmt) { 164 | match stmt.values.len() { 165 | 0 => { 166 | self.builder.build_return(None).unwrap(); 167 | } 168 | 1 => { 169 | let ast_value = &stmt.values[0]; 170 | let value = self.llvm.compile_value(ast_value, Scope::Local(self)).unwrap(); 171 | self.builder.build_return(Some(&value.raw)).unwrap(); 172 | } 173 | _ => { 174 | let values = stmt.values.iter() 175 | .map(|x| self.llvm.compile_value(x, Scope::Local(self)).unwrap().raw) 176 | .collect::>(); 177 | self.builder.build_aggregate_return(&values).unwrap(); 178 | } 179 | } 180 | } 181 | 182 | fn append_block(&self) -> llvm::Block<'ctx> { 183 | self.llvm.context.append_basic_block(self.function, "") 184 | } 185 | 186 | fn compile_while_stmt(&mut self, stmt: &WhileStmt) { 187 | let condition = self.llvm.compile_value(&stmt.condition, Scope::Local(self)).unwrap(); 188 | 189 | let exit_block = self.append_block(); 190 | let loop_block = self.append_block(); 191 | self.builder.build_conditional_branch(condition.raw.into_int_value(), loop_block, exit_block).unwrap(); 192 | 193 | self.builder.position_at_end(loop_block); 194 | self.compile_block(&stmt.loop_block); 195 | 196 | let condition = self.llvm.compile_value(&stmt.condition, Scope::Local(self)).unwrap(); 197 | self.builder.build_conditional_branch(condition.raw.into_int_value(), loop_block, exit_block).unwrap(); 198 | 199 | self.builder.position_at_end(exit_block); 200 | } 201 | 202 | fn compile_bind_stmt(&mut self, stmt: &BindStmt) { 203 | let stored_value = self.llvm.compile_value(&stmt.value, Scope::Local(self)).unwrap(); 204 | let stored_type = stored_value.raw.get_type(); 205 | 206 | // TODO: there are more efficient ways to handle variables and avoid stack allocation by increasing context awareness 207 | let pointer_value = self.builder.build_alloca(stored_type, "").unwrap(); 208 | self.builder.build_store(pointer_value, stored_value.raw).unwrap(); 209 | 210 | let raw_value = pointer_value.as_basic_value_enum(); 211 | let value = llvm::Value::new(raw_value, stored_value.signage); 212 | 213 | self.levels[self.depth].values.insert(stmt.name.to_string(), StoredValue { base_type: stored_type, value }); 214 | } 215 | 216 | fn compile_assign_stmt(&mut self, stmt: &AssignStmt) { 217 | let var = self.get_var(&stmt.var, false).unwrap(); 218 | let new_value = self.llvm.compile_value(&stmt.value, Scope::Local(self)).unwrap(); 219 | self.builder.build_store(var.raw.into_pointer_value(), new_value.raw).unwrap(); 220 | } 221 | } 222 | 223 | pub(crate) struct Level<'ctx> { 224 | values: HashMap>, 225 | } 226 | 227 | impl<'ctx> Level<'ctx> { 228 | pub(crate) fn new() -> Self { 229 | Level { values: HashMap::new() } 230 | } 231 | } 232 | 233 | #[derive(Copy, Clone)] 234 | pub(crate) struct StoredValue<'ctx> { 235 | base_type: llvm::Type<'ctx>, 236 | value: llvm::Value<'ctx>, 237 | } 238 | -------------------------------------------------------------------------------- /dorian-llvm/src/ty.rs: -------------------------------------------------------------------------------- 1 | use crate::{llvm, Llvm}; 2 | use ast::ty::{BoolType, FloatType, IntType, IntWidth, NumType, PtrType, ScalarType, Type, VectorType}; 3 | use inkwell::types::BasicType; 4 | use ast::function::Signature; 5 | 6 | impl Llvm { 7 | pub(crate) fn compile_type(&self, ty: &Type) -> llvm::Type { 8 | match ty { 9 | Type::Scalar(x) => self.compile_scalar_type(x).as_basic_type_enum(), 10 | Type::Vector(x) => self.compile_vector_type(x).as_basic_type_enum(), 11 | } 12 | } 13 | 14 | fn compile_scalar_type(&self, ty: &ScalarType) -> llvm::ScalarType { 15 | match ty { 16 | ScalarType::Num(x) => self.compile_num_type(x), 17 | ScalarType::Bool(x) => llvm::ScalarType::Int(self.compile_bool_type(x)), 18 | ScalarType::Ptr(x) => llvm::ScalarType::Pointer(self.compile_ptr_type(x)), 19 | } 20 | } 21 | 22 | fn compile_num_type(&self, ty: &NumType) -> llvm::ScalarType { 23 | match ty { 24 | NumType::Int(x) => llvm::ScalarType::Int(self.compile_int_type(x)), 25 | NumType::Float(x) => llvm::ScalarType::Float(self.compile_float_type(x)), 26 | } 27 | } 28 | 29 | fn compile_int_type(&self, ty: &IntType) -> llvm::IntType { 30 | match ty.width { 31 | IntWidth::I8 => self.context.i8_type(), 32 | IntWidth::I16 => self.context.i16_type(), 33 | IntWidth::I32 => self.context.i32_type(), 34 | IntWidth::I64 => self.context.i64_type(), 35 | IntWidth::I128 => self.context.i128_type(), 36 | } 37 | } 38 | 39 | fn compile_float_type(&self, ty: &FloatType) -> llvm::FloatType { 40 | match ty { 41 | FloatType::F16 => self.context.f16_type(), 42 | FloatType::F32 => self.context.f32_type(), 43 | FloatType::F64 => self.context.f64_type(), 44 | FloatType::F128 => self.context.f128_type(), 45 | } 46 | } 47 | 48 | fn compile_bool_type(&self, _: &BoolType) -> llvm::IntType { 49 | self.context.bool_type() 50 | } 51 | 52 | fn compile_ptr_type(&self, _: &PtrType) -> llvm::PointerType { 53 | self.context.ptr_type(llvm::AddressSpace::default()) 54 | } 55 | 56 | fn compile_vector_type(&self, ty: &VectorType) -> llvm::VectorType { 57 | self.compile_scalar_type(&ty.elem).vec_type(ty.len) 58 | } 59 | 60 | pub(crate) fn compile_signature(&self, signature: &Signature) -> llvm::FunctionType { 61 | let param_types = signature.input.iter() 62 | .map(|param| self.compile_type(param).into()) 63 | .collect::>(); 64 | let return_type = self.compile_aggregate_type(&signature.output); 65 | 66 | return_type.fn_type(¶m_types, false) 67 | } 68 | 69 | fn compile_aggregate_type(&self, types: &[Type]) -> llvm::AggregateType { 70 | match types.len() { 71 | 0 => llvm::AggregateType::Void(self.context.void_type()), 72 | 1 => llvm::AggregateType::Value(self.compile_type(&types[0])), 73 | _ => { 74 | let field_types = types.iter() 75 | .map(|x| self.compile_type(x)) 76 | .collect::>(); 77 | let struct_type = self.context.struct_type(&field_types, false); 78 | 79 | llvm::AggregateType::Struct(struct_type) 80 | } 81 | } 82 | } 83 | } -------------------------------------------------------------------------------- /dorian-llvm/src/val.rs: -------------------------------------------------------------------------------- 1 | use inkwell::values::BasicValue; 2 | use ast::val::{Arg, Bin, BinOp, Call, ContextValue, Expr, Float, Int, Lit, Num, SignedInt, Una, UnaOp, UnsignedInt, Value}; 3 | use crate::{llvm, Llvm}; 4 | use crate::scope::{LocalScope, Scope}; 5 | 6 | impl Llvm { 7 | pub(crate) fn compile_value<'ctx>( 8 | &'ctx self, 9 | value: &Value, 10 | scope: Scope<'ctx, '_, '_>, 11 | ) -> Option> { 12 | match &value { 13 | Value::Context(x) => Some(self.compile_context_value(x, scope.to_local()?)), 14 | Value::Expr(x) => self.compile_expr(x, scope), 15 | Value::Lit(x) => Some(self.compile_lit(x)), 16 | Value::Call(x) => self.compile_call(x, scope), 17 | } 18 | } 19 | 20 | fn compile_context_value<'ctx>( 21 | &'ctx self, 22 | value: &ContextValue, 23 | scope: &LocalScope<'ctx, '_>, 24 | ) -> llvm::Value<'ctx> { 25 | match value { 26 | ContextValue::Arg(arg) => self.compile_arg_value(arg, scope), 27 | ContextValue::Var(var) => scope 28 | .get_var(var, true) 29 | .expect("Failed to get variable from scope"), 30 | } 31 | } 32 | 33 | fn compile_arg_value<'ctx>( 34 | &'ctx self, 35 | arg: &Arg, 36 | scope: &LocalScope<'ctx, '_>, 37 | ) -> llvm::Value<'ctx> { 38 | let value = scope 39 | .function 40 | .get_nth_param(arg.param_index) 41 | .expect("Failed to get function parameter"); 42 | 43 | let attribute_loc = llvm::AttributeLoc::Param(arg.param_index); 44 | let signage = scope.function 45 | .get_string_attribute(attribute_loc, "signage") 46 | .map(|x| x.get_string_value().to_bytes() == b"signed"); 47 | 48 | llvm::Value::new(value, signage) 49 | } 50 | 51 | fn compile_expr<'ctx>( 52 | &'ctx self, 53 | value: &Expr, 54 | scope: Scope<'ctx, '_, '_> 55 | ) -> Option> { 56 | match value { 57 | Expr::Bin(x) => self.compile_bin(x, scope), 58 | Expr::Una(x) => self.compile_una(x, scope), 59 | } 60 | } 61 | 62 | fn compile_bin<'ctx>(&'ctx self, value: &Bin, scope: Scope<'ctx, '_, '_>) -> Option> { 63 | let lhs = self.compile_value(&value.lhs, scope)?; 64 | let rhs = self.compile_value(&value.rhs, scope)?; 65 | 66 | let value = if lhs.raw.is_int_value() { 67 | let signed = lhs.signage.unwrap_or(false); 68 | let lhs = lhs.raw.into_int_value(); 69 | 70 | self.compile_int_bin(value.op, value.no_wrap, signed, lhs, rhs.raw, scope.to_local()?) 71 | .as_basic_value_enum() 72 | } else if lhs.raw.is_float_value() { 73 | let lhs = lhs.raw.into_float_value(); 74 | 75 | self.compile_float_bin(value.op, lhs, rhs.raw, scope.to_local()?) 76 | } else { 77 | panic!("Value does not support binary operations: {:?}", lhs.raw); 78 | }; 79 | 80 | Some(llvm::Value::new(value, lhs.signage)) 81 | } 82 | 83 | fn compile_int_bin<'ctx>( 84 | &'ctx self, 85 | op: BinOp, 86 | no_wrap: bool, 87 | signed: bool, 88 | lhs: llvm::Int<'ctx>, 89 | rhs: llvm::RawValue<'ctx>, 90 | scope: &LocalScope<'ctx, '_>, 91 | ) -> llvm::Int<'ctx> { 92 | if !rhs.is_int_value() { 93 | panic!( 94 | "Right-hand value type of binary operation does not match left-hand type which is an integer" 95 | ); 96 | } 97 | let rhs = rhs.into_int_value(); 98 | 99 | match op { 100 | BinOp::Add if no_wrap && signed => { 101 | scope.builder.build_int_nsw_add(lhs, rhs, "").unwrap() 102 | } 103 | BinOp::Add if no_wrap && !signed => { 104 | scope.builder.build_int_nuw_add(lhs, rhs, "").unwrap() 105 | } 106 | BinOp::Add => scope.builder.build_int_add(lhs, rhs, "").unwrap(), 107 | BinOp::Sub if no_wrap && signed => { 108 | scope.builder.build_int_nsw_sub(lhs, rhs, "").unwrap() 109 | } 110 | BinOp::Sub if no_wrap && !signed => { 111 | scope.builder.build_int_nuw_sub(lhs, rhs, "").unwrap() 112 | } 113 | BinOp::Sub => scope.builder.build_int_sub(lhs, rhs, "").unwrap(), 114 | BinOp::Mul if no_wrap && signed => { 115 | scope.builder.build_int_nsw_mul(lhs, rhs, "").unwrap() 116 | } 117 | BinOp::Mul if no_wrap && !signed => { 118 | scope.builder.build_int_nuw_mul(lhs, rhs, "").unwrap() 119 | } 120 | BinOp::Mul => scope.builder.build_int_mul(lhs, rhs, "").unwrap(), 121 | BinOp::Div if signed => scope.builder.build_int_signed_div(lhs, rhs, "").unwrap(), 122 | BinOp::Div => scope.builder.build_int_unsigned_div(lhs, rhs, "").unwrap(), 123 | BinOp::Rem if signed => scope.builder.build_int_signed_rem(lhs, rhs, "").unwrap(), 124 | BinOp::Rem => scope.builder.build_int_unsigned_rem(lhs, rhs, "").unwrap(), 125 | BinOp::And | BinOp::BitAnd => scope.builder.build_and(lhs, rhs, "").unwrap(), 126 | BinOp::Or | BinOp::BitOr => scope.builder.build_or(lhs, rhs, "").unwrap(), 127 | BinOp::BitXor => scope.builder.build_xor(lhs, rhs, "").unwrap(), 128 | BinOp::Shl => scope.builder.build_left_shift(lhs, rhs, "").unwrap(), 129 | BinOp::Shr => scope.builder.build_right_shift(lhs, rhs, true, "").unwrap(), 130 | BinOp::Eq => scope 131 | .builder 132 | .build_int_compare(llvm::IntCmpOp::EQ, lhs, rhs, "") 133 | .unwrap(), 134 | BinOp::Ne => scope 135 | .builder 136 | .build_int_compare(llvm::IntCmpOp::NE, lhs, rhs, "") 137 | .unwrap(), 138 | BinOp::Lt if signed => scope 139 | .builder 140 | .build_int_compare(llvm::IntCmpOp::SLT, lhs, rhs, "") 141 | .unwrap(), 142 | BinOp::Lt => scope 143 | .builder 144 | .build_int_compare(llvm::IntCmpOp::ULT, lhs, rhs, "") 145 | .unwrap(), 146 | BinOp::Gt if signed => scope 147 | .builder 148 | .build_int_compare(llvm::IntCmpOp::SGT, lhs, rhs, "") 149 | .unwrap(), 150 | BinOp::Gt => scope 151 | .builder 152 | .build_int_compare(llvm::IntCmpOp::UGT, lhs, rhs, "") 153 | .unwrap(), 154 | BinOp::Le if signed => scope 155 | .builder 156 | .build_int_compare(llvm::IntCmpOp::SLE, lhs, rhs, "") 157 | .unwrap(), 158 | BinOp::Le => scope 159 | .builder 160 | .build_int_compare(llvm::IntCmpOp::ULE, lhs, rhs, "") 161 | .unwrap(), 162 | BinOp::Ge if signed => scope 163 | .builder 164 | .build_int_compare(llvm::IntCmpOp::SGE, lhs, rhs, "") 165 | .unwrap(), 166 | BinOp::Ge => scope 167 | .builder 168 | .build_int_compare(llvm::IntCmpOp::UGE, lhs, rhs, "") 169 | .unwrap(), 170 | } 171 | } 172 | 173 | fn compile_float_bin<'ctx>( 174 | &'ctx self, 175 | op: BinOp, 176 | lhs: llvm::Float<'ctx>, 177 | rhs: llvm::RawValue<'ctx>, 178 | scope: &LocalScope<'ctx, '_>, 179 | ) -> llvm::RawValue<'ctx> { 180 | if !rhs.is_float_value() { 181 | panic!( 182 | "Right-hand value type of binary operation does not match left-hand type which is a float" 183 | ); 184 | } 185 | let rhs = rhs.into_float_value(); 186 | 187 | match op { 188 | BinOp::Add => scope 189 | .builder 190 | .build_float_add(lhs, rhs, "") 191 | .unwrap() 192 | .as_basic_value_enum(), 193 | BinOp::Sub => scope 194 | .builder 195 | .build_float_sub(lhs, rhs, "") 196 | .unwrap() 197 | .as_basic_value_enum(), 198 | BinOp::Mul => scope 199 | .builder 200 | .build_float_mul(lhs, rhs, "") 201 | .unwrap() 202 | .as_basic_value_enum(), 203 | BinOp::Div => scope 204 | .builder 205 | .build_float_div(lhs, rhs, "") 206 | .unwrap() 207 | .as_basic_value_enum(), 208 | BinOp::Rem => scope 209 | .builder 210 | .build_float_rem(lhs, rhs, "") 211 | .unwrap() 212 | .as_basic_value_enum(), 213 | BinOp::Eq => scope 214 | .builder 215 | .build_float_compare(llvm::FloatCmpOp::OEQ, lhs, rhs, "") 216 | .unwrap() 217 | .as_basic_value_enum(), 218 | BinOp::Ne => scope 219 | .builder 220 | .build_float_compare(llvm::FloatCmpOp::ONE, lhs, rhs, "") 221 | .unwrap() 222 | .as_basic_value_enum(), 223 | BinOp::Lt => scope 224 | .builder 225 | .build_float_compare(llvm::FloatCmpOp::OLT, lhs, rhs, "") 226 | .unwrap() 227 | .as_basic_value_enum(), 228 | BinOp::Gt => scope 229 | .builder 230 | .build_float_compare(llvm::FloatCmpOp::OGT, lhs, rhs, "") 231 | .unwrap() 232 | .as_basic_value_enum(), 233 | BinOp::Le => scope 234 | .builder 235 | .build_float_compare(llvm::FloatCmpOp::OLE, lhs, rhs, "") 236 | .unwrap() 237 | .as_basic_value_enum(), 238 | BinOp::Ge => scope 239 | .builder 240 | .build_float_compare(llvm::FloatCmpOp::OGE, lhs, rhs, "") 241 | .unwrap() 242 | .as_basic_value_enum(), 243 | _ => panic!("Unsupported float operation: {:?}", op), 244 | } 245 | } 246 | 247 | fn compile_una<'ctx>(&'ctx self, value: &Una, scope: Scope<'ctx, '_, '_>) -> Option> { 248 | let operand = self.compile_value(&value.operand, scope)?; 249 | let signed = operand.signage.unwrap_or(false); 250 | 251 | let value = if operand.raw.is_int_value() { 252 | let operand = operand.raw.into_int_value(); 253 | self.compile_int_una(value.op, value.no_wrap, signed, operand, scope).as_basic_value_enum() 254 | } else if operand.raw.is_float_value() { 255 | let operand = operand.raw.into_float_value(); 256 | self.compile_float_una(value.op, operand, scope.to_local()?).as_basic_value_enum() 257 | } else { 258 | panic!("Value does not support unary operations: {:?}", operand.raw); 259 | }; 260 | 261 | Some(llvm::Value::new(value, operand.signage)) 262 | } 263 | 264 | fn compile_int_una<'ctx>( 265 | &'ctx self, 266 | op: UnaOp, 267 | no_wrap: bool, 268 | signed: bool, 269 | operand: llvm::Int<'ctx>, 270 | scope: Scope<'ctx, '_, '_> 271 | ) -> llvm::Int<'ctx> { 272 | match scope { 273 | Scope::Global { .. } => match op { 274 | UnaOp::Neg if no_wrap && signed => operand.const_nsw_neg(), 275 | UnaOp::Neg if no_wrap && !signed => operand.const_nuw_neg(), 276 | UnaOp::Neg => operand.const_neg(), 277 | UnaOp::Not => operand.const_not(), 278 | } 279 | Scope::Local(LocalScope { builder: b, .. }) => match op { 280 | UnaOp::Neg if no_wrap && signed => b.build_int_nsw_neg(operand, ""), 281 | UnaOp::Neg if no_wrap && !signed => b.build_int_nuw_neg(operand, ""), 282 | UnaOp::Neg => b.build_int_neg(operand, ""), 283 | UnaOp::Not => b.build_not(operand, ""), 284 | }.unwrap(), 285 | } 286 | } 287 | 288 | fn compile_float_una<'ctx>( 289 | &'ctx self, 290 | op: UnaOp, 291 | operand: llvm::Float<'ctx>, 292 | scope: &LocalScope<'ctx, '_>, 293 | ) -> llvm::Float<'ctx> { 294 | match op { 295 | UnaOp::Neg => scope.builder.build_float_neg(operand, "").unwrap(), 296 | UnaOp::Not => panic!("Not operation is not supported for float values"), 297 | } 298 | } 299 | 300 | fn compile_lit(&self, value: &Lit) -> llvm::Value { 301 | match value { 302 | Lit::Num(x) => self.compile_num(x), 303 | Lit::Bool(x) => { 304 | let raw_value = self.context.bool_type() 305 | .const_int(*x as u64, false) 306 | .as_basic_value_enum(); 307 | llvm::Value::new(raw_value, None) 308 | }, 309 | } 310 | } 311 | 312 | fn compile_num(&self, value: &Num) -> llvm::Value { 313 | match value { 314 | Num::Int(x) => match x { 315 | &Int::Signed(x) => { 316 | let (raw_value, neg) = match x { 317 | SignedInt::B8(i) => ( 318 | self.context.i8_type().const_int(i.abs() as u64, true), 319 | i < 0, 320 | ), 321 | SignedInt::B16(i) => ( 322 | self.context.i16_type().const_int(i.abs() as u64, true), 323 | i < 0, 324 | ), 325 | SignedInt::B32(i) => ( 326 | self.context.i32_type().const_int(i.abs() as u64, true), 327 | i < 0, 328 | ), 329 | SignedInt::B64(i) => ( 330 | self.context.i64_type().const_int(i.abs() as u64, true), 331 | i < 0, 332 | ), 333 | SignedInt::B128(i) => ( 334 | self.context.i128_type().const_int_from_string( 335 | &i.abs().to_string(), 336 | llvm::StringRadix::Decimal, 337 | ).unwrap(), 338 | i < 0, 339 | ), 340 | }; 341 | 342 | let raw_value = if neg { 343 | raw_value.const_neg().as_basic_value_enum() 344 | } else { 345 | raw_value.as_basic_value_enum() 346 | }; 347 | 348 | llvm::Value::new(raw_value, Some(true)) 349 | }, 350 | &Int::Unsigned(x) => { 351 | let raw_value = match x { 352 | UnsignedInt::U8(i) => self.context.i8_type().const_int(i as u64, false), 353 | UnsignedInt::U16(i) => self.context.i16_type().const_int(i as u64, false), 354 | UnsignedInt::U32(i) => self.context.i32_type().const_int(i as u64, false), 355 | UnsignedInt::U64(i) => self.context.i64_type().const_int(i, false), 356 | UnsignedInt::U128(i) => self.context.i128_type() 357 | .const_int_from_string(&i.to_string(), llvm::StringRadix::Decimal) 358 | .unwrap(), 359 | }.as_basic_value_enum(); 360 | 361 | llvm::Value::new(raw_value, Some(false)) 362 | }, 363 | }, 364 | Num::Float(x) => { 365 | let raw_value = match x { 366 | Float::F32(x) => self.context.f32_type() 367 | .const_float(*x as f64) 368 | .as_basic_value_enum(), 369 | Float::F64(x) => self.context.f64_type() 370 | .const_float(*x) 371 | .as_basic_value_enum(), 372 | }; 373 | llvm::Value::new(raw_value, None) 374 | }, 375 | } 376 | } 377 | 378 | fn compile_call<'ctx>( 379 | &'ctx self, 380 | value: &Call, 381 | scope: Scope<'ctx, '_, '_>, 382 | ) -> Option> { 383 | let function_value = scope.to_local()?.module 384 | .get_function(&value.function_name) 385 | .expect("Failed to get function"); 386 | let signage = function_value 387 | .get_string_attribute(llvm::AttributeLoc::Return, "signage") 388 | .map(|x| x.get_string_value().to_bytes() == b"signed"); 389 | 390 | if function_value.get_type().get_return_type().is_none() { 391 | panic!("Function {} has no return type", value.function_name); 392 | } 393 | 394 | let mut args = Vec::with_capacity(value.args.len()); 395 | for arg in &value.args { 396 | let compiled_arg = self.compile_value(arg, scope)?; 397 | args.push(compiled_arg.raw.into()); 398 | } 399 | let value = scope.to_local()?.builder 400 | .build_call(function_value, &args, "") 401 | .unwrap() 402 | .try_as_basic_value() 403 | .unwrap_left(); 404 | 405 | Some(llvm::Value::new(value, signage)) 406 | } 407 | } 408 | -------------------------------------------------------------------------------- /examples/iterative_fib.rs: -------------------------------------------------------------------------------- 1 | use dorian::prelude::*; 2 | 3 | fn main() { 4 | let fib_function = Function::new("iterative_fib") 5 | .add_input(ty::u32()) 6 | .add_output(ty::u32()) 7 | .build_block(build_fib_body); 8 | 9 | let mut module = Module::new("iterative_fib_example"); 10 | module.add_function(fib_function); 11 | 12 | let mut llvm = Llvm::new(); 13 | let compiled_module = llvm.compile_module(&module); 14 | 15 | let execution_engine = compiled_module 16 | .create_jit_execution_engine(inkwell::OptimizationLevel::None) 17 | .unwrap(); 18 | let fib_fn = unsafe { 19 | execution_engine 20 | .get_function:: u32>("iterative_fib") 21 | .unwrap() 22 | }; 23 | 24 | let result = unsafe { fib_fn.call(10) }; 25 | 26 | assert_eq!(result, 55, "The 10th Fibonacci number should be 55"); 27 | } 28 | 29 | fn build_fib_body(scope: &mut BlockBuilder) { 30 | use val::*; 31 | 32 | let condition = le(arg(0), lit(1i32)); 33 | scope 34 | .if_then(condition, |scope| { 35 | scope.ret([arg(0)]); 36 | }); 37 | 38 | scope.bind("a", lit(0u32)); 39 | scope.bind("b", lit(1u32)); 40 | scope.bind("i", lit(2u32)); 41 | 42 | scope.loop_while(le(var("i"), arg(0)), |scope| { 43 | scope.assign(var("i"), add(var("i"), lit(1u32))); 44 | scope.bind("c", add(var("a"), var("b"))); 45 | scope.assign(var("a"), var("b")); 46 | scope.assign(var("b"), var("c")); 47 | }); 48 | 49 | scope.ret([var("b")]); 50 | } 51 | -------------------------------------------------------------------------------- /examples/recursive_fib.rs: -------------------------------------------------------------------------------- 1 | use dorian::prelude::*; 2 | 3 | fn main() { 4 | let fib_function = Function::new("recursive_fib") 5 | .add_input(ty::u32()) 6 | .add_output(ty::u32()) 7 | .build_block(build_fib_body); 8 | 9 | let mut module = Module::new("recursive_fib_example"); 10 | module.add_function(fib_function); 11 | 12 | let mut llvm = Llvm::new(); 13 | let compiled_module = llvm.compile_module(&module); 14 | 15 | let execution_engine = compiled_module 16 | .create_jit_execution_engine(inkwell::OptimizationLevel::None) 17 | .unwrap(); 18 | let fib_fn = unsafe { 19 | execution_engine 20 | .get_function:: u32>("recursive_fib") 21 | .unwrap() 22 | }; 23 | 24 | let result = unsafe { fib_fn.call(10) }; 25 | 26 | assert_eq!(result, 55, "The 10th Fibonacci number should be 55"); 27 | } 28 | 29 | fn build_fib_body(scope: &mut BlockBuilder) { 30 | use val::*; 31 | 32 | let condition = le(arg(0), lit(1i32)); 33 | scope 34 | .if_then(condition, |scope| { 35 | scope.ret([arg(0)]); 36 | }) 37 | .or_else(|scope| { 38 | scope.ret([add( 39 | call("recursive_fib", vec![sub(arg(0), lit(1u32))]), 40 | call("recursive_fib", vec![sub(arg(0), lit(2u32))]), 41 | )]); 42 | }) 43 | } 44 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | pub extern crate dorian_ast as ast; 2 | #[cfg(feature = "cranelift")] 3 | pub extern crate dorian_cranelift as cranelift; 4 | #[cfg(feature = "llvm")] 5 | pub extern crate dorian_llvm as llvm; 6 | 7 | pub mod prelude { 8 | pub use crate::ast::block::Block; 9 | pub use crate::ast::block::builder::*; 10 | pub use crate::ast::block::stmt::*; 11 | pub use crate::ast::ty::{Type, ScalarType, NumType, IntType, IntWidth, FloatType, BoolType, PtrType, 12 | VectorType, VoidType}; 13 | pub use crate::ast::ty::util as ty; 14 | pub use crate::ast::val::{Value, ContextValue, Arg, Var, Expr, Bin, BinOp, Una, UnaOp, Lit, Num, Int, SignedInt, 15 | UnsignedInt, Float, Call}; 16 | pub use crate::ast::val::util as val; 17 | pub use crate::ast::backend::*; 18 | pub use crate::ast::function::*; 19 | pub use crate::ast::global::*; 20 | pub use crate::ast::module::*; 21 | pub use crate::ast::structure::*; 22 | 23 | #[cfg(feature = "llvm")] 24 | pub use crate::llvm::Llvm; 25 | 26 | #[cfg(feature = "cranelift")] 27 | pub use crate::cranelift::Cranelift; 28 | } --------------------------------------------------------------------------------