├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── README.md ├── docs ├── notes.md ├── resources │ └── blog.md ├── rust-current.md └── rust-history.md ├── helloworld ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── README.md ├── README.zh-CN.md ├── docs │ ├── README.md │ ├── images │ │ └── gep-examples.png │ └── refs.md └── src │ └── main.rs ├── justfile ├── llvm-wasm ├── .gitignore ├── Cargo.toml ├── README.md ├── index.html └── src │ └── main.rs ├── offical-demo ├── Cargo.toml ├── README.md └── src │ └── main.rs ├── pass ├── Cargo.toml ├── README.md └── src │ └── main.rs ├── src └── main.rs ├── stdlib ├── Cargo.toml ├── src │ └── main.rs └── stdlib │ ├── .gitignore │ ├── Makefile │ ├── charj.c │ └── lib.c ├── tools └── control_flow_graph │ ├── .main.dot │ ├── README.md │ ├── callgraph.dot │ └── callgraph.png └── wasm ├── .gitignore ├── Cargo.toml ├── README.md ├── demos ├── CMakeLists.txt ├── hello.c └── hello.o ├── hello.asm ├── hello.wat ├── index.html └── src └── main.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | .idea 3 | *.ll 4 | main.bc 5 | *.wasm 6 | **/*.wasm 7 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "aho-corasick" 5 | version = "0.7.15" 6 | source = "registry+https://github.com/rust-lang/crates.io-index" 7 | checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5" 8 | dependencies = [ 9 | "memchr", 10 | ] 11 | 12 | [[package]] 13 | name = "bitflags" 14 | version = "1.2.1" 15 | source = "registry+https://github.com/rust-lang/crates.io-index" 16 | checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" 17 | 18 | [[package]] 19 | name = "cc" 20 | version = "1.0.62" 21 | source = "registry+https://github.com/rust-lang/crates.io-index" 22 | checksum = "f1770ced377336a88a67c473594ccc14eca6f4559217c34f64aac8f83d641b40" 23 | 24 | [[package]] 25 | name = "cfg-if" 26 | version = "0.1.10" 27 | source = "registry+https://github.com/rust-lang/crates.io-index" 28 | checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" 29 | 30 | [[package]] 31 | name = "cfg-if" 32 | version = "1.0.0" 33 | source = "registry+https://github.com/rust-lang/crates.io-index" 34 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 35 | 36 | [[package]] 37 | name = "cloudabi" 38 | version = "0.1.0" 39 | source = "registry+https://github.com/rust-lang/crates.io-index" 40 | checksum = "4344512281c643ae7638bbabc3af17a11307803ec8f0fcad9fae512a8bf36467" 41 | dependencies = [ 42 | "bitflags", 43 | ] 44 | 45 | [[package]] 46 | name = "either" 47 | version = "1.6.1" 48 | source = "registry+https://github.com/rust-lang/crates.io-index" 49 | checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" 50 | 51 | [[package]] 52 | name = "hello" 53 | version = "0.1.0" 54 | dependencies = [ 55 | "inkwell 0.1.0-llvm10sample", 56 | ] 57 | 58 | [[package]] 59 | name = "inkwell" 60 | version = "0.1.0-llvm10sample" 61 | source = "registry+https://github.com/rust-lang/crates.io-index" 62 | checksum = "e079c12273d96e41481454a37ad968e607e1ce51b39b9facd3a802a12df6e9dc" 63 | dependencies = [ 64 | "either", 65 | "inkwell_internals 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 66 | "libc", 67 | "llvm-sys 100.2.0", 68 | "once_cell", 69 | "parking_lot", 70 | "regex", 71 | ] 72 | 73 | [[package]] 74 | name = "inkwell" 75 | version = "0.1.0" 76 | source = "git+https://github.com/TheDan64/inkwell#4e8c101ea5f1961b0e7f1e9a275ae508750f3b28" 77 | dependencies = [ 78 | "either", 79 | "inkwell_internals 0.2.0 (git+https://github.com/TheDan64/inkwell)", 80 | "libc", 81 | "llvm-sys 100.2.0", 82 | "once_cell", 83 | "parking_lot", 84 | "regex", 85 | ] 86 | 87 | [[package]] 88 | name = "inkwell_internals" 89 | version = "0.2.0" 90 | source = "git+https://github.com/TheDan64/inkwell#4e8c101ea5f1961b0e7f1e9a275ae508750f3b28" 91 | dependencies = [ 92 | "proc-macro2", 93 | "quote", 94 | "syn", 95 | ] 96 | 97 | [[package]] 98 | name = "inkwell_internals" 99 | version = "0.2.0" 100 | source = "registry+https://github.com/rust-lang/crates.io-index" 101 | checksum = "b22cf4eda09069b48204cce4b7cd9a25311da813780e95a038524f2210fab44e" 102 | dependencies = [ 103 | "proc-macro2", 104 | "quote", 105 | "syn", 106 | ] 107 | 108 | [[package]] 109 | name = "instant" 110 | version = "0.1.8" 111 | source = "registry+https://github.com/rust-lang/crates.io-index" 112 | checksum = "cb1fc4429a33e1f80d41dc9fea4d108a88bec1de8053878898ae448a0b52f613" 113 | dependencies = [ 114 | "cfg-if 1.0.0", 115 | ] 116 | 117 | [[package]] 118 | name = "lazy_static" 119 | version = "1.4.0" 120 | source = "registry+https://github.com/rust-lang/crates.io-index" 121 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 122 | 123 | [[package]] 124 | name = "libc" 125 | version = "0.2.80" 126 | source = "registry+https://github.com/rust-lang/crates.io-index" 127 | checksum = "4d58d1b70b004888f764dfbf6a26a3b0342a1632d33968e4a179d8011c760614" 128 | 129 | [[package]] 130 | name = "llvm-rust" 131 | version = "0.1.0" 132 | 133 | [[package]] 134 | name = "llvm-stdlib" 135 | version = "0.1.0" 136 | dependencies = [ 137 | "inkwell 0.1.0-llvm10sample", 138 | ] 139 | 140 | [[package]] 141 | name = "llvm-sys" 142 | version = "100.2.0" 143 | source = "registry+https://github.com/rust-lang/crates.io-index" 144 | checksum = "9109e19fbfac3458f2970189719fa19f1007c6fd4e08c44fdebf4be0ddbe261d" 145 | dependencies = [ 146 | "cc", 147 | "lazy_static", 148 | "libc", 149 | "regex", 150 | "semver 0.9.0", 151 | ] 152 | 153 | [[package]] 154 | name = "llvm-sys" 155 | version = "110.0.0" 156 | source = "registry+https://github.com/rust-lang/crates.io-index" 157 | checksum = "b0062a0c6635fb5d57c6ebba072dcae50e41651030363cf06d220b0d016840f2" 158 | dependencies = [ 159 | "cc", 160 | "lazy_static", 161 | "libc", 162 | "regex", 163 | "semver 0.11.0", 164 | ] 165 | 166 | [[package]] 167 | name = "llvm-wasm" 168 | version = "0.1.0" 169 | dependencies = [ 170 | "llvm-sys 110.0.0", 171 | ] 172 | 173 | [[package]] 174 | name = "lock_api" 175 | version = "0.4.1" 176 | source = "registry+https://github.com/rust-lang/crates.io-index" 177 | checksum = "28247cc5a5be2f05fbcd76dd0cf2c7d3b5400cb978a28042abcd4fa0b3f8261c" 178 | dependencies = [ 179 | "scopeguard", 180 | ] 181 | 182 | [[package]] 183 | name = "memchr" 184 | version = "2.3.4" 185 | source = "registry+https://github.com/rust-lang/crates.io-index" 186 | checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" 187 | 188 | [[package]] 189 | name = "once_cell" 190 | version = "1.5.0" 191 | source = "registry+https://github.com/rust-lang/crates.io-index" 192 | checksum = "95c43eba5c640051fbde86a3377842386a94281df3ff0a5f0365c2075ed5c66f" 193 | 194 | [[package]] 195 | name = "parking_lot" 196 | version = "0.11.0" 197 | source = "registry+https://github.com/rust-lang/crates.io-index" 198 | checksum = "a4893845fa2ca272e647da5d0e46660a314ead9c2fdd9a883aabc32e481a8733" 199 | dependencies = [ 200 | "instant", 201 | "lock_api", 202 | "parking_lot_core", 203 | ] 204 | 205 | [[package]] 206 | name = "parking_lot_core" 207 | version = "0.8.0" 208 | source = "registry+https://github.com/rust-lang/crates.io-index" 209 | checksum = "c361aa727dd08437f2f1447be8b59a33b0edd15e0fcee698f935613d9efbca9b" 210 | dependencies = [ 211 | "cfg-if 0.1.10", 212 | "cloudabi", 213 | "instant", 214 | "libc", 215 | "redox_syscall", 216 | "smallvec", 217 | "winapi", 218 | ] 219 | 220 | [[package]] 221 | name = "pass" 222 | version = "0.1.0" 223 | 224 | [[package]] 225 | name = "pest" 226 | version = "2.1.3" 227 | source = "registry+https://github.com/rust-lang/crates.io-index" 228 | checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" 229 | dependencies = [ 230 | "ucd-trie", 231 | ] 232 | 233 | [[package]] 234 | name = "proc-macro2" 235 | version = "1.0.24" 236 | source = "registry+https://github.com/rust-lang/crates.io-index" 237 | checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" 238 | dependencies = [ 239 | "unicode-xid", 240 | ] 241 | 242 | [[package]] 243 | name = "quote" 244 | version = "1.0.7" 245 | source = "registry+https://github.com/rust-lang/crates.io-index" 246 | checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" 247 | dependencies = [ 248 | "proc-macro2", 249 | ] 250 | 251 | [[package]] 252 | name = "redox_syscall" 253 | version = "0.1.57" 254 | source = "registry+https://github.com/rust-lang/crates.io-index" 255 | checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" 256 | 257 | [[package]] 258 | name = "regex" 259 | version = "1.4.2" 260 | source = "registry+https://github.com/rust-lang/crates.io-index" 261 | checksum = "38cf2c13ed4745de91a5eb834e11c00bcc3709e773173b2ce4c56c9fbde04b9c" 262 | dependencies = [ 263 | "aho-corasick", 264 | "memchr", 265 | "regex-syntax", 266 | "thread_local", 267 | ] 268 | 269 | [[package]] 270 | name = "regex-syntax" 271 | version = "0.6.21" 272 | source = "registry+https://github.com/rust-lang/crates.io-index" 273 | checksum = "3b181ba2dcf07aaccad5448e8ead58db5b742cf85dfe035e2227f137a539a189" 274 | 275 | [[package]] 276 | name = "scopeguard" 277 | version = "1.1.0" 278 | source = "registry+https://github.com/rust-lang/crates.io-index" 279 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 280 | 281 | [[package]] 282 | name = "semver" 283 | version = "0.9.0" 284 | source = "registry+https://github.com/rust-lang/crates.io-index" 285 | checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" 286 | dependencies = [ 287 | "semver-parser 0.7.0", 288 | ] 289 | 290 | [[package]] 291 | name = "semver" 292 | version = "0.11.0" 293 | source = "registry+https://github.com/rust-lang/crates.io-index" 294 | checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" 295 | dependencies = [ 296 | "semver-parser 0.10.1", 297 | ] 298 | 299 | [[package]] 300 | name = "semver-parser" 301 | version = "0.7.0" 302 | source = "registry+https://github.com/rust-lang/crates.io-index" 303 | checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" 304 | 305 | [[package]] 306 | name = "semver-parser" 307 | version = "0.10.1" 308 | source = "registry+https://github.com/rust-lang/crates.io-index" 309 | checksum = "42ef146c2ad5e5f4b037cd6ce2ebb775401729b19a82040c1beac9d36c7d1428" 310 | dependencies = [ 311 | "pest", 312 | ] 313 | 314 | [[package]] 315 | name = "smallvec" 316 | version = "1.4.2" 317 | source = "registry+https://github.com/rust-lang/crates.io-index" 318 | checksum = "fbee7696b84bbf3d89a1c2eccff0850e3047ed46bfcd2e92c29a2d074d57e252" 319 | 320 | [[package]] 321 | name = "syn" 322 | version = "1.0.48" 323 | source = "registry+https://github.com/rust-lang/crates.io-index" 324 | checksum = "cc371affeffc477f42a221a1e4297aedcea33d47d19b61455588bd9d8f6b19ac" 325 | dependencies = [ 326 | "proc-macro2", 327 | "quote", 328 | "unicode-xid", 329 | ] 330 | 331 | [[package]] 332 | name = "thread_local" 333 | version = "1.0.1" 334 | source = "registry+https://github.com/rust-lang/crates.io-index" 335 | checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" 336 | dependencies = [ 337 | "lazy_static", 338 | ] 339 | 340 | [[package]] 341 | name = "ucd-trie" 342 | version = "0.1.3" 343 | source = "registry+https://github.com/rust-lang/crates.io-index" 344 | checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" 345 | 346 | [[package]] 347 | name = "unicode-xid" 348 | version = "0.2.1" 349 | source = "registry+https://github.com/rust-lang/crates.io-index" 350 | checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" 351 | 352 | [[package]] 353 | name = "wasm" 354 | version = "0.1.0" 355 | dependencies = [ 356 | "inkwell 0.1.0", 357 | ] 358 | 359 | [[package]] 360 | name = "well" 361 | version = "0.1.0" 362 | dependencies = [ 363 | "inkwell 0.1.0", 364 | "llvm-sys 100.2.0", 365 | ] 366 | 367 | [[package]] 368 | name = "winapi" 369 | version = "0.3.9" 370 | source = "registry+https://github.com/rust-lang/crates.io-index" 371 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 372 | dependencies = [ 373 | "winapi-i686-pc-windows-gnu", 374 | "winapi-x86_64-pc-windows-gnu", 375 | ] 376 | 377 | [[package]] 378 | name = "winapi-i686-pc-windows-gnu" 379 | version = "0.4.0" 380 | source = "registry+https://github.com/rust-lang/crates.io-index" 381 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 382 | 383 | [[package]] 384 | name = "winapi-x86_64-pc-windows-gnu" 385 | version = "0.4.0" 386 | source = "registry+https://github.com/rust-lang/crates.io-index" 387 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 388 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "llvm-rust" 3 | version = "0.1.0" 4 | authors = ["Phodal Huang "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | 11 | 12 | 13 | 14 | [workspace] 15 | # for ideas only 16 | members = [ 17 | "helloworld", 18 | "stdlib", 19 | "pass", 20 | "wasm", 21 | "llvm-wasm", 22 | "offical-demo", 23 | ] -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rust LLVM Practise 2 | 3 | ``` 4 | ├── helloworld // hello, world demo 5 | └── stdlib // Rust call c libs demo 6 | ``` 7 | 8 | ## Setup 9 | 10 | 0. Rust 1.39+ + LLVM 10.0 11 | 1. use [llvmenv](https://github.com/termoshtt/llvmenv) to Manage multiple LLVM/Clang builds 12 | 2. Rust LLVM wrapper use [inkwell](https://github.com/TheDan64/inkwell) 13 | 3. generated LLVM IR code see in [main.ll](main.ll) 14 | 15 | ### Install LLVM 16 | 17 | ``` 18 | brew install cmake ninja 19 | ``` 20 | 21 | Homebrew 11 22 | 23 | ``` 24 | brew install llvm 25 | ``` 26 | 27 | #### remote 28 | 29 | ```bash 30 | cargo install llvmenv 31 | ``` 32 | 33 | usage 34 | 35 | ``` 36 | llvmenv init 37 | llvmenv entries 38 | local-llvm 10.0.0 39 | ``` 40 | 41 | about `1.5 hours` in my MBP 2018 42 | 43 | #### local 44 | 45 | Add local llvm for build to: `$XDG_CONFIG_HOME/llvmenv/entry.toml 46 | 47 | docs: https://docs.rs/llvmenv/0.3.0/llvmenv/entry/index.html 48 | 49 | ```` 50 | [local-llvm] 51 | path = "/path/to/your/src" 52 | target = ["X86"] 53 | ``` 54 | 55 | **build** 56 | 57 | ``` 58 | llvmenv build-entry local-llvm 59 | ``` 60 | 61 | ### run & build 62 | 63 | ``` 64 | LLVM_SYS_101_PREFIX=$HOME/llvm/llvm-10.0.1.src/build cargo build 65 | LLVM_SYS_101_PREFIX=$HOME/llvm/llvm-10.0.1.src/build cargo run 66 | ```` 67 | -------------------------------------------------------------------------------- /docs/notes.md: -------------------------------------------------------------------------------- 1 | ## 符号表 2 | 3 | 在计算机科学中,符号表是一种用于语言翻译器中的数据结构。在符号表中,程序源代码中的每个标识符都和它的声明或使用信息绑定在一起,比如其数据类型、作用域以及内存地址。 4 | 5 | -------------------------------------------------------------------------------- /docs/resources/blog.md: -------------------------------------------------------------------------------- 1 | - [Compiling a Functional Language Using C++, Part 8 - LLVM](https://danilafe.com/blog/08_compiler_llvm/) -------------------------------------------------------------------------------- /docs/rust-current.md: -------------------------------------------------------------------------------- 1 | Lexer 2 | 3 | ```rust 4 | /// Enum representing common lexeme types. 5 | // perf note: Changing all `usize` to `u32` doesn't change performance. See #77629 6 | #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] 7 | pub enum TokenKind { 8 | // Multi-char tokens: 9 | /// "// comment" 10 | LineComment { doc_style: Option }, 11 | /// `/* block comment */` 12 | /// 13 | /// Block comments can be recursive, so the sequence like `/* /* */` 14 | /// will not be considered terminated and will result in a parsing error. 15 | BlockComment { doc_style: Option, terminated: bool }, 16 | /// Any whitespace characters sequence. 17 | Whitespace, 18 | /// "ident" or "continue" 19 | /// At this step keywords are also considered identifiers. 20 | Ident, 21 | /// "r#ident" 22 | RawIdent, 23 | /// "12_u8", "1.0e-40", "b"123"". See `LiteralKind` for more details. 24 | Literal { kind: LiteralKind, suffix_start: usize }, 25 | /// "'a" 26 | Lifetime { starts_with_number: bool }, 27 | 28 | // One-char tokens: 29 | /// ";" 30 | Semi, 31 | /// "," 32 | Comma, 33 | /// "." 34 | Dot, 35 | /// "(" 36 | OpenParen, 37 | /// ")" 38 | CloseParen, 39 | /// "{" 40 | OpenBrace, 41 | /// "}" 42 | CloseBrace, 43 | /// "[" 44 | OpenBracket, 45 | /// "]" 46 | CloseBracket, 47 | /// "@" 48 | At, 49 | /// "#" 50 | Pound, 51 | /// "~" 52 | Tilde, 53 | /// "?" 54 | Question, 55 | /// ":" 56 | Colon, 57 | /// "$" 58 | Dollar, 59 | /// "=" 60 | Eq, 61 | /// "!" 62 | Bang, 63 | /// "<" 64 | Lt, 65 | /// ">" 66 | Gt, 67 | /// "-" 68 | Minus, 69 | /// "&" 70 | And, 71 | /// "|" 72 | Or, 73 | /// "+" 74 | Plus, 75 | /// "*" 76 | Star, 77 | /// "/" 78 | Slash, 79 | /// "^" 80 | Caret, 81 | /// "%" 82 | Percent, 83 | 84 | /// Unknown token, not expected by the lexer, e.g. "№" 85 | Unknown, 86 | } 87 | ``` 88 | 89 | ```rust 90 | #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] 91 | pub enum LiteralKind { 92 | /// "12_u8", "0o100", "0b120i99" 93 | Int { base: Base, empty_int: bool }, 94 | /// "12.34f32", "0b100.100" 95 | Float { base: Base, empty_exponent: bool }, 96 | /// "'a'", "'\\'", "'''", "';" 97 | Char { terminated: bool }, 98 | /// "b'a'", "b'\\'", "b'''", "b';" 99 | Byte { terminated: bool }, 100 | /// ""abc"", ""abc" 101 | Str { terminated: bool }, 102 | /// "b"abc"", "b"abc" 103 | ByteStr { terminated: bool }, 104 | /// "r"abc"", "r#"abc"#", "r####"ab"###"c"####", "r#"a" 105 | RawStr { n_hashes: u16, err: Option }, 106 | /// "br"abc"", "br#"abc"#", "br####"ab"###"c"####", "br#"a" 107 | RawByteStr { n_hashes: u16, err: Option }, 108 | } 109 | ``` 110 | 111 | -------------------------------------------------------------------------------- /docs/rust-history.md: -------------------------------------------------------------------------------- 1 | ```rust 2 | pub fn Parser(sess: @mut ParseSess, 3 | +cfg: ast::crate_cfg, 4 | +rdr: @reader) 5 | -> Parser { 6 | let tok0 = copy rdr.next_token(); 7 | let interner = rdr.interner(); 8 | 9 | Parser { 10 | reader: rdr, 11 | interner: interner, 12 | sess: sess, 13 | cfg: cfg, 14 | token: @mut copy tok0.tok, 15 | span: @mut copy tok0.sp, 16 | last_span: @mut copy tok0.sp, 17 | buffer: @mut ([copy tok0, .. 4]), 18 | buffer_start: @mut 0, 19 | buffer_end: @mut 0, 20 | tokens_consumed: @mut 0, 21 | restriction: @mut UNRESTRICTED, 22 | quote_depth: @mut 0, 23 | keywords: token::keyword_table(), 24 | strict_keywords: token::strict_keyword_table(), 25 | reserved_keywords: token::reserved_keyword_table(), 26 | obsolete_set: @mut LinearSet::new(), 27 | mod_path_stack: @mut ~[], 28 | } 29 | } 30 | ``` 31 | -------------------------------------------------------------------------------- /helloworld/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | .idea 3 | -------------------------------------------------------------------------------- /helloworld/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "aho-corasick" 5 | version = "0.7.15" 6 | source = "registry+https://github.com/rust-lang/crates.io-index" 7 | checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5" 8 | dependencies = [ 9 | "memchr", 10 | ] 11 | 12 | [[package]] 13 | name = "bitflags" 14 | version = "1.2.1" 15 | source = "registry+https://github.com/rust-lang/crates.io-index" 16 | checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" 17 | 18 | [[package]] 19 | name = "cc" 20 | version = "1.0.62" 21 | source = "registry+https://github.com/rust-lang/crates.io-index" 22 | checksum = "f1770ced377336a88a67c473594ccc14eca6f4559217c34f64aac8f83d641b40" 23 | 24 | [[package]] 25 | name = "cfg-if" 26 | version = "0.1.10" 27 | source = "registry+https://github.com/rust-lang/crates.io-index" 28 | checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" 29 | 30 | [[package]] 31 | name = "cfg-if" 32 | version = "1.0.0" 33 | source = "registry+https://github.com/rust-lang/crates.io-index" 34 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 35 | 36 | [[package]] 37 | name = "cloudabi" 38 | version = "0.1.0" 39 | source = "registry+https://github.com/rust-lang/crates.io-index" 40 | checksum = "4344512281c643ae7638bbabc3af17a11307803ec8f0fcad9fae512a8bf36467" 41 | dependencies = [ 42 | "bitflags", 43 | ] 44 | 45 | [[package]] 46 | name = "either" 47 | version = "1.6.1" 48 | source = "registry+https://github.com/rust-lang/crates.io-index" 49 | checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" 50 | 51 | [[package]] 52 | name = "hello" 53 | version = "0.1.0" 54 | dependencies = [ 55 | "inkwell", 56 | ] 57 | 58 | [[package]] 59 | name = "inkwell" 60 | version = "0.1.0-llvm10sample" 61 | source = "registry+https://github.com/rust-lang/crates.io-index" 62 | checksum = "e079c12273d96e41481454a37ad968e607e1ce51b39b9facd3a802a12df6e9dc" 63 | dependencies = [ 64 | "either", 65 | "inkwell_internals", 66 | "libc", 67 | "llvm-sys", 68 | "once_cell", 69 | "parking_lot", 70 | "regex", 71 | ] 72 | 73 | [[package]] 74 | name = "inkwell_internals" 75 | version = "0.2.0" 76 | source = "registry+https://github.com/rust-lang/crates.io-index" 77 | checksum = "b22cf4eda09069b48204cce4b7cd9a25311da813780e95a038524f2210fab44e" 78 | dependencies = [ 79 | "proc-macro2", 80 | "quote", 81 | "syn", 82 | ] 83 | 84 | [[package]] 85 | name = "instant" 86 | version = "0.1.8" 87 | source = "registry+https://github.com/rust-lang/crates.io-index" 88 | checksum = "cb1fc4429a33e1f80d41dc9fea4d108a88bec1de8053878898ae448a0b52f613" 89 | dependencies = [ 90 | "cfg-if 1.0.0", 91 | ] 92 | 93 | [[package]] 94 | name = "lazy_static" 95 | version = "1.4.0" 96 | source = "registry+https://github.com/rust-lang/crates.io-index" 97 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 98 | 99 | [[package]] 100 | name = "libc" 101 | version = "0.2.80" 102 | source = "registry+https://github.com/rust-lang/crates.io-index" 103 | checksum = "4d58d1b70b004888f764dfbf6a26a3b0342a1632d33968e4a179d8011c760614" 104 | 105 | [[package]] 106 | name = "llvm-sys" 107 | version = "100.2.0" 108 | source = "registry+https://github.com/rust-lang/crates.io-index" 109 | checksum = "9109e19fbfac3458f2970189719fa19f1007c6fd4e08c44fdebf4be0ddbe261d" 110 | dependencies = [ 111 | "cc", 112 | "lazy_static", 113 | "libc", 114 | "regex", 115 | "semver", 116 | ] 117 | 118 | [[package]] 119 | name = "lock_api" 120 | version = "0.4.1" 121 | source = "registry+https://github.com/rust-lang/crates.io-index" 122 | checksum = "28247cc5a5be2f05fbcd76dd0cf2c7d3b5400cb978a28042abcd4fa0b3f8261c" 123 | dependencies = [ 124 | "scopeguard", 125 | ] 126 | 127 | [[package]] 128 | name = "memchr" 129 | version = "2.3.4" 130 | source = "registry+https://github.com/rust-lang/crates.io-index" 131 | checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" 132 | 133 | [[package]] 134 | name = "once_cell" 135 | version = "1.4.1" 136 | source = "registry+https://github.com/rust-lang/crates.io-index" 137 | checksum = "260e51e7efe62b592207e9e13a68e43692a7a279171d6ba57abd208bf23645ad" 138 | 139 | [[package]] 140 | name = "parking_lot" 141 | version = "0.11.0" 142 | source = "registry+https://github.com/rust-lang/crates.io-index" 143 | checksum = "a4893845fa2ca272e647da5d0e46660a314ead9c2fdd9a883aabc32e481a8733" 144 | dependencies = [ 145 | "instant", 146 | "lock_api", 147 | "parking_lot_core", 148 | ] 149 | 150 | [[package]] 151 | name = "parking_lot_core" 152 | version = "0.8.0" 153 | source = "registry+https://github.com/rust-lang/crates.io-index" 154 | checksum = "c361aa727dd08437f2f1447be8b59a33b0edd15e0fcee698f935613d9efbca9b" 155 | dependencies = [ 156 | "cfg-if 0.1.10", 157 | "cloudabi", 158 | "instant", 159 | "libc", 160 | "redox_syscall", 161 | "smallvec", 162 | "winapi", 163 | ] 164 | 165 | [[package]] 166 | name = "proc-macro2" 167 | version = "1.0.24" 168 | source = "registry+https://github.com/rust-lang/crates.io-index" 169 | checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" 170 | dependencies = [ 171 | "unicode-xid", 172 | ] 173 | 174 | [[package]] 175 | name = "quote" 176 | version = "1.0.7" 177 | source = "registry+https://github.com/rust-lang/crates.io-index" 178 | checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" 179 | dependencies = [ 180 | "proc-macro2", 181 | ] 182 | 183 | [[package]] 184 | name = "redox_syscall" 185 | version = "0.1.57" 186 | source = "registry+https://github.com/rust-lang/crates.io-index" 187 | checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" 188 | 189 | [[package]] 190 | name = "regex" 191 | version = "1.4.2" 192 | source = "registry+https://github.com/rust-lang/crates.io-index" 193 | checksum = "38cf2c13ed4745de91a5eb834e11c00bcc3709e773173b2ce4c56c9fbde04b9c" 194 | dependencies = [ 195 | "aho-corasick", 196 | "memchr", 197 | "regex-syntax", 198 | "thread_local", 199 | ] 200 | 201 | [[package]] 202 | name = "regex-syntax" 203 | version = "0.6.21" 204 | source = "registry+https://github.com/rust-lang/crates.io-index" 205 | checksum = "3b181ba2dcf07aaccad5448e8ead58db5b742cf85dfe035e2227f137a539a189" 206 | 207 | [[package]] 208 | name = "scopeguard" 209 | version = "1.1.0" 210 | source = "registry+https://github.com/rust-lang/crates.io-index" 211 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 212 | 213 | [[package]] 214 | name = "semver" 215 | version = "0.9.0" 216 | source = "registry+https://github.com/rust-lang/crates.io-index" 217 | checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" 218 | dependencies = [ 219 | "semver-parser", 220 | ] 221 | 222 | [[package]] 223 | name = "semver-parser" 224 | version = "0.7.0" 225 | source = "registry+https://github.com/rust-lang/crates.io-index" 226 | checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" 227 | 228 | [[package]] 229 | name = "smallvec" 230 | version = "1.4.2" 231 | source = "registry+https://github.com/rust-lang/crates.io-index" 232 | checksum = "fbee7696b84bbf3d89a1c2eccff0850e3047ed46bfcd2e92c29a2d074d57e252" 233 | 234 | [[package]] 235 | name = "syn" 236 | version = "1.0.48" 237 | source = "registry+https://github.com/rust-lang/crates.io-index" 238 | checksum = "cc371affeffc477f42a221a1e4297aedcea33d47d19b61455588bd9d8f6b19ac" 239 | dependencies = [ 240 | "proc-macro2", 241 | "quote", 242 | "unicode-xid", 243 | ] 244 | 245 | [[package]] 246 | name = "thread_local" 247 | version = "1.0.1" 248 | source = "registry+https://github.com/rust-lang/crates.io-index" 249 | checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" 250 | dependencies = [ 251 | "lazy_static", 252 | ] 253 | 254 | [[package]] 255 | name = "unicode-xid" 256 | version = "0.2.1" 257 | source = "registry+https://github.com/rust-lang/crates.io-index" 258 | checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" 259 | 260 | [[package]] 261 | name = "winapi" 262 | version = "0.3.9" 263 | source = "registry+https://github.com/rust-lang/crates.io-index" 264 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 265 | dependencies = [ 266 | "winapi-i686-pc-windows-gnu", 267 | "winapi-x86_64-pc-windows-gnu", 268 | ] 269 | 270 | [[package]] 271 | name = "winapi-i686-pc-windows-gnu" 272 | version = "0.4.0" 273 | source = "registry+https://github.com/rust-lang/crates.io-index" 274 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 275 | 276 | [[package]] 277 | name = "winapi-x86_64-pc-windows-gnu" 278 | version = "0.4.0" 279 | source = "registry+https://github.com/rust-lang/crates.io-index" 280 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 281 | -------------------------------------------------------------------------------- /helloworld/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hello" 3 | version = "0.1.0" 4 | authors = ["Phodal Huang "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | inkwell = { version = "0.1.0-llvm10sample", features = ["target-x86", "target-webassembly", "llvm10-0"] } 11 | -------------------------------------------------------------------------------- /helloworld/README.md: -------------------------------------------------------------------------------- 1 | # Rust LLVM hello, world 2 | -------------------------------------------------------------------------------- /helloworld/README.zh-CN.md: -------------------------------------------------------------------------------- 1 | # Rust LLVM "hello, world!" 2 | 3 | ## Create 4 | 5 | 创建过程: 6 | 7 | 1. 创建一个 Module 8 | 2. 在 Module 中添加 Function 9 | 3. 在 Function 中添加 BasicBlock 10 | 4. 在 BasicBlock 中添加指令 11 | 5. 创建一个 ExecutionEngine 12 | 6. 使用 ExecutionEngine 来运行 IR 13 | 14 | ## Concepts 15 | 16 | **Module**,可以将 LLVM 中的 Module 类比为 C 程序中的源文件。一个 C 源文件中包含函数和全局变量定义、外部函数和外部函数声明,一个 Module 中包含的内容也基本上如此,只不过C源文件中是源码来表示,Module 中是用 IR 来表示。 17 | 18 | **Function**,Function 是 LLVM JIT 操作的基本单位。Function 被 Module 所包含。LLVM 的 Function 包含函数名、函数的返回值和参数类型。Function 内部则包含 BasicBlock。 19 | 20 | **BasicBlock**,BasicBlock与编译技术中常见的基本块 (basic block) 的概念是一致的。BasicBlock 必须以跳转指令结尾。 21 | 22 | **Instruction**,Instruction就是 LLVM IR 的最基本单位。Instruction 被包含在 **BasicBlock 中。 23 | 24 | **ExecutionEngine**,ExecutionEngine 是用来运行 IR 的。运行IR有两种方式:解释运行和 JIT 生成机器码运行。相应的 ExecutionEngine 就有两种:Interpreter 和 JIT。ExecutionEngine 的类型可以在创建 ExecutionEngine 时指定。 25 | 26 | ## Convert 27 | 28 | 29 | ### Struct 30 | 31 | ![Mapping](docs/images/gep-examples.png) 32 | 33 | In C code: 34 | 35 | ```c 36 | struct RT { 37 | int A; 38 | int B[10][20]; 39 | int C; 40 | } 41 | struct ST { 42 | struct RT X; 43 | int Y; 44 | struct RT Z; 45 | } 46 | int *foo(struct ST *s) { 47 | return &s[1].Z.B[5][13]; 48 | } 49 | ``` 50 | 51 | In LLVM code: 52 | 53 | ```ll 54 | %RT = type { i32, [10 x [20 x i32]], i32 } 55 | %ST = type { %RT, i32, %RT } 56 | define i32* @foo(%ST* %s) { 57 | entry: 58 | %arrayidx = getelementptr %ST* %s, i32 1, i32 2, i32 1, i32 5, i32 13 59 | ret i32* %arrayidx 60 | } 61 | ``` 62 | 63 | 参考文章: 64 | 65 | - 《[使用LLVM IR编程](http://richardustc.github.io/2013-06-19-2013-06-19-programming-with-llvm-ir.html)》 66 | -------------------------------------------------------------------------------- /helloworld/docs/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phodal/rust-llvm-practises/85a100bf148108802f3740b124d0e78a3361ac45/helloworld/docs/README.md -------------------------------------------------------------------------------- /helloworld/docs/images/gep-examples.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phodal/rust-llvm-practises/85a100bf148108802f3740b124d0e78a3361ac45/helloworld/docs/images/gep-examples.png -------------------------------------------------------------------------------- /helloworld/docs/refs.md: -------------------------------------------------------------------------------- 1 | From: [https://people.seas.harvard.edu/~chong/software.html](https://people.seas.harvard.edu/~chong/software.html) 2 | 3 | ## Language Design and Analysis 4 | 5 | * Flamio 6 | [Flamio](https://people.seas.harvard.edu/~chong/abstracts/PedersenC2019.html) is a programming language (a domain-specific languages embedded in Haskell) that instantiates the Flow-Limited Authorization Model (FLAM) with coarse-grained information-flow control. (See the publication for a link to download the code.) 7 | 8 | * Whip 9 | [Whip](https://people.seas.harvard.edu/~chong/abstracts/WayeDC2017.html) is a contract system for modern services. Whip (i) provides programmers with a higher-order contract language tailored to the needs of modern services; and (ii) monitors services at run time to detect services that do not live up to their advertised interfaces. Contract monitoring is local to a service. Services are treated as black boxes, allowing heterogeneous implementation languages without modification to services' code. Thus, Whip does not disturb the loosely coupled nature of modern services. See the [Whip website](http://whip.services/) for more details and links to code. 10 | 11 | * Pidgin 12 | [Pidgin](https://people.seas.harvard.edu/~chong/abstracts/JohnsonWMC2015.html) is a program analysis and understanding tool that enables the specification and enforcement of precise application-specific information security guarantees. Pidgin combines program-dependence graphs (PDGs), which precisely capture the information flows in a whole application, with a custom PDG query language. Queries express properties about the paths in the PDG; because paths in the PDG correspond to information flows in the application, queries can be used to specify global security policies. You can download Pidgin from the [Accrue project page](https://people.seas.harvard.edu/~chong/accrue.html). 13 | 14 | * Shill 15 | Shill is a shell scripting language designed to make it easy to follow the Principle of Least Privilege. Shill uses capabilities to control what access scripts have to your system. Every Shill script comes with a contract that describes what it can do, so users can run third-party scripts with confidence. Using capability-based sandboxes, Shill's security guarantees extend even to native executables launched by scripts. See the [Shill web page](http://shill.seas.harvard.edu/) for more information. 16 | 17 | * Accrue 18 | The Accrue Interprocedural Java Analysis Framework (Accrue) is a framework for interprocedural analysis of Java programs. It is built using the [Polyglot extensible compiler framework](http://www.cs.cornell.edu/Projects/polyglot/), and is also suitable for interprocedural analysis of programs written in Java language extensions implemented with Polyglot. We developed Accrue to enable inference and discovery of strong information security guarantees in Java programs. See the [Accrue project page](https://people.seas.harvard.edu/~chong/accrue.html) for more information. 19 | 20 | * Polyglot 21 | Polyglot is an extensible Java source-to-source compiler. Polyglot facilitates extension of the Java programming language with new language features. See the [Polyglot home page](http://www.cs.cornell.edu/Projects/polyglot/) for more information. 22 | 23 | * Jif 24 | Jif is a security-typed programming language that extends Java with support for information flow control and access control, enforced at both compile time and run time. See the [Jif home page](http://www.cs.cornell.edu/jif/) for more information. 25 | 26 | * Jif_E_ 27 | Jif_E_ extends the Jif programming language with support for [declassification and erasure](https://people.seas.harvard.edu/~chong/abstracts/ChongM08.html) information security policies. Jif_E_ has been used to implement [Civitas](https://www.cs.cornell.edu/projects/civitas/), a remote voting system. Please contact me if you are interested in obtaining a copy of the Jif_E_ compiler and run-time system. -------------------------------------------------------------------------------- /helloworld/src/main.rs: -------------------------------------------------------------------------------- 1 | use inkwell::builder::Builder; 2 | use inkwell::context::Context; 3 | use inkwell::module::{Linkage, Module}; 4 | use inkwell::types::{IntType}; 5 | use inkwell::values::PointerValue; 6 | use inkwell::{AddressSpace, OptimizationLevel}; 7 | 8 | pub struct Compiler<'a, 'ctx> { 9 | pub context: &'ctx Context, 10 | pub builder: &'a Builder<'ctx>, 11 | pub module: &'a Module<'ctx>, 12 | } 13 | 14 | impl<'a, 'ctx> Compiler<'a, 'ctx> { 15 | pub fn new(context: &'ctx Context, builder: &'a Builder<'ctx>, module: &'a Module<'ctx>) { 16 | let compiler = Compiler { 17 | context, 18 | builder, 19 | module, 20 | }; 21 | 22 | compiler.compile(); 23 | } 24 | 25 | pub fn compile(&self) { 26 | let i32_type = self.context.i32_type(); 27 | let function_type = i32_type.fn_type(&[], false); 28 | 29 | let function = self.module.add_function("main", function_type, None); 30 | let basic_block = self.context.append_basic_block(function, "entrypoint"); 31 | 32 | self.builder.position_at_end(basic_block); 33 | 34 | let i32_type = self.emit_printf_call(&"hello, world!\n", "hello"); 35 | self.builder 36 | .build_return(Some(&i32_type.const_int(0, false))); 37 | 38 | let _result = self.module.print_to_file("main.ll"); 39 | 40 | self.execute() 41 | } 42 | 43 | fn emit_printf_call(&self, hello_str: &&str, name: &str) -> IntType { 44 | let i32_type = self.context.i32_type(); 45 | let str_type = self.context.i8_type().ptr_type(AddressSpace::Generic); 46 | let printf_type = i32_type.fn_type(&[str_type.into()], true); 47 | 48 | // `printf` is same to `puts` 49 | let printf = self 50 | .module 51 | .add_function("puts", printf_type, Some(Linkage::External)); 52 | 53 | let pointer_value = self.emit_global_string(hello_str, name); 54 | self.builder.build_call(printf, &[pointer_value.into()], ""); 55 | 56 | i32_type 57 | } 58 | 59 | fn execute(&self) { 60 | // verify function logic, change emit_printf_call type float i32_type to f64_type will works 61 | self.module.get_function("main").unwrap().verify(true); 62 | 63 | let ee = self 64 | .module 65 | .create_jit_execution_engine(OptimizationLevel::None) 66 | .unwrap(); 67 | let maybe_fn = unsafe { ee.get_function:: i32>("main") }; 68 | 69 | let compiled_fn = match maybe_fn { 70 | Ok(f) => f, 71 | Err(err) => { 72 | panic!("{:?}", err); 73 | } 74 | }; 75 | 76 | unsafe { 77 | compiled_fn.call(); 78 | } 79 | } 80 | 81 | fn emit_global_string(&self, string: &&str, name: &str) -> PointerValue { 82 | let ty = self.context.i8_type().array_type(string.len() as u32); 83 | let gv = self 84 | .module 85 | .add_global(ty, Some(AddressSpace::Generic), name); 86 | gv.set_linkage(Linkage::Internal); 87 | gv.set_initializer(&self.context.const_string(string.as_ref(), false)); 88 | 89 | let pointer_value = self.builder.build_pointer_cast( 90 | gv.as_pointer_value(), 91 | self.context.i8_type().ptr_type(AddressSpace::Generic), 92 | name, 93 | ); 94 | 95 | pointer_value 96 | } 97 | } 98 | 99 | pub fn create_compiler() { 100 | let context = Context::create(); 101 | let module = context.create_module("repl"); 102 | let builder = context.create_builder(); 103 | 104 | Compiler::new(&context, &builder, &module); 105 | } 106 | 107 | fn main() { 108 | create_compiler(); 109 | } 110 | -------------------------------------------------------------------------------- /justfile: -------------------------------------------------------------------------------- 1 | tests: 2 | cargo test --all 3 | 4 | tests-ci: 5 | cargo test 6 | 7 | build: 8 | cargo build 9 | 10 | @bench: 11 | cargo bench 12 | 13 | @lint: 14 | rustup component add clippy 15 | rustup component add rustfmt 16 | cargo clippy -- -D warnings 17 | cargo clippy --tests 18 | cargo fmt -- --check 19 | 20 | @fix: 21 | cargo fmt --all 22 | 23 | clean: 24 | cargo clean 25 | find . -type f -name "*.orig" -exec rm {} \; 26 | find . -type f -name "*.bk" -exec rm {} \; 27 | find . -type f -name ".*~" -exec rm {} \; 28 | -------------------------------------------------------------------------------- /llvm-wasm/.gitignore: -------------------------------------------------------------------------------- 1 | main.bc 2 | main.o 3 | main.wat 4 | -------------------------------------------------------------------------------- /llvm-wasm/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "llvm-wasm" 3 | version = "0.1.0" 4 | authors = ["Phodal Huang "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | llvm-sys = "110" 11 | -------------------------------------------------------------------------------- /llvm-wasm/README.md: -------------------------------------------------------------------------------- 1 | // from https://github.com/jayphelps/using-llvm-from-rust-to-generate-webassembly/blob/master/main.rs 2 | 3 | ## WASM 4 | 5 | ``` 6 | llc -march=wasm32 -filetype=obj main.bc -o main.o 7 | lld -flavor wasm --allow-undefined main.o -o main.wasm 8 | ``` 9 | 10 | to main 11 | 12 | ``` 13 | lld -flavor wasm --allow-undefined main.o -o main.wasm --entry=main 14 | ``` 15 | 16 | convert 17 | 18 | ``` 19 | wasm2wat main.wasm > main.wat 20 | ``` 21 | 22 | 23 | ## BitCode 24 | 25 | ``` 26 | llc -march=wasm32 main.bc -o main.s 27 | ``` 28 | 29 | ``` 30 | s2wasm main.s -o main.wast 31 | ``` 32 | 33 | ``` 34 | s2wasm --emit-binary main.s -o main.wasm 35 | ``` 36 | 37 | -------------------------------------------------------------------------------- /llvm-wasm/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 25 | 26 | 27 |
28 | 29 | -------------------------------------------------------------------------------- /llvm-wasm/src/main.rs: -------------------------------------------------------------------------------- 1 | extern crate llvm_sys; 2 | 3 | use llvm_sys::bit_writer::*; 4 | use llvm_sys::core::*; 5 | use std::ptr; 6 | 7 | macro_rules! c_str { 8 | ($s:expr) => ( 9 | concat!($s, "\0").as_ptr() as *const i8 10 | ); 11 | } 12 | 13 | fn main() { 14 | unsafe { 15 | // setup 16 | let context = LLVMContextCreate(); 17 | let module = LLVMModuleCreateWithName(c_str!("main")); 18 | let builder = LLVMCreateBuilderInContext(context); 19 | 20 | // common types 21 | let void_type = LLVMVoidTypeInContext(context); 22 | let i8_type = LLVMIntTypeInContext(context, 8); 23 | let i8_pointer_type = LLVMPointerType(i8_type, 0); 24 | 25 | // declare that there's a `void log(i8*)` function in the environment 26 | // but don't provide a block (aka body) so that it in the wasm module 27 | // it'll be imported 28 | let log_func_type = LLVMFunctionType(void_type, [i8_pointer_type].as_ptr() as *mut _, 1, 0); 29 | let log_func = LLVMAddFunction(module, c_str!("log"), log_func_type); 30 | 31 | // our "main" function which we'll need to call explicitly from JavaScript 32 | // after we've instantiated the WebAssembly.Instance 33 | let main_func_type = LLVMFunctionType(void_type, ptr::null_mut(), 0, 0); 34 | let main_func = LLVMAddFunction(module, c_str!("main"), main_func_type); 35 | let main_block = LLVMAppendBasicBlockInContext(context, main_func, c_str!("main")); 36 | LLVMPositionBuilderAtEnd(builder, main_block); 37 | 38 | // main's function body 39 | let hello_world_str = LLVMBuildGlobalStringPtr(builder, c_str!("hello, world."), c_str!("")); 40 | let log_args = [hello_world_str].as_ptr() as *mut _; 41 | // calling `log("hello, world.")` 42 | LLVMBuildCall(builder, log_func, log_args, 1, c_str!("")); 43 | LLVMBuildRetVoid(builder); 44 | 45 | // write our bitcode file 46 | LLVMSetTarget(module, c_str!("wasm32-unknown-unknown-wasm")); 47 | LLVMWriteBitcodeToFile(module, c_str!("main.bc")); 48 | 49 | // clean up 50 | LLVMDisposeBuilder(builder); 51 | LLVMDisposeModule(module); 52 | LLVMContextDispose(context); 53 | } 54 | } 55 | 56 | -------------------------------------------------------------------------------- /offical-demo/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "well" 3 | version = "0.1.0" 4 | authors = ["Phodal Huang "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | llvm-sys-100 = { package = "llvm-sys", version = "100", optional = true } 11 | inkwell = { git = "https://github.com/TheDan64/inkwell", branch = "master", features = ["llvm10-0"] } 12 | -------------------------------------------------------------------------------- /offical-demo/README.md: -------------------------------------------------------------------------------- 1 | # Kaleidoscope 2 | 3 | This example shows how one can implement the [Kaleidoscope programming language](https://llvm.org/docs/tutorial/index.html) using Inkwell. 4 | It implements every feature up to the [7th chapter](https://llvm.org/docs/tutorial/LangImpl07.html). 5 | 6 | When running this example (using the `cargo run --example kaleidoscope` command), a prompt will be displayed; for example: 7 | 8 | ``` 9 | ?> 1 + 1 10 | => 2 11 | 12 | ?> var a = 5, b = 10 in a * b 13 | => 50 14 | 15 | ?> def fib(n) if n < 2 then n else fib(n - 1) + fib(n - 2) 16 | 17 | ?> fib(40) 18 | => 102334155 19 | 20 | ?> 21 | ``` 22 | 23 | Additional arguments can be passed to the produced executable: 24 | - `--dc`: **D**isplay **C**ompiler output 25 | - `--dp`: **D**isplay **P**arser output 26 | - `--dl`: **D**isplay **L**exer output 27 | 28 | For example, running with all three switches may lead to the following output: 29 | ``` 30 | ?> 1 + 2 * 2 31 | -> Attempting to parse lexed input: 32 | [Number(1), Op('+'), Number(2), Op('*'), Number(2)] 33 | 34 | -> Expression parsed: 35 | Binary { op: '+', left: Number(1), right: Binary { op: '*', left: Number(2), right: Number(2) } } 36 | 37 | -> Expression compiled to IR: 38 | define double @anonymous() { 39 | entry: 40 | ret double 5.000000e+00 41 | } 42 | 43 | => 5 44 | ``` 45 | 46 | Finally, the prompt can be exited by entering "exit" or "quit". 47 | -------------------------------------------------------------------------------- /offical-demo/src/main.rs: -------------------------------------------------------------------------------- 1 | //! This is an example of the [Kaleidoscope tutorial](https://llvm.org/docs/tutorial/) 2 | //! made in Rust, using Inkwell. 3 | //! Currently, all features up to the [7th chapter](https://llvm.org/docs/tutorial/LangImpl07.html) 4 | //! are available. 5 | //! This example is supposed to be ran as a executable, which launches a REPL. 6 | //! The source code is in the following order: 7 | //! - Lexer, 8 | //! - Parser, 9 | //! - Compiler, 10 | //! - Program. 11 | //! 12 | //! Both the `Parser` and the `Compiler` may fail, in which case they would return 13 | //! an error represented by `Result`, for easier error reporting. 14 | 15 | extern crate inkwell; 16 | 17 | use std::borrow::Borrow; 18 | use std::collections::HashMap; 19 | use std::io::{self, Write}; 20 | use std::iter::Peekable; 21 | use std::str::Chars; 22 | use std::ops::DerefMut; 23 | 24 | use self::inkwell::builder::Builder; 25 | use self::inkwell::context::Context; 26 | use self::inkwell::module::Module; 27 | use self::inkwell::passes::PassManager; 28 | use self::inkwell::types::BasicTypeEnum; 29 | use self::inkwell::values::{BasicValue, BasicValueEnum, FloatValue, FunctionValue, PointerValue}; 30 | use self::inkwell::{OptimizationLevel, FloatPredicate}; 31 | 32 | use crate::Token::*; 33 | 34 | const ANONYMOUS_FUNCTION_NAME: &str = "anonymous"; 35 | 36 | 37 | // ====================================================================================== 38 | // LEXER ================================================================================ 39 | // ====================================================================================== 40 | 41 | /// Represents a primitive syntax token. 42 | #[derive(Debug, Clone)] 43 | pub enum Token { 44 | Binary, 45 | Comma, 46 | Comment, 47 | Def, 48 | Else, 49 | EOF, 50 | Extern, 51 | For, 52 | Ident(String), 53 | If, 54 | In, 55 | LParen, 56 | Number(f64), 57 | Op(char), 58 | RParen, 59 | Then, 60 | Unary, 61 | Var 62 | } 63 | 64 | /// Defines an error encountered by the `Lexer`. 65 | pub struct LexError { 66 | pub error: &'static str, 67 | pub index: usize 68 | } 69 | 70 | impl LexError { 71 | pub fn new(msg: &'static str) -> LexError { 72 | LexError { error: msg, index: 0 } 73 | } 74 | 75 | pub fn with_index(msg: &'static str, index: usize) -> LexError { 76 | LexError { error: msg, index: index } 77 | } 78 | } 79 | 80 | /// Defines the result of a lexing operation; namely a 81 | /// `Token` on success, or a `LexError` on failure. 82 | pub type LexResult = Result; 83 | 84 | /// Defines a lexer which transforms an input `String` into 85 | /// a `Token` stream. 86 | pub struct Lexer<'a> { 87 | input: &'a str, 88 | chars: Box>>, 89 | pos: usize 90 | } 91 | 92 | impl<'a> Lexer<'a> { 93 | /// Creates a new `Lexer`, given its source `input`. 94 | pub fn new(input: &'a str) -> Lexer<'a> { 95 | Lexer { input, chars: Box::new(input.chars().peekable()), pos: 0 } 96 | } 97 | 98 | /// Lexes and returns the next `Token` from the source code. 99 | pub fn lex(&mut self) -> LexResult { 100 | let chars = self.chars.deref_mut(); 101 | let src = self.input; 102 | 103 | let mut pos = self.pos; 104 | 105 | // Skip whitespaces 106 | loop { 107 | // Note: the following lines are in their own scope to 108 | // limit how long 'chars' is borrowed, and in order to allow 109 | // it to be borrowed again in the loop by 'chars.next()'. 110 | { 111 | let ch = chars.peek(); 112 | 113 | if ch.is_none() { 114 | self.pos = pos; 115 | 116 | return Ok(Token::EOF); 117 | } 118 | 119 | if !ch.unwrap().is_whitespace() { 120 | break; 121 | } 122 | } 123 | 124 | chars.next(); 125 | pos += 1; 126 | } 127 | 128 | let start = pos; 129 | let next = chars.next(); 130 | 131 | if next.is_none() { 132 | return Ok(Token::EOF); 133 | } 134 | 135 | pos += 1; 136 | 137 | // Actually get the next token. 138 | let result = match next.unwrap() { 139 | '(' => Ok(Token::LParen), 140 | ')' => Ok(Token::RParen), 141 | ',' => Ok(Token::Comma), 142 | 143 | '#' => { 144 | // Comment 145 | loop { 146 | let ch = chars.next(); 147 | pos += 1; 148 | 149 | if ch == Some('\n') { 150 | break; 151 | } 152 | } 153 | 154 | Ok(Token::Comment) 155 | }, 156 | 157 | '.' | '0' ..= '9' => { 158 | // Parse number literal 159 | loop { 160 | let ch = match chars.peek() { 161 | Some(ch) => *ch, 162 | None => return Ok(Token::EOF) 163 | }; 164 | 165 | // Parse float. 166 | if ch != '.' && !ch.is_digit(16) { 167 | break; 168 | } 169 | 170 | chars.next(); 171 | pos += 1; 172 | } 173 | 174 | Ok(Token::Number(src[start..pos].parse().unwrap())) 175 | }, 176 | 177 | 'a' ..= 'z' | 'A' ..= 'Z' | '_' => { 178 | // Parse identifier 179 | loop { 180 | let ch = match chars.peek() { 181 | Some(ch) => *ch, 182 | None => return Ok(Token::EOF) 183 | }; 184 | 185 | // A word-like identifier only contains underscores and alphanumeric characters. 186 | if ch != '_' && !ch.is_alphanumeric() { 187 | break; 188 | } 189 | 190 | chars.next(); 191 | pos += 1; 192 | } 193 | 194 | match &src[start..pos] { 195 | "def" => Ok(Token::Def), 196 | "extern" => Ok(Token::Extern), 197 | "if" => Ok(Token::If), 198 | "then" => Ok(Token::Then), 199 | "else" => Ok(Token::Else), 200 | "for" => Ok(Token::For), 201 | "in" => Ok(Token::In), 202 | "unary" => Ok(Token::Unary), 203 | "binary" => Ok(Token::Binary), 204 | "var" => Ok(Token::Var), 205 | 206 | ident => Ok(Token::Ident(ident.to_string())) 207 | } 208 | }, 209 | 210 | op => { 211 | // Parse operator 212 | Ok(Token::Op(op)) 213 | } 214 | }; 215 | 216 | // Update stored position, and return 217 | self.pos = pos; 218 | 219 | result 220 | } 221 | } 222 | 223 | impl<'a> Iterator for Lexer<'a> { 224 | type Item = Token; 225 | 226 | /// Lexes the next `Token` and returns it. 227 | /// On EOF or failure, `None` will be returned. 228 | fn next(&mut self) -> Option { 229 | match self.lex() { 230 | Ok(EOF) | Err(_) => None, 231 | Ok(token) => Some(token) 232 | } 233 | } 234 | } 235 | 236 | 237 | // ====================================================================================== 238 | // PARSER =============================================================================== 239 | // ====================================================================================== 240 | 241 | /// Defines a primitive expression. 242 | #[derive(Debug)] 243 | pub enum Expr { 244 | Binary { 245 | op: char, 246 | left: Box, 247 | right: Box 248 | }, 249 | 250 | Call { 251 | fn_name: String, 252 | args: Vec 253 | }, 254 | 255 | Conditional { 256 | cond: Box, 257 | consequence: Box, 258 | alternative: Box 259 | }, 260 | 261 | For { 262 | var_name: String, 263 | start: Box, 264 | end: Box, 265 | step: Option>, 266 | body: Box 267 | }, 268 | 269 | Number(f64), 270 | 271 | Variable(String), 272 | 273 | VarIn { 274 | variables: Vec<(String, Option)>, 275 | body: Box 276 | } 277 | } 278 | 279 | /// Defines the prototype (name and parameters) of a function. 280 | #[derive(Debug)] 281 | pub struct Prototype { 282 | pub name: String, 283 | pub args: Vec, 284 | pub is_op: bool, 285 | pub prec: usize 286 | } 287 | 288 | /// Defines a user-defined or external function. 289 | #[derive(Debug)] 290 | pub struct Function { 291 | pub prototype: Prototype, 292 | pub body: Option, 293 | pub is_anon: bool 294 | } 295 | 296 | /// Represents the `Expr` parser. 297 | pub struct Parser<'a> { 298 | tokens: Vec, 299 | pos: usize, 300 | prec: &'a mut HashMap 301 | } 302 | 303 | // I'm ignoring the 'must_use' lint in order to call 'self.advance' without checking 304 | // the result when an EOF is acceptable. 305 | #[allow(unused_must_use)] 306 | impl<'a> Parser<'a> { 307 | /// Creates a new parser, given an input `str` and a `HashMap` binding 308 | /// an operator and its precedence in binary expressions. 309 | pub fn new(input: String, op_precedence: &'a mut HashMap) -> Self { 310 | let mut lexer = Lexer::new(input.as_str()); 311 | let tokens = lexer.by_ref().collect(); 312 | 313 | Parser { 314 | tokens: tokens, 315 | prec: op_precedence, 316 | pos: 0 317 | } 318 | } 319 | 320 | /// Parses the content of the parser. 321 | pub fn parse(&mut self) -> Result { 322 | let result = match self.current()? { 323 | Def => self.parse_def(), 324 | Extern => self.parse_extern(), 325 | _ => self.parse_toplevel_expr() 326 | }; 327 | 328 | match result { 329 | Ok(result) => { 330 | if !self.at_end() { 331 | Err("Unexpected token after parsed expression.") 332 | } else { 333 | Ok(result) 334 | } 335 | }, 336 | 337 | err => err 338 | } 339 | } 340 | 341 | /// Returns the current `Token`, without performing safety checks beforehand. 342 | fn curr(&self) -> Token { 343 | self.tokens[self.pos].clone() 344 | } 345 | 346 | /// Returns the current `Token`, or an error that 347 | /// indicates that the end of the file has been unexpectedly reached if it is the case. 348 | fn current(&self) -> Result { 349 | if self.pos >= self.tokens.len() { 350 | Err("Unexpected end of file.") 351 | } else { 352 | Ok(self.tokens[self.pos].clone()) 353 | } 354 | } 355 | 356 | /// Advances the position, and returns an empty `Result` whose error 357 | /// indicates that the end of the file has been unexpectedly reached. 358 | /// This allows to use the `self.advance()?;` syntax. 359 | fn advance(&mut self) -> Result<(), &'static str> { 360 | let npos = self.pos + 1; 361 | 362 | self.pos = npos; 363 | 364 | if npos < self.tokens.len() { 365 | Ok(()) 366 | } else { 367 | Err("Unexpected end of file.") 368 | } 369 | } 370 | 371 | /// Returns a value indicating whether or not the `Parser` 372 | /// has reached the end of the input. 373 | fn at_end(&self) -> bool { 374 | self.pos >= self.tokens.len() 375 | } 376 | 377 | /// Returns the precedence of the current `Token`, or 0 if it is not recognized as a binary operator. 378 | fn get_tok_precedence(&self) -> i32 { 379 | if let Ok(Op(op)) = self.current() { 380 | *self.prec.get(&op).unwrap_or(&100) 381 | } else { 382 | -1 383 | } 384 | } 385 | 386 | /// Parses the prototype of a function, whether external or user-defined. 387 | fn parse_prototype(&mut self) -> Result { 388 | let (id, is_operator, precedence) = match self.curr() { 389 | Ident(id) => { 390 | self.advance()?; 391 | 392 | (id, false, 0) 393 | }, 394 | 395 | Binary => { 396 | self.advance()?; 397 | 398 | let op = match self.curr() { 399 | Op(ch) => ch, 400 | _ => return Err("Expected operator in custom operator declaration.") 401 | }; 402 | 403 | self.advance()?; 404 | 405 | let mut name = String::from("binary"); 406 | 407 | name.push(op); 408 | 409 | let prec = if let Number(prec) = self.curr() { 410 | self.advance()?; 411 | 412 | prec as usize 413 | } else { 414 | 0 415 | }; 416 | 417 | self.prec.insert(op, prec as i32); 418 | 419 | (name, true, prec) 420 | }, 421 | 422 | Unary => { 423 | self.advance()?; 424 | 425 | let op = match self.curr() { 426 | Op(ch) => ch, 427 | _ => return Err("Expected operator in custom operator declaration.") 428 | }; 429 | 430 | let mut name = String::from("unary"); 431 | 432 | name.push(op); 433 | 434 | self.advance()?; 435 | 436 | (name, true, 0) 437 | }, 438 | 439 | _ => return Err("Expected identifier in prototype declaration.") 440 | }; 441 | 442 | match self.curr() { 443 | LParen => (), 444 | _ => return Err("Expected '(' character in prototype declaration.") 445 | } 446 | 447 | self.advance()?; 448 | 449 | if let RParen = self.curr() { 450 | self.advance(); 451 | 452 | return Ok(Prototype { 453 | name: id, 454 | args: vec![], 455 | is_op: is_operator, 456 | prec: precedence 457 | }); 458 | } 459 | 460 | let mut args = vec![]; 461 | 462 | loop { 463 | match self.curr() { 464 | Ident(name) => args.push(name), 465 | _ => return Err("Expected identifier in parameter declaration.") 466 | } 467 | 468 | self.advance()?; 469 | 470 | match self.curr() { 471 | RParen => { 472 | self.advance(); 473 | break; 474 | }, 475 | Comma => { 476 | self.advance(); 477 | }, 478 | _ => return Err("Expected ',' or ')' character in prototype declaration.") 479 | } 480 | } 481 | 482 | Ok(Prototype { 483 | name: id, 484 | args: args, 485 | is_op: is_operator, 486 | prec: precedence 487 | }) 488 | } 489 | 490 | /// Parses a user-defined function. 491 | fn parse_def(&mut self) -> Result { 492 | // Eat 'def' keyword 493 | self.pos += 1; 494 | 495 | // Parse signature of function 496 | let proto = self.parse_prototype()?; 497 | 498 | // Parse body of function 499 | let body = self.parse_expr()?; 500 | 501 | // Return new function 502 | Ok(Function { 503 | prototype: proto, 504 | body: Some(body), 505 | is_anon: false 506 | }) 507 | } 508 | 509 | /// Parses an external function declaration. 510 | fn parse_extern(&mut self) -> Result { 511 | // Eat 'extern' keyword 512 | self.pos += 1; 513 | 514 | // Parse signature of extern function 515 | let proto = self.parse_prototype()?; 516 | 517 | Ok(Function { 518 | prototype: proto, 519 | body: None, 520 | is_anon: false 521 | }) 522 | } 523 | 524 | /// Parses any expression. 525 | fn parse_expr(&mut self) -> Result { 526 | match self.parse_unary_expr() { 527 | Ok(left) => self.parse_binary_expr(0, left), 528 | err => err 529 | } 530 | } 531 | 532 | /// Parses a literal number. 533 | fn parse_nb_expr(&mut self) -> Result { 534 | // Simply convert Token::Number to Expr::Number 535 | match self.curr() { 536 | Number(nb) => { 537 | self.advance(); 538 | Ok(Expr::Number(nb)) 539 | }, 540 | _ => Err("Expected number literal.") 541 | } 542 | } 543 | 544 | /// Parses an expression enclosed in parenthesis. 545 | fn parse_paren_expr(&mut self) -> Result { 546 | match self.current()? { 547 | LParen => (), 548 | _ => return Err("Expected '(' character at start of parenthesized expression.") 549 | } 550 | 551 | self.advance()?; 552 | 553 | let expr = self.parse_expr()?; 554 | 555 | match self.current()? { 556 | RParen => (), 557 | _ => return Err("Expected ')' character at end of parenthesized expression.") 558 | } 559 | 560 | self.advance(); 561 | 562 | Ok(expr) 563 | } 564 | 565 | /// Parses an expression that starts with an identifier (either a variable or a function call). 566 | fn parse_id_expr(&mut self) -> Result { 567 | let id = match self.curr() { 568 | Ident(id) => id, 569 | _ => return Err("Expected identifier.") 570 | }; 571 | 572 | if self.advance().is_err() { 573 | return Ok(Expr::Variable(id)); 574 | } 575 | 576 | match self.curr() { 577 | LParen => { 578 | self.advance()?; 579 | 580 | if let RParen = self.curr() { 581 | return Ok(Expr::Call { fn_name: id, args: vec![] }); 582 | } 583 | 584 | let mut args = vec![]; 585 | 586 | loop { 587 | args.push(self.parse_expr()?); 588 | 589 | match self.current()? { 590 | Comma => (), 591 | RParen => break, 592 | _ => return Err("Expected ',' character in function call.") 593 | } 594 | 595 | self.advance()?; 596 | } 597 | 598 | self.advance(); 599 | 600 | Ok(Expr::Call { fn_name: id, args: args }) 601 | }, 602 | 603 | _ => Ok(Expr::Variable(id)) 604 | } 605 | } 606 | 607 | /// Parses an unary expression. 608 | fn parse_unary_expr(&mut self) -> Result { 609 | let op = match self.current()? { 610 | Op(ch) => { 611 | self.advance()?; 612 | ch 613 | }, 614 | _ => return self.parse_primary() 615 | }; 616 | 617 | let mut name = String::from("unary"); 618 | 619 | name.push(op); 620 | 621 | Ok(Expr::Call { 622 | fn_name: name, 623 | args: vec![ self.parse_unary_expr()? ] 624 | }) 625 | } 626 | 627 | /// Parses a binary expression, given its left-hand expression. 628 | fn parse_binary_expr(&mut self, prec: i32, mut left: Expr) -> Result { 629 | loop { 630 | let curr_prec = self.get_tok_precedence(); 631 | 632 | if curr_prec < prec || self.at_end() { 633 | return Ok(left); 634 | } 635 | 636 | let op = match self.curr() { 637 | Op(op) => op, 638 | _ => return Err("Invalid operator.") 639 | }; 640 | 641 | self.advance()?; 642 | 643 | let mut right = self.parse_unary_expr()?; 644 | 645 | let next_prec = self.get_tok_precedence(); 646 | 647 | if curr_prec < next_prec { 648 | right = self.parse_binary_expr(curr_prec + 1, right)?; 649 | } 650 | 651 | left = Expr::Binary { 652 | op: op, 653 | left: Box::new(left), 654 | right: Box::new(right) 655 | }; 656 | } 657 | } 658 | 659 | /// Parses a conditional if..then..else expression. 660 | fn parse_conditional_expr(&mut self) -> Result { 661 | // eat 'if' token 662 | self.advance()?; 663 | 664 | let cond = self.parse_expr()?; 665 | 666 | // eat 'then' token 667 | match self.current() { 668 | Ok(Then) => self.advance()?, 669 | _ => return Err("Expected 'then' keyword.") 670 | } 671 | 672 | let then = self.parse_expr()?; 673 | 674 | // eat 'else' token 675 | match self.current() { 676 | Ok(Else) => self.advance()?, 677 | _ => return Err("Expected 'else' keyword.") 678 | } 679 | 680 | let otherwise = self.parse_expr()?; 681 | 682 | Ok(Expr::Conditional { 683 | cond: Box::new(cond), 684 | consequence: Box::new(then), 685 | alternative: Box::new(otherwise) 686 | }) 687 | } 688 | 689 | /// Parses a loop for..in.. expression. 690 | fn parse_for_expr(&mut self) -> Result { 691 | // eat 'for' token 692 | self.advance()?; 693 | 694 | let name = match self.curr() { 695 | Ident(n) => n, 696 | _ => return Err("Expected identifier in for loop.") 697 | }; 698 | 699 | // eat identifier 700 | self.advance()?; 701 | 702 | // eat '=' token 703 | match self.curr() { 704 | Op('=') => self.advance()?, 705 | _ => return Err("Expected '=' character in for loop.") 706 | } 707 | 708 | let start = self.parse_expr()?; 709 | 710 | // eat ',' token 711 | match self.current()? { 712 | Comma => self.advance()?, 713 | _ => return Err("Expected ',' character in for loop.") 714 | } 715 | 716 | let end = self.parse_expr()?; 717 | 718 | // parse (optional) step expression 719 | let step = match self.current()? { 720 | Comma => { 721 | self.advance()?; 722 | 723 | Some(self.parse_expr()?) 724 | }, 725 | 726 | _ => None 727 | }; 728 | 729 | // eat 'in' token 730 | match self.current()? { 731 | In => self.advance()?, 732 | _ => return Err("Expected 'in' keyword in for loop.") 733 | } 734 | 735 | let body = self.parse_expr()?; 736 | 737 | Ok(Expr::For { 738 | var_name: name, 739 | start: Box::new(start), 740 | end: Box::new(end), 741 | step: step.map(Box::new), 742 | body: Box::new(body) 743 | }) 744 | } 745 | 746 | /// Parses a var..in expression. 747 | fn parse_var_expr(&mut self) -> Result { 748 | // eat 'var' token 749 | self.advance()?; 750 | 751 | let mut variables = Vec::new(); 752 | 753 | // parse variables 754 | loop { 755 | let name = match self.curr() { 756 | Ident(name) => name, 757 | _ => return Err("Expected identifier in 'var..in' declaration.") 758 | }; 759 | 760 | self.advance()?; 761 | 762 | // read (optional) initializer 763 | let initializer = match self.curr() { 764 | Op('=') => Some({ 765 | self.advance()?; 766 | self.parse_expr()? 767 | }), 768 | 769 | _ => None 770 | }; 771 | 772 | variables.push((name, initializer)); 773 | 774 | match self.curr() { 775 | Comma => { 776 | self.advance()?; 777 | }, 778 | In => { 779 | self.advance()?; 780 | break; 781 | } 782 | _ => { 783 | return Err("Expected comma or 'in' keyword in variable declaration.") 784 | } 785 | } 786 | } 787 | 788 | // parse body 789 | let body = self.parse_expr()?; 790 | 791 | Ok(Expr::VarIn { 792 | variables: variables, 793 | body: Box::new(body) 794 | }) 795 | } 796 | 797 | /// Parses a primary expression (an identifier, a number or a parenthesized expression). 798 | fn parse_primary(&mut self) -> Result { 799 | match self.curr() { 800 | Ident(_) => self.parse_id_expr(), 801 | Number(_) => self.parse_nb_expr(), 802 | LParen => self.parse_paren_expr(), 803 | If => self.parse_conditional_expr(), 804 | For => self.parse_for_expr(), 805 | Var => self.parse_var_expr(), 806 | _ => Err("Unknown expression.") 807 | } 808 | } 809 | 810 | /// Parses a top-level expression and makes an anonymous function out of it, 811 | /// for easier compilation. 812 | fn parse_toplevel_expr(&mut self) -> Result { 813 | match self.parse_expr() { 814 | Ok(expr) => { 815 | Ok(Function { 816 | prototype: Prototype { 817 | name: ANONYMOUS_FUNCTION_NAME.to_string(), 818 | args: vec![], 819 | is_op: false, 820 | prec: 0 821 | 822 | }, 823 | body: Some(expr), 824 | is_anon: true 825 | }) 826 | }, 827 | 828 | Err(err) => Err(err) 829 | } 830 | } 831 | } 832 | 833 | 834 | // ====================================================================================== 835 | // COMPILER ============================================================================= 836 | // ====================================================================================== 837 | 838 | /// Defines the `Expr` compiler. 839 | pub struct Compiler<'a, 'ctx> { 840 | pub context: &'ctx Context, 841 | pub builder: &'a Builder<'ctx>, 842 | pub fpm: &'a PassManager>, 843 | pub module: &'a Module<'ctx>, 844 | pub function: &'a Function, 845 | 846 | variables: HashMap>, 847 | fn_value_opt: Option> 848 | } 849 | 850 | impl<'a, 'ctx> Compiler<'a, 'ctx> { 851 | /// Gets a defined function given its name. 852 | #[inline] 853 | fn get_function(&self, name: &str) -> Option> { 854 | self.module.get_function(name) 855 | } 856 | 857 | /// Returns the `FunctionValue` representing the function being compiled. 858 | #[inline] 859 | fn fn_value(&self) -> FunctionValue<'ctx> { 860 | self.fn_value_opt.unwrap() 861 | } 862 | 863 | /// Creates a new stack allocation instruction in the entry block of the function. 864 | fn create_entry_block_alloca(&self, name: &str) -> PointerValue<'ctx> { 865 | let builder = self.context.create_builder(); 866 | 867 | let entry = self.fn_value().get_first_basic_block().unwrap(); 868 | 869 | match entry.get_first_instruction() { 870 | Some(first_instr) => builder.position_before(&first_instr), 871 | None => builder.position_at_end(entry) 872 | } 873 | 874 | builder.build_alloca(self.context.f64_type(), name) 875 | } 876 | 877 | /// Compiles the specified `Expr` into an LLVM `FloatValue`. 878 | fn compile_expr(&mut self, expr: &Expr) -> Result, &'static str> { 879 | match *expr { 880 | Expr::Number(nb) => Ok(self.context.f64_type().const_float(nb)), 881 | 882 | Expr::Variable(ref name) => { 883 | match self.variables.get(name.as_str()) { 884 | Some(var) => Ok(self.builder.build_load(*var, name.as_str()).into_float_value()), 885 | None => Err("Could not find a matching variable.") 886 | } 887 | }, 888 | 889 | Expr::VarIn { ref variables, ref body } => { 890 | let mut old_bindings = Vec::new(); 891 | 892 | for &(ref var_name, ref initializer) in variables { 893 | let var_name = var_name.as_str(); 894 | 895 | let initial_val = match *initializer { 896 | Some(ref init) => self.compile_expr(init)?, 897 | None => self.context.f64_type().const_float(0.) 898 | }; 899 | 900 | let alloca = self.create_entry_block_alloca(var_name); 901 | 902 | self.builder.build_store(alloca, initial_val); 903 | 904 | if let Some(old_binding) = self.variables.remove(var_name) { 905 | old_bindings.push(old_binding); 906 | } 907 | 908 | self.variables.insert(var_name.to_string(), alloca); 909 | } 910 | 911 | let body = self.compile_expr(body)?; 912 | 913 | for binding in old_bindings { 914 | self.variables.insert(binding.get_name().to_str().unwrap().to_string(), binding); 915 | } 916 | 917 | Ok(body) 918 | }, 919 | 920 | Expr::Binary { op, ref left, ref right } => { 921 | if op == '=' { 922 | // handle assignement 923 | let var_name = match *left.borrow() { 924 | Expr::Variable(ref var_name) => var_name, 925 | _ => { 926 | return Err("Expected variable as left-hand operator of assignement."); 927 | } 928 | }; 929 | 930 | let var_val = self.compile_expr(right)?; 931 | let var = self.variables.get(var_name.as_str()).ok_or("Undefined variable.")?; 932 | 933 | self.builder.build_store(*var, var_val); 934 | 935 | Ok(var_val) 936 | } else { 937 | let lhs = self.compile_expr(left)?; 938 | let rhs = self.compile_expr(right)?; 939 | 940 | match op { 941 | '+' => Ok(self.builder.build_float_add(lhs, rhs, "tmpadd")), 942 | '-' => Ok(self.builder.build_float_sub(lhs, rhs, "tmpsub")), 943 | '*' => Ok(self.builder.build_float_mul(lhs, rhs, "tmpmul")), 944 | '/' => Ok(self.builder.build_float_div(lhs, rhs, "tmpdiv")), 945 | '<' => Ok({ 946 | let cmp = self.builder.build_float_compare(FloatPredicate::ULT, lhs, rhs, "tmpcmp"); 947 | 948 | self.builder.build_unsigned_int_to_float(cmp, self.context.f64_type(), "tmpbool") 949 | }), 950 | '>' => Ok({ 951 | let cmp = self.builder.build_float_compare(FloatPredicate::ULT, rhs, lhs, "tmpcmp"); 952 | 953 | self.builder.build_unsigned_int_to_float(cmp, self.context.f64_type(), "tmpbool") 954 | }), 955 | 956 | custom => { 957 | let mut name = String::from("binary"); 958 | 959 | name.push(custom); 960 | 961 | match self.get_function(name.as_str()) { 962 | Some(fun) => { 963 | match self.builder.build_call(fun, &[lhs.into(), rhs.into()], "tmpbin").try_as_basic_value().left() { 964 | Some(value) => Ok(value.into_float_value()), 965 | None => Err("Invalid call produced.") 966 | } 967 | }, 968 | 969 | None => Err("Undefined binary operator.") 970 | } 971 | } 972 | } 973 | } 974 | }, 975 | 976 | Expr::Call { ref fn_name, ref args } => { 977 | match self.get_function(fn_name.as_str()) { 978 | Some(fun) => { 979 | let mut compiled_args = Vec::with_capacity(args.len()); 980 | 981 | for arg in args { 982 | compiled_args.push(self.compile_expr(arg)?); 983 | } 984 | 985 | let argsv: Vec = compiled_args.iter().by_ref().map(|&val| val.into()).collect(); 986 | 987 | match self.builder.build_call(fun, argsv.as_slice(), "tmp").try_as_basic_value().left() { 988 | Some(value) => Ok(value.into_float_value()), 989 | None => Err("Invalid call produced.") 990 | } 991 | }, 992 | None => Err("Unknown function.") 993 | } 994 | }, 995 | 996 | Expr::Conditional { ref cond, ref consequence, ref alternative } => { 997 | let parent = self.fn_value(); 998 | let zero_const = self.context.f64_type().const_float(0.0); 999 | 1000 | // create condition by comparing without 0.0 and returning an int 1001 | let cond = self.compile_expr(cond)?; 1002 | let cond = self.builder.build_float_compare(FloatPredicate::ONE, cond, zero_const, "ifcond"); 1003 | 1004 | // build branch 1005 | let then_bb = self.context.append_basic_block(parent, "then"); 1006 | let else_bb = self.context.append_basic_block(parent, "else"); 1007 | let cont_bb = self.context.append_basic_block(parent, "ifcont"); 1008 | 1009 | self.builder.build_conditional_branch(cond, then_bb, else_bb); 1010 | 1011 | // build then block 1012 | self.builder.position_at_end(then_bb); 1013 | let then_val = self.compile_expr(consequence)?; 1014 | self.builder.build_unconditional_branch(cont_bb); 1015 | 1016 | let then_bb = self.builder.get_insert_block().unwrap(); 1017 | 1018 | // build else block 1019 | self.builder.position_at_end(else_bb); 1020 | let else_val = self.compile_expr(alternative)?; 1021 | self.builder.build_unconditional_branch(cont_bb); 1022 | 1023 | let else_bb = self.builder.get_insert_block().unwrap(); 1024 | 1025 | // emit merge block 1026 | self.builder.position_at_end(cont_bb); 1027 | 1028 | let phi = self.builder.build_phi(self.context.f64_type(), "iftmp"); 1029 | 1030 | phi.add_incoming(&[ 1031 | (&then_val, then_bb), 1032 | (&else_val, else_bb) 1033 | ]); 1034 | 1035 | Ok(phi.as_basic_value().into_float_value()) 1036 | }, 1037 | 1038 | Expr::For { ref var_name, ref start, ref end, ref step, ref body } => { 1039 | let parent = self.fn_value(); 1040 | 1041 | let start_alloca = self.create_entry_block_alloca(var_name); 1042 | let start = self.compile_expr(start)?; 1043 | 1044 | self.builder.build_store(start_alloca, start); 1045 | 1046 | // go from current block to loop block 1047 | let loop_bb = self.context.append_basic_block(parent, "loop"); 1048 | 1049 | self.builder.build_unconditional_branch(loop_bb); 1050 | self.builder.position_at_end(loop_bb); 1051 | 1052 | let old_val = self.variables.remove(var_name.as_str()); 1053 | 1054 | self.variables.insert(var_name.to_owned(), start_alloca); 1055 | 1056 | // emit body 1057 | self.compile_expr(body)?; 1058 | 1059 | // emit step 1060 | let step = match *step { 1061 | Some(ref step) => self.compile_expr(step)?, 1062 | None => self.context.f64_type().const_float(1.0) 1063 | }; 1064 | 1065 | // compile end condition 1066 | let end_cond = self.compile_expr(end)?; 1067 | 1068 | let curr_var = self.builder.build_load(start_alloca, var_name); 1069 | let next_var = self.builder.build_float_add(curr_var.into_float_value(), step, "nextvar"); 1070 | 1071 | self.builder.build_store(start_alloca, next_var); 1072 | 1073 | let end_cond = self.builder.build_float_compare(FloatPredicate::ONE, end_cond, self.context.f64_type().const_float(0.0), "loopcond"); 1074 | let after_bb = self.context.append_basic_block(parent, "afterloop"); 1075 | 1076 | self.builder.build_conditional_branch(end_cond, loop_bb, after_bb); 1077 | self.builder.position_at_end(after_bb); 1078 | 1079 | self.variables.remove(var_name); 1080 | 1081 | if let Some(val) = old_val { 1082 | self.variables.insert(var_name.to_owned(), val); 1083 | } 1084 | 1085 | Ok(self.context.f64_type().const_float(0.0)) 1086 | } 1087 | } 1088 | } 1089 | 1090 | /// Compiles the specified `Prototype` into an extern LLVM `FunctionValue`. 1091 | fn compile_prototype(&self, proto: &Prototype) -> Result, &'static str> { 1092 | let ret_type = self.context.f64_type(); 1093 | let args_types = std::iter::repeat(ret_type) 1094 | .take(proto.args.len()) 1095 | .map(|f| f.into()) 1096 | .collect::>(); 1097 | let args_types = args_types.as_slice(); 1098 | 1099 | let fn_type = self.context.f64_type().fn_type(args_types, false); 1100 | let fn_val = self.module.add_function(proto.name.as_str(), fn_type, None); 1101 | 1102 | // set arguments names 1103 | for (i, arg) in fn_val.get_param_iter().enumerate() { 1104 | arg.into_float_value().set_name(proto.args[i].as_str()); 1105 | } 1106 | 1107 | // finally return built prototype 1108 | Ok(fn_val) 1109 | } 1110 | 1111 | /// Compiles the specified `Function` into an LLVM `FunctionValue`. 1112 | fn compile_fn(&mut self) -> Result, &'static str> { 1113 | let proto = &self.function.prototype; 1114 | let function = self.compile_prototype(proto)?; 1115 | 1116 | // got external function, returning only compiled prototype 1117 | if self.function.body.is_none() { 1118 | return Ok(function); 1119 | } 1120 | 1121 | let entry = self.context.append_basic_block(function, "entry"); 1122 | 1123 | self.builder.position_at_end(entry); 1124 | 1125 | // update fn field 1126 | self.fn_value_opt = Some(function); 1127 | 1128 | // build variables map 1129 | self.variables.reserve(proto.args.len()); 1130 | 1131 | for (i, arg) in function.get_param_iter().enumerate() { 1132 | let arg_name = proto.args[i].as_str(); 1133 | let alloca = self.create_entry_block_alloca(arg_name); 1134 | 1135 | self.builder.build_store(alloca, arg); 1136 | 1137 | self.variables.insert(proto.args[i].clone(), alloca); 1138 | } 1139 | 1140 | // compile body 1141 | let body = self.compile_expr(self.function.body.as_ref().unwrap())?; 1142 | 1143 | self.builder.build_return(Some(&body)); 1144 | 1145 | // return the whole thing after verification and optimization 1146 | if function.verify(true) { 1147 | self.fpm.run_on(&function); 1148 | 1149 | Ok(function) 1150 | } else { 1151 | unsafe { 1152 | function.delete(); 1153 | } 1154 | 1155 | Err("Invalid generated function.") 1156 | } 1157 | } 1158 | 1159 | /// Compiles the specified `Function` in the given `Context` and using the specified `Builder`, `PassManager`, and `Module`. 1160 | pub fn compile( 1161 | context: &'ctx Context, 1162 | builder: &'a Builder<'ctx>, 1163 | pass_manager: &'a PassManager>, 1164 | module: &'a Module<'ctx>, 1165 | function: &Function, 1166 | ) -> Result, &'static str> { 1167 | let mut compiler = Compiler { 1168 | context: context, 1169 | builder: builder, 1170 | fpm: pass_manager, 1171 | module: module, 1172 | function: function, 1173 | fn_value_opt: None, 1174 | variables: HashMap::new() 1175 | }; 1176 | 1177 | compiler.compile_fn() 1178 | } 1179 | } 1180 | 1181 | 1182 | // ====================================================================================== 1183 | // PROGRAM ============================================================================== 1184 | // ====================================================================================== 1185 | 1186 | 1187 | // macro used to print & flush without printing a new line 1188 | macro_rules! print_flush { 1189 | ( $( $x:expr ),* ) => { 1190 | print!( $($x, )* ); 1191 | 1192 | std::io::stdout().flush().expect("Could not flush to standard output."); 1193 | }; 1194 | } 1195 | 1196 | #[no_mangle] 1197 | pub extern fn putchard(x: f64) -> f64 { 1198 | print_flush!("{}", x as u8 as char); 1199 | x 1200 | } 1201 | 1202 | #[no_mangle] 1203 | pub extern fn printd(x: f64) -> f64 { 1204 | println!("{}", x); 1205 | x 1206 | 1207 | } 1208 | 1209 | // Adding the functions above to a global array, 1210 | // so Rust compiler won't remove them. 1211 | #[used] 1212 | static EXTERNAL_FNS: [extern fn(f64) -> f64; 2] = [putchard, printd]; 1213 | 1214 | /// Entry point of the program; acts as a REPL. 1215 | pub fn main() { 1216 | // use self::inkwell::support::add_symbol; 1217 | let mut display_lexer_output = false; 1218 | let mut display_parser_output = false; 1219 | let mut display_compiler_output = false; 1220 | 1221 | for arg in std::env::args() { 1222 | match arg.as_str() { 1223 | "--dl" => display_lexer_output = true, 1224 | "--dp" => display_parser_output = true, 1225 | "--dc" => display_compiler_output = true, 1226 | _ => () 1227 | } 1228 | } 1229 | 1230 | let context = Context::create(); 1231 | let module = context.create_module("repl"); 1232 | let builder = context.create_builder(); 1233 | 1234 | // Create FPM 1235 | let fpm = PassManager::create(&module); 1236 | 1237 | fpm.add_instruction_combining_pass(); 1238 | fpm.add_reassociate_pass(); 1239 | fpm.add_gvn_pass(); 1240 | fpm.add_cfg_simplification_pass(); 1241 | fpm.add_basic_alias_analysis_pass(); 1242 | fpm.add_promote_memory_to_register_pass(); 1243 | fpm.add_instruction_combining_pass(); 1244 | fpm.add_reassociate_pass(); 1245 | 1246 | fpm.initialize(); 1247 | 1248 | let mut previous_exprs = Vec::new(); 1249 | 1250 | loop { 1251 | println!(); 1252 | print_flush!("?> "); 1253 | 1254 | // Read input from stdin 1255 | let mut input = String::new(); 1256 | io::stdin().read_line(&mut input).expect("Could not read from standard input."); 1257 | 1258 | if input.starts_with("exit") || input.starts_with("quit") { 1259 | break; 1260 | } else if input.chars().all(char::is_whitespace) { 1261 | continue; 1262 | } 1263 | 1264 | // Build precedence map 1265 | let mut prec = HashMap::with_capacity(6); 1266 | 1267 | prec.insert('=', 2); 1268 | prec.insert('<', 10); 1269 | prec.insert('+', 20); 1270 | prec.insert('-', 20); 1271 | prec.insert('*', 40); 1272 | prec.insert('/', 40); 1273 | 1274 | // Parse and (optionally) display input 1275 | if display_lexer_output { 1276 | println!("-> Attempting to parse lexed input: \n{:?}\n", Lexer::new(input.as_str()).collect::>()); 1277 | } 1278 | 1279 | // make module 1280 | let module = context.create_module("tmp"); 1281 | 1282 | // recompile every previously parsed function into the new module 1283 | for prev in &previous_exprs { 1284 | Compiler::compile(&context, &builder, &fpm, &module, prev).expect("Cannot re-add previously compiled function."); 1285 | } 1286 | 1287 | let (name, is_anonymous) = match Parser::new(input, &mut prec).parse() { 1288 | Ok(fun) => { 1289 | let is_anon = fun.is_anon; 1290 | 1291 | if display_parser_output { 1292 | if is_anon { 1293 | println!("-> Expression parsed: \n{:?}\n", fun.body); 1294 | } else { 1295 | println!("-> Function parsed: \n{:?}\n", fun); 1296 | } 1297 | } 1298 | 1299 | match Compiler::compile(&context, &builder, &fpm, &module, &fun) { 1300 | Ok(function) => { 1301 | if display_compiler_output { 1302 | // Not printing a new line since LLVM automatically 1303 | // prefixes the generated string with one 1304 | print_flush!("-> Expression compiled to IR:"); 1305 | function.print_to_stderr(); 1306 | } 1307 | 1308 | if !is_anon { 1309 | // only add it now to ensure it is correct 1310 | previous_exprs.push(fun); 1311 | } 1312 | 1313 | (function.get_name().to_str().unwrap().to_string(), is_anon) 1314 | }, 1315 | Err(err) => { 1316 | println!("!> Error compiling function: {}", err); 1317 | continue; 1318 | } 1319 | } 1320 | }, 1321 | Err(err) => { 1322 | println!("!> Error parsing expression: {}", err); 1323 | continue; 1324 | } 1325 | }; 1326 | 1327 | if is_anonymous { 1328 | let ee = module.create_jit_execution_engine(OptimizationLevel::None).unwrap(); 1329 | 1330 | let maybe_fn = unsafe { ee.get_function:: f64>(name.as_str()) }; 1331 | let compiled_fn = match maybe_fn { 1332 | Ok(f) => f, 1333 | Err(err) => { 1334 | println!("!> Error during execution: {:?}", err); 1335 | continue; 1336 | } 1337 | }; 1338 | 1339 | unsafe { 1340 | println!("=> {}", compiled_fn.call()); 1341 | } 1342 | } 1343 | } 1344 | } 1345 | -------------------------------------------------------------------------------- /pass/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "pass" 3 | version = "0.1.0" 4 | authors = ["Phodal Huang "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | -------------------------------------------------------------------------------- /pass/README.md: -------------------------------------------------------------------------------- 1 | # PASS 2 | 3 | > LLVM Pass框架是LLVM系统的重要组成部分,因为LLVM传递是编译器最有趣的部分所在。传递执行构成编译器的转换和优化,它们构建这些转换使用的分析结果,并且最重要的是,它们是编译器代码的结构化技术。 4 | -------------------------------------------------------------------------------- /pass/src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | println!("Hello, world!"); 3 | } 4 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phodal/rust-llvm-practises/85a100bf148108802f3740b124d0e78a3361ac45/src/main.rs -------------------------------------------------------------------------------- /stdlib/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "llvm-stdlib" 3 | version = "0.1.0" 4 | authors = ["Phodal Huang "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | inkwell = { version = "0.1.0-llvm10sample", features = ["target-x86", "target-webassembly", "llvm10-0"] } 11 | -------------------------------------------------------------------------------- /stdlib/src/main.rs: -------------------------------------------------------------------------------- 1 | use inkwell::context::Context; 2 | use inkwell::memory_buffer::MemoryBuffer; 3 | use inkwell::module::Module; 4 | use inkwell::OptimizationLevel; 5 | 6 | static CHARJ_LIB: &[u8] = include_bytes!("../stdlib/charj.bc"); 7 | 8 | fn main() { 9 | let context = Context::create(); 10 | let memory = MemoryBuffer::create_from_memory_range(CHARJ_LIB, "charj"); 11 | let module = Module::parse_bitcode_from_buffer(&memory, &context).unwrap(); 12 | 13 | if let Some(_fun) = module.get_function("main") { 14 | let ee = module 15 | .create_jit_execution_engine(OptimizationLevel::None) 16 | .unwrap(); 17 | let maybe_fn = unsafe { ee.get_function:: f64>("main") }; 18 | 19 | unsafe { 20 | maybe_fn.unwrap().call(); 21 | } 22 | } 23 | } 24 | 25 | pub fn load_std_lib(context: &Context) -> Module { 26 | let memory = MemoryBuffer::create_from_memory_range(CHARJ_LIB, "charj"); 27 | let module = Module::parse_bitcode_from_buffer(&memory, context).unwrap(); 28 | return module; 29 | } 30 | -------------------------------------------------------------------------------- /stdlib/stdlib/.gitignore: -------------------------------------------------------------------------------- 1 | *.bc 2 | -------------------------------------------------------------------------------- /stdlib/stdlib/Makefile: -------------------------------------------------------------------------------- 1 | CC=clang 2 | CFLAGS=--target=$(TARGET) -emit-llvm -O3 -ffreestanding -fno-builtin -Wall -Wno-unused-function 3 | 4 | %.bc: %.c 5 | $(CC) -c $(CFLAGS) $< -o $@ 6 | 7 | CHARJ=charj.bc 8 | BPF_DEMO=lib.bc 9 | 10 | all: $(CHARJ) ${BPF_DEMO} 11 | 12 | # target: https://clang.llvm.org/docs/CrossCompilation.html 13 | # don't include swap 14 | $(CHARJ): TARGET=x86_64-apple-darwin-macho # macos 15 | 16 | $(BPF_DEMO): TARGET=bpf 17 | -------------------------------------------------------------------------------- /stdlib/stdlib/charj.c: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Fengda Huang on 2020/11/11. 3 | // 4 | #include 5 | 6 | int main() 7 | { 8 | printf("Hello, World!"); 9 | return 0; 10 | } 11 | -------------------------------------------------------------------------------- /stdlib/stdlib/lib.c: -------------------------------------------------------------------------------- 1 | void swap(int *pa,int *pb){ 2 | int temp; 3 | temp=*pa,*pa=*pb,*pb=temp; 4 | } 5 | -------------------------------------------------------------------------------- /tools/control_flow_graph/.main.dot: -------------------------------------------------------------------------------- 1 | digraph "CFG for 'main' function" { 2 | label="CFG for 'main' function"; 3 | 4 | Node0x7ff3bac21110 [shape=record,label="{entrypoint:\l %3 = call i64 (i8*, ...) @puts(i8* getelementptr inbounds ([14 x i8], [14 x\l... i8]* @hello, i64 0, i64 0))\l ret i64 0\l}"]; 5 | } 6 | -------------------------------------------------------------------------------- /tools/control_flow_graph/README.md: -------------------------------------------------------------------------------- 1 | my path: `~/llvm/llvm-10.0.1.src/build/bin/opt` 2 | 3 | ```bash 4 | opt -dot-callgraph main.ll 5 | ``` 6 | 7 | ```bash 8 | opt -dot-cfg lab.ll 9 | ``` 10 | 11 | 12 | ``` 13 | dot callgraph.dot -Tpng -o callgraph.png 14 | ``` 15 | 16 | ![CallGraph](callgraph.png) 17 | -------------------------------------------------------------------------------- /tools/control_flow_graph/callgraph.dot: -------------------------------------------------------------------------------- 1 | digraph "Call graph" { 2 | label="Call graph"; 3 | 4 | Node0x7fb1fa420130 [shape=record,label="{external node}"]; 5 | Node0x7fb1fa420130 -> Node0x7fb1fa4201c0; 6 | Node0x7fb1fa420130 -> Node0x7fb1fa420240; 7 | Node0x7fb1fa4201c0 [shape=record,label="{main}"]; 8 | Node0x7fb1fa4201c0 -> Node0x7fb1fa420240; 9 | Node0x7fb1fa420240 [shape=record,label="{puts}"]; 10 | Node0x7fb1fa420240 -> Node0x7fb1fa420160; 11 | } 12 | -------------------------------------------------------------------------------- /tools/control_flow_graph/callgraph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phodal/rust-llvm-practises/85a100bf148108802f3740b124d0e78a3361ac45/tools/control_flow_graph/callgraph.png -------------------------------------------------------------------------------- /wasm/.gitignore: -------------------------------------------------------------------------------- 1 | cmake-build-debug 2 | -------------------------------------------------------------------------------- /wasm/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "wasm" 3 | version = "0.1.0" 4 | authors = ["Phodal Huang "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | inkwell = { git = "https://github.com/TheDan64/inkwell", branch = "master", features = ["llvm10-0"] } 11 | -------------------------------------------------------------------------------- /wasm/README.md: -------------------------------------------------------------------------------- 1 | # WASM 2 | 3 | 4 | ### WASM 5 | 6 | Refs: 7 | 8 | - [Using WebAssembly in LLVM](https://gist.github.com/yurydelendik/4eeff8248aeb14ce763e) 9 | - [Using LLVM from Rust to generate WebAssembly binaries](https://medium.com/@jayphelps/using-llvm-from-rust-to-generate-webassembly-93e8c193fdb4) 10 | 11 | ``` 12 | -DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD=WebAssembly 13 | ``` 14 | 15 | ``` 16 | llc -march=wasm32 main.bc -o main.s 17 | ``` 18 | 19 | Using solang: [https://solang.readthedocs.io/en/latest/installing.html#installing-llvm-on-mac](https://solang.readthedocs.io/en/latest/installing.html#installing-llvm-on-mac) 20 | 21 | ``` 22 | cmake -G Ninja -DLLVM_ENABLE_ASSERTIONS=On '-DLLVM_ENABLE_PROJECTS=clang;lld' \ 23 | -DLLVM_ENABLE_TERMINFO=Off -DCMAKE_BUILD_TYPE=Release \ 24 | -DCMAKE_INSTALL_PREFIX=installdir -B build llvm 25 | cmake --build build --target install 26 | ``` 27 | 28 | ### Run Rasm 29 | 30 | ``` 31 | curl https://get.wasmer.io -sSfL | sh 32 | ``` 33 | 34 | ``` 35 | $ wasmer qjs.wasm 36 | ``` 37 | 38 | ``` 39 | rustup target add wasm32-unknown-emscripten 40 | ``` 41 | -------------------------------------------------------------------------------- /wasm/demos/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.17) 2 | project(wasm C) 3 | 4 | set(CMAKE_C_STANDARD 11) 5 | 6 | add_executable(wasm 7 | demos/hello.c) 8 | -------------------------------------------------------------------------------- /wasm/demos/hello.c: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Fengda Huang on 2020/12/29. 3 | // 4 | 5 | #include 6 | 7 | int main() { 8 | printf("hello, world!\n"); 9 | return 0; 10 | } 11 | -------------------------------------------------------------------------------- /wasm/demos/hello.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phodal/rust-llvm-practises/85a100bf148108802f3740b124d0e78a3361ac45/wasm/demos/hello.o -------------------------------------------------------------------------------- /wasm/hello.asm: -------------------------------------------------------------------------------- 1 | .text 2 | .file "main" 3 | .section .text.main,"",@ 4 | .globl main 5 | .type main,@function 6 | main: 7 | .functype main () -> () 8 | .local i32, i32, i32 9 | i32.const __unnamed_1 10 | local.set 0 11 | i32.const 0 12 | local.set 1 13 | local.get 0 14 | local.get 1 15 | call jsprint 16 | drop 17 | i32.const 0 18 | local.set 2 19 | local.get 2 20 | return 21 | end_function 22 | .Lfunc_end0: 23 | .size main, .Lfunc_end0-main 24 | 25 | .type __unnamed_1,@object 26 | .section .data.__unnamed_1,"",@ 27 | __unnamed_1: 28 | .ascii "hello, world!" 29 | .size __unnamed_1, 13 30 | 31 | .functype jsprint (i32, i32) -> (i32) 32 | -------------------------------------------------------------------------------- /wasm/hello.wat: -------------------------------------------------------------------------------- 1 | (module 2 | ;; Import our myprint function 3 | (import "env" "jsprint" (func $jsprint (param i32))) 4 | ;; Define a single page memory of 64KB. 5 | (memory $0 1) 6 | ;; Store the Hello World (null terminated) string at byte offset 0 7 | (data (i32.const 0) "Hello World!\00") 8 | ;; Export the memory so it can be access in the host environment. 9 | (export "pagememory" (memory $0)) 10 | ;; Define a function to be called from our host 11 | (func $helloworld 12 | (call $jsprint (i32.const 0)) 13 | ) 14 | ;; Export the wasmprint function for the host to call. 15 | (export "helloworld" (func $helloworld)) 16 | ) 17 | 18 | -------------------------------------------------------------------------------- /wasm/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 38 | 39 | 40 |
41 | 42 | -------------------------------------------------------------------------------- /wasm/src/main.rs: -------------------------------------------------------------------------------- 1 | extern crate inkwell; 2 | 3 | use inkwell::{AddressSpace, OptimizationLevel}; 4 | use inkwell::builder::Builder; 5 | use inkwell::context::Context; 6 | use inkwell::module::{Linkage, Module}; 7 | use inkwell::targets::{CodeModel, FileType, RelocMode, TargetTriple}; 8 | use inkwell::values::{FunctionValue, PointerValue}; 9 | 10 | struct CodeGen<'ctx> { 11 | context: &'ctx Context, 12 | module: Module<'ctx>, 13 | builder: Builder<'ctx>, 14 | } 15 | 16 | impl<'ctx> CodeGen<'ctx> { 17 | fn emit_global_string(&self, string: &&str, name: &str) -> PointerValue { 18 | let ty = self.context.i8_type().array_type(string.len() as u32); 19 | let gv = self 20 | .module 21 | .add_global(ty, Some(AddressSpace::Generic), name); 22 | gv.set_linkage(Linkage::Internal); 23 | gv.set_initializer(&self.context.const_string(string.as_ref(), false)); 24 | 25 | let pointer_value = self.builder.build_pointer_cast( 26 | gv.as_pointer_value(), 27 | self.context.i8_type().ptr_type(AddressSpace::Generic), 28 | name, 29 | ); 30 | 31 | pointer_value 32 | } 33 | 34 | fn compile(&self) { 35 | let function_type = self.context.void_type().fn_type(&[], false); 36 | 37 | // create function & block 38 | let main_func = self.module.add_function("hello", function_type, None); 39 | let main_block = self.context.append_basic_block(main_func, "hello"); 40 | self.builder.position_at_end(main_block); 41 | 42 | // create inner out function 43 | let printf_func = self.create_out_func(); 44 | 45 | // make call 46 | let pointer_value = self.emit_global_string(&"hello, world!", ""); 47 | self.builder.build_call(printf_func, &[pointer_value.into()], ""); 48 | 49 | self.builder 50 | .build_return(Some(&self.context.i32_type().const_int(0, false))); 51 | } 52 | 53 | fn create_out_func(&self) -> FunctionValue { 54 | if let Some(fun) = self.module.get_function("jsprint") { 55 | return fun 56 | } 57 | 58 | let str_type = self.context.i8_type().ptr_type(AddressSpace::Generic); 59 | let printf_type = self.context.i32_type().fn_type(&[str_type.into()], true); 60 | let printf_func = self 61 | .module 62 | .add_function("jsprint", printf_type, Some(Linkage::External)); 63 | printf_func 64 | } 65 | } 66 | 67 | fn main() { 68 | let context = Context::create(); 69 | let module = context.create_module("main"); 70 | let builder = context.create_builder(); 71 | 72 | let codegen = CodeGen { context: &context, module, builder}; 73 | codegen.compile(); 74 | 75 | inkwell::targets::Target::initialize_webassembly(&Default::default()); 76 | let target = inkwell::targets::Target::from_name("wasm32").unwrap(); 77 | let target_machine = target 78 | .create_target_machine( 79 | &TargetTriple::create("wasm32-unknown-unknown-wasm"), 80 | "", 81 | "", 82 | OptimizationLevel::None, 83 | RelocMode::Default, 84 | CodeModel::Default, 85 | ) 86 | .unwrap(); 87 | 88 | let result = target_machine.write_to_file(&codegen.module, FileType::Object, "hello.wasm".as_ref()); 89 | match result { 90 | Ok(()) => { 91 | println!("write file ok"); 92 | } 93 | Err(err) => { 94 | println!("{:?}", err); 95 | } 96 | } 97 | } 98 | 99 | --------------------------------------------------------------------------------