├── .gitattributes ├── .github ├── dependabot.yml └── workflows │ ├── ci.yml │ └── deploy.yml ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── bf ├── cgbfi2.bf ├── hello.bf └── mendelbrot.bf ├── book ├── book.toml └── src │ ├── 1.md │ ├── 2.md │ ├── 3.md │ ├── 4.md │ ├── 5.md │ ├── 6.md │ ├── 7.md │ ├── 8.md │ ├── SUMMARY.md │ ├── conclusion.md │ └── introduction.md ├── justfile └── src ├── bfir.rs ├── bfjit.rs ├── error.rs └── main.rs /.gitattributes: -------------------------------------------------------------------------------- 1 | docs/* linguist-vendored 2 | bf/* linguist-vendored 3 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "cargo" # See documentation for possible values 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "monthly" 12 | groups: 13 | dependencies: 14 | patterns: 15 | - "*" 16 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | - 'feat/**' 8 | pull_request: 9 | branches: 10 | - main 11 | - 'feat/**' 12 | schedule: 13 | - cron: '0 0 * * 0' # at midnight of each sunday 14 | workflow_dispatch: 15 | 16 | jobs: 17 | develop: 18 | runs-on: ubuntu-latest 19 | steps: 20 | - uses: actions/checkout@v4 21 | - uses: taiki-e/install-action@just 22 | - uses: dtolnay/rust-toolchain@nightly 23 | with: 24 | components: rustfmt, clippy, miri, rust-src 25 | - uses: Swatinem/rust-cache@v2 26 | - run: just ci 27 | 28 | msrv: 29 | runs-on: ubuntu-latest 30 | strategy: 31 | fail-fast: false 32 | matrix: 33 | toolchain: 34 | - 1.78.0 # MSRV 35 | - stable 36 | steps: 37 | - uses: actions/checkout@v4 38 | - uses: taiki-e/install-action@just 39 | - uses: dtolnay/rust-toolchain@master 40 | with: 41 | toolchain: ${{ matrix.toolchain }} 42 | - uses: Swatinem/rust-cache@v2 43 | - run: just test 44 | 45 | audit: 46 | runs-on: ubuntu-latest 47 | steps: 48 | - uses: actions/checkout@v4 49 | - uses: taiki-e/install-action@cargo-audit 50 | - run: cargo audit -D warnings 51 | -------------------------------------------------------------------------------- /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: Deploy 2 | 3 | on: 4 | push: 5 | branches: ["main"] 6 | workflow_dispatch: 7 | 8 | permissions: 9 | contents: read 10 | pages: write 11 | id-token: write 12 | 13 | concurrency: 14 | group: "pages" 15 | cancel-in-progress: false 16 | 17 | jobs: 18 | deploy: 19 | environment: 20 | name: github-pages 21 | url: ${{ steps.deployment.outputs.page_url }} 22 | runs-on: ubuntu-latest 23 | steps: 24 | - uses: actions/checkout@v4 25 | - uses: taiki-e/install-action@mdbook 26 | - run: cd book && mdbook build 27 | - name: Upload artifact 28 | uses: actions/upload-pages-artifact@v3 29 | with: 30 | path: ./dist 31 | - name: Deploy to GitHub Pages 32 | id: deployment 33 | uses: actions/deploy-pages@v4 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | .vscode 4 | /dist 5 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "anstream" 7 | version = "0.6.18" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" 10 | dependencies = [ 11 | "anstyle", 12 | "anstyle-parse", 13 | "anstyle-query", 14 | "anstyle-wincon", 15 | "colorchoice", 16 | "is_terminal_polyfill", 17 | "utf8parse", 18 | ] 19 | 20 | [[package]] 21 | name = "anstyle" 22 | version = "1.0.10" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" 25 | 26 | [[package]] 27 | name = "anstyle-parse" 28 | version = "0.2.6" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" 31 | dependencies = [ 32 | "utf8parse", 33 | ] 34 | 35 | [[package]] 36 | name = "anstyle-query" 37 | version = "1.1.2" 38 | source = "registry+https://github.com/rust-lang/crates.io-index" 39 | checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" 40 | dependencies = [ 41 | "windows-sys", 42 | ] 43 | 44 | [[package]] 45 | name = "anstyle-wincon" 46 | version = "3.0.7" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" 49 | dependencies = [ 50 | "anstyle", 51 | "once_cell", 52 | "windows-sys", 53 | ] 54 | 55 | [[package]] 56 | name = "bfjit" 57 | version = "0.1.4" 58 | dependencies = [ 59 | "clap", 60 | "dynasm", 61 | "dynasmrt", 62 | "thiserror", 63 | ] 64 | 65 | [[package]] 66 | name = "bitflags" 67 | version = "2.8.0" 68 | source = "registry+https://github.com/rust-lang/crates.io-index" 69 | checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" 70 | 71 | [[package]] 72 | name = "byteorder" 73 | version = "1.5.0" 74 | source = "registry+https://github.com/rust-lang/crates.io-index" 75 | checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" 76 | 77 | [[package]] 78 | name = "clap" 79 | version = "4.5.39" 80 | source = "registry+https://github.com/rust-lang/crates.io-index" 81 | checksum = "fd60e63e9be68e5fb56422e397cf9baddded06dae1d2e523401542383bc72a9f" 82 | dependencies = [ 83 | "clap_builder", 84 | "clap_derive", 85 | ] 86 | 87 | [[package]] 88 | name = "clap_builder" 89 | version = "4.5.39" 90 | source = "registry+https://github.com/rust-lang/crates.io-index" 91 | checksum = "89cc6392a1f72bbeb820d71f32108f61fdaf18bc526e1d23954168a67759ef51" 92 | dependencies = [ 93 | "anstream", 94 | "anstyle", 95 | "clap_lex", 96 | "strsim", 97 | ] 98 | 99 | [[package]] 100 | name = "clap_derive" 101 | version = "4.5.32" 102 | source = "registry+https://github.com/rust-lang/crates.io-index" 103 | checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" 104 | dependencies = [ 105 | "heck", 106 | "proc-macro2", 107 | "quote", 108 | "syn", 109 | ] 110 | 111 | [[package]] 112 | name = "clap_lex" 113 | version = "0.7.4" 114 | source = "registry+https://github.com/rust-lang/crates.io-index" 115 | checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" 116 | 117 | [[package]] 118 | name = "colorchoice" 119 | version = "1.0.3" 120 | source = "registry+https://github.com/rust-lang/crates.io-index" 121 | checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" 122 | 123 | [[package]] 124 | name = "dynasm" 125 | version = "3.0.1" 126 | source = "registry+https://github.com/rust-lang/crates.io-index" 127 | checksum = "697c04882d6b25c9eb2583d2737bea1947a28c9d7544dddec76d85e6e1545c92" 128 | dependencies = [ 129 | "bitflags", 130 | "byteorder", 131 | "lazy_static", 132 | "proc-macro-error2", 133 | "proc-macro2", 134 | "quote", 135 | "syn", 136 | ] 137 | 138 | [[package]] 139 | name = "dynasmrt" 140 | version = "3.0.1" 141 | source = "registry+https://github.com/rust-lang/crates.io-index" 142 | checksum = "1718a074cf8317b3509228d7ea9a99d7014b17483109d701c733699065fe5e35" 143 | dependencies = [ 144 | "byteorder", 145 | "dynasm", 146 | "fnv", 147 | "memmap2", 148 | ] 149 | 150 | [[package]] 151 | name = "fnv" 152 | version = "1.0.7" 153 | source = "registry+https://github.com/rust-lang/crates.io-index" 154 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 155 | 156 | [[package]] 157 | name = "heck" 158 | version = "0.5.0" 159 | source = "registry+https://github.com/rust-lang/crates.io-index" 160 | checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" 161 | 162 | [[package]] 163 | name = "is_terminal_polyfill" 164 | version = "1.70.1" 165 | source = "registry+https://github.com/rust-lang/crates.io-index" 166 | checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" 167 | 168 | [[package]] 169 | name = "lazy_static" 170 | version = "1.5.0" 171 | source = "registry+https://github.com/rust-lang/crates.io-index" 172 | checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" 173 | 174 | [[package]] 175 | name = "libc" 176 | version = "0.2.169" 177 | source = "registry+https://github.com/rust-lang/crates.io-index" 178 | checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" 179 | 180 | [[package]] 181 | name = "memmap2" 182 | version = "0.9.5" 183 | source = "registry+https://github.com/rust-lang/crates.io-index" 184 | checksum = "fd3f7eed9d3848f8b98834af67102b720745c4ec028fcd0aa0239277e7de374f" 185 | dependencies = [ 186 | "libc", 187 | ] 188 | 189 | [[package]] 190 | name = "once_cell" 191 | version = "1.20.2" 192 | source = "registry+https://github.com/rust-lang/crates.io-index" 193 | checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" 194 | 195 | [[package]] 196 | name = "proc-macro-error-attr2" 197 | version = "2.0.0" 198 | source = "registry+https://github.com/rust-lang/crates.io-index" 199 | checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" 200 | dependencies = [ 201 | "proc-macro2", 202 | "quote", 203 | ] 204 | 205 | [[package]] 206 | name = "proc-macro-error2" 207 | version = "2.0.1" 208 | source = "registry+https://github.com/rust-lang/crates.io-index" 209 | checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" 210 | dependencies = [ 211 | "proc-macro-error-attr2", 212 | "proc-macro2", 213 | "quote", 214 | "syn", 215 | ] 216 | 217 | [[package]] 218 | name = "proc-macro2" 219 | version = "1.0.93" 220 | source = "registry+https://github.com/rust-lang/crates.io-index" 221 | checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" 222 | dependencies = [ 223 | "unicode-ident", 224 | ] 225 | 226 | [[package]] 227 | name = "quote" 228 | version = "1.0.38" 229 | source = "registry+https://github.com/rust-lang/crates.io-index" 230 | checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" 231 | dependencies = [ 232 | "proc-macro2", 233 | ] 234 | 235 | [[package]] 236 | name = "strsim" 237 | version = "0.11.1" 238 | source = "registry+https://github.com/rust-lang/crates.io-index" 239 | checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" 240 | 241 | [[package]] 242 | name = "syn" 243 | version = "2.0.96" 244 | source = "registry+https://github.com/rust-lang/crates.io-index" 245 | checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" 246 | dependencies = [ 247 | "proc-macro2", 248 | "quote", 249 | "unicode-ident", 250 | ] 251 | 252 | [[package]] 253 | name = "thiserror" 254 | version = "2.0.12" 255 | source = "registry+https://github.com/rust-lang/crates.io-index" 256 | checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" 257 | dependencies = [ 258 | "thiserror-impl", 259 | ] 260 | 261 | [[package]] 262 | name = "thiserror-impl" 263 | version = "2.0.12" 264 | source = "registry+https://github.com/rust-lang/crates.io-index" 265 | checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" 266 | dependencies = [ 267 | "proc-macro2", 268 | "quote", 269 | "syn", 270 | ] 271 | 272 | [[package]] 273 | name = "unicode-ident" 274 | version = "1.0.14" 275 | source = "registry+https://github.com/rust-lang/crates.io-index" 276 | checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" 277 | 278 | [[package]] 279 | name = "utf8parse" 280 | version = "0.2.2" 281 | source = "registry+https://github.com/rust-lang/crates.io-index" 282 | checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" 283 | 284 | [[package]] 285 | name = "windows-sys" 286 | version = "0.59.0" 287 | source = "registry+https://github.com/rust-lang/crates.io-index" 288 | checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" 289 | dependencies = [ 290 | "windows-targets", 291 | ] 292 | 293 | [[package]] 294 | name = "windows-targets" 295 | version = "0.52.6" 296 | source = "registry+https://github.com/rust-lang/crates.io-index" 297 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 298 | dependencies = [ 299 | "windows_aarch64_gnullvm", 300 | "windows_aarch64_msvc", 301 | "windows_i686_gnu", 302 | "windows_i686_gnullvm", 303 | "windows_i686_msvc", 304 | "windows_x86_64_gnu", 305 | "windows_x86_64_gnullvm", 306 | "windows_x86_64_msvc", 307 | ] 308 | 309 | [[package]] 310 | name = "windows_aarch64_gnullvm" 311 | version = "0.52.6" 312 | source = "registry+https://github.com/rust-lang/crates.io-index" 313 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 314 | 315 | [[package]] 316 | name = "windows_aarch64_msvc" 317 | version = "0.52.6" 318 | source = "registry+https://github.com/rust-lang/crates.io-index" 319 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 320 | 321 | [[package]] 322 | name = "windows_i686_gnu" 323 | version = "0.52.6" 324 | source = "registry+https://github.com/rust-lang/crates.io-index" 325 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 326 | 327 | [[package]] 328 | name = "windows_i686_gnullvm" 329 | version = "0.52.6" 330 | source = "registry+https://github.com/rust-lang/crates.io-index" 331 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 332 | 333 | [[package]] 334 | name = "windows_i686_msvc" 335 | version = "0.52.6" 336 | source = "registry+https://github.com/rust-lang/crates.io-index" 337 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 338 | 339 | [[package]] 340 | name = "windows_x86_64_gnu" 341 | version = "0.52.6" 342 | source = "registry+https://github.com/rust-lang/crates.io-index" 343 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 344 | 345 | [[package]] 346 | name = "windows_x86_64_gnullvm" 347 | version = "0.52.6" 348 | source = "registry+https://github.com/rust-lang/crates.io-index" 349 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 350 | 351 | [[package]] 352 | name = "windows_x86_64_msvc" 353 | version = "0.52.6" 354 | source = "registry+https://github.com/rust-lang/crates.io-index" 355 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 356 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bfjit" 3 | version = "0.1.4" 4 | authors = ["Nugine "] 5 | edition = "2021" 6 | license = "AGPL-3.0" 7 | repository = "https://github.com/Nugine/bfjit" 8 | description = "Brainfuck JIT VM tutorial" 9 | keywords = ["brainfuck", "jit", "tutorial"] 10 | categories = ["compilers", "command-line-utilities"] 11 | 12 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 13 | 14 | [dependencies] 15 | thiserror = "2.0.12" 16 | dynasm = "3.0.1" 17 | dynasmrt = "3.0.1" 18 | clap = { version = "4.5.39", features = ["derive"] } 19 | 20 | [profile.release] 21 | debug = "full" 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU AFFERO GENERAL PUBLIC LICENSE 2 | Version 3, 19 November 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU Affero General Public License is a free, copyleft license for 11 | software and other kinds of works, specifically designed to ensure 12 | cooperation with the community in the case of network server software. 13 | 14 | The licenses for most software and other practical works are designed 15 | to take away your freedom to share and change the works. By contrast, 16 | our General Public Licenses are intended to guarantee your freedom to 17 | share and change all versions of a program--to make sure it remains free 18 | software for all its users. 19 | 20 | When we speak of free software, we are referring to freedom, not 21 | price. Our General Public Licenses are designed to make sure that you 22 | have the freedom to distribute copies of free software (and charge for 23 | them if you wish), that you receive source code or can get it if you 24 | want it, that you can change the software or use pieces of it in new 25 | free programs, and that you know you can do these things. 26 | 27 | Developers that use our General Public Licenses protect your rights 28 | with two steps: (1) assert copyright on the software, and (2) offer 29 | you this License which gives you legal permission to copy, distribute 30 | and/or modify the software. 31 | 32 | A secondary benefit of defending all users' freedom is that 33 | improvements made in alternate versions of the program, if they 34 | receive widespread use, become available for other developers to 35 | incorporate. Many developers of free software are heartened and 36 | encouraged by the resulting cooperation. However, in the case of 37 | software used on network servers, this result may fail to come about. 38 | The GNU General Public License permits making a modified version and 39 | letting the public access it on a server without ever releasing its 40 | source code to the public. 41 | 42 | The GNU Affero General Public License is designed specifically to 43 | ensure that, in such cases, the modified source code becomes available 44 | to the community. It requires the operator of a network server to 45 | provide the source code of the modified version running there to the 46 | users of that server. Therefore, public use of a modified version, on 47 | a publicly accessible server, gives the public access to the source 48 | code of the modified version. 49 | 50 | An older license, called the Affero General Public License and 51 | published by Affero, was designed to accomplish similar goals. This is 52 | a different license, not a version of the Affero GPL, but Affero has 53 | released a new version of the Affero GPL which permits relicensing under 54 | this license. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | TERMS AND CONDITIONS 60 | 61 | 0. Definitions. 62 | 63 | "This License" refers to version 3 of the GNU Affero General Public License. 64 | 65 | "Copyright" also means copyright-like laws that apply to other kinds of 66 | works, such as semiconductor masks. 67 | 68 | "The Program" refers to any copyrightable work licensed under this 69 | License. Each licensee is addressed as "you". "Licensees" and 70 | "recipients" may be individuals or organizations. 71 | 72 | To "modify" a work means to copy from or adapt all or part of the work 73 | in a fashion requiring copyright permission, other than the making of an 74 | exact copy. The resulting work is called a "modified version" of the 75 | earlier work or a work "based on" the earlier work. 76 | 77 | A "covered work" means either the unmodified Program or a work based 78 | on the Program. 79 | 80 | To "propagate" a work means to do anything with it that, without 81 | permission, would make you directly or secondarily liable for 82 | infringement under applicable copyright law, except executing it on a 83 | computer or modifying a private copy. Propagation includes copying, 84 | distribution (with or without modification), making available to the 85 | public, and in some countries other activities as well. 86 | 87 | To "convey" a work means any kind of propagation that enables other 88 | parties to make or receive copies. Mere interaction with a user through 89 | a computer network, with no transfer of a copy, is not conveying. 90 | 91 | An interactive user interface displays "Appropriate Legal Notices" 92 | to the extent that it includes a convenient and prominently visible 93 | feature that (1) displays an appropriate copyright notice, and (2) 94 | tells the user that there is no warranty for the work (except to the 95 | extent that warranties are provided), that licensees may convey the 96 | work under this License, and how to view a copy of this License. If 97 | the interface presents a list of user commands or options, such as a 98 | menu, a prominent item in the list meets this criterion. 99 | 100 | 1. Source Code. 101 | 102 | The "source code" for a work means the preferred form of the work 103 | for making modifications to it. "Object code" means any non-source 104 | form of a work. 105 | 106 | A "Standard Interface" means an interface that either is an official 107 | standard defined by a recognized standards body, or, in the case of 108 | interfaces specified for a particular programming language, one that 109 | is widely used among developers working in that language. 110 | 111 | The "System Libraries" of an executable work include anything, other 112 | than the work as a whole, that (a) is included in the normal form of 113 | packaging a Major Component, but which is not part of that Major 114 | Component, and (b) serves only to enable use of the work with that 115 | Major Component, or to implement a Standard Interface for which an 116 | implementation is available to the public in source code form. A 117 | "Major Component", in this context, means a major essential component 118 | (kernel, window system, and so on) of the specific operating system 119 | (if any) on which the executable work runs, or a compiler used to 120 | produce the work, or an object code interpreter used to run it. 121 | 122 | The "Corresponding Source" for a work in object code form means all 123 | the source code needed to generate, install, and (for an executable 124 | work) run the object code and to modify the work, including scripts to 125 | control those activities. However, it does not include the work's 126 | System Libraries, or general-purpose tools or generally available free 127 | programs which are used unmodified in performing those activities but 128 | which are not part of the work. For example, Corresponding Source 129 | includes interface definition files associated with source files for 130 | the work, and the source code for shared libraries and dynamically 131 | linked subprograms that the work is specifically designed to require, 132 | such as by intimate data communication or control flow between those 133 | subprograms and other parts of the work. 134 | 135 | The Corresponding Source need not include anything that users 136 | can regenerate automatically from other parts of the Corresponding 137 | Source. 138 | 139 | The Corresponding Source for a work in source code form is that 140 | same work. 141 | 142 | 2. Basic Permissions. 143 | 144 | All rights granted under this License are granted for the term of 145 | copyright on the Program, and are irrevocable provided the stated 146 | conditions are met. This License explicitly affirms your unlimited 147 | permission to run the unmodified Program. The output from running a 148 | covered work is covered by this License only if the output, given its 149 | content, constitutes a covered work. This License acknowledges your 150 | rights of fair use or other equivalent, as provided by copyright law. 151 | 152 | You may make, run and propagate covered works that you do not 153 | convey, without conditions so long as your license otherwise remains 154 | in force. You may convey covered works to others for the sole purpose 155 | of having them make modifications exclusively for you, or provide you 156 | with facilities for running those works, provided that you comply with 157 | the terms of this License in conveying all material for which you do 158 | not control copyright. Those thus making or running the covered works 159 | for you must do so exclusively on your behalf, under your direction 160 | and control, on terms that prohibit them from making any copies of 161 | your copyrighted material outside their relationship with you. 162 | 163 | Conveying under any other circumstances is permitted solely under 164 | the conditions stated below. Sublicensing is not allowed; section 10 165 | makes it unnecessary. 166 | 167 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 168 | 169 | No covered work shall be deemed part of an effective technological 170 | measure under any applicable law fulfilling obligations under article 171 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 172 | similar laws prohibiting or restricting circumvention of such 173 | measures. 174 | 175 | When you convey a covered work, you waive any legal power to forbid 176 | circumvention of technological measures to the extent such circumvention 177 | is effected by exercising rights under this License with respect to 178 | the covered work, and you disclaim any intention to limit operation or 179 | modification of the work as a means of enforcing, against the work's 180 | users, your or third parties' legal rights to forbid circumvention of 181 | technological measures. 182 | 183 | 4. Conveying Verbatim Copies. 184 | 185 | You may convey verbatim copies of the Program's source code as you 186 | receive it, in any medium, provided that you conspicuously and 187 | appropriately publish on each copy an appropriate copyright notice; 188 | keep intact all notices stating that this License and any 189 | non-permissive terms added in accord with section 7 apply to the code; 190 | keep intact all notices of the absence of any warranty; and give all 191 | recipients a copy of this License along with the Program. 192 | 193 | You may charge any price or no price for each copy that you convey, 194 | and you may offer support or warranty protection for a fee. 195 | 196 | 5. Conveying Modified Source Versions. 197 | 198 | You may convey a work based on the Program, or the modifications to 199 | produce it from the Program, in the form of source code under the 200 | terms of section 4, provided that you also meet all of these conditions: 201 | 202 | a) The work must carry prominent notices stating that you modified 203 | it, and giving a relevant date. 204 | 205 | b) The work must carry prominent notices stating that it is 206 | released under this License and any conditions added under section 207 | 7. This requirement modifies the requirement in section 4 to 208 | "keep intact all notices". 209 | 210 | c) You must license the entire work, as a whole, under this 211 | License to anyone who comes into possession of a copy. This 212 | License will therefore apply, along with any applicable section 7 213 | additional terms, to the whole of the work, and all its parts, 214 | regardless of how they are packaged. This License gives no 215 | permission to license the work in any other way, but it does not 216 | invalidate such permission if you have separately received it. 217 | 218 | d) If the work has interactive user interfaces, each must display 219 | Appropriate Legal Notices; however, if the Program has interactive 220 | interfaces that do not display Appropriate Legal Notices, your 221 | work need not make them do so. 222 | 223 | A compilation of a covered work with other separate and independent 224 | works, which are not by their nature extensions of the covered work, 225 | and which are not combined with it such as to form a larger program, 226 | in or on a volume of a storage or distribution medium, is called an 227 | "aggregate" if the compilation and its resulting copyright are not 228 | used to limit the access or legal rights of the compilation's users 229 | beyond what the individual works permit. Inclusion of a covered work 230 | in an aggregate does not cause this License to apply to the other 231 | parts of the aggregate. 232 | 233 | 6. Conveying Non-Source Forms. 234 | 235 | You may convey a covered work in object code form under the terms 236 | of sections 4 and 5, provided that you also convey the 237 | machine-readable Corresponding Source under the terms of this License, 238 | in one of these ways: 239 | 240 | a) Convey the object code in, or embodied in, a physical product 241 | (including a physical distribution medium), accompanied by the 242 | Corresponding Source fixed on a durable physical medium 243 | customarily used for software interchange. 244 | 245 | b) Convey the object code in, or embodied in, a physical product 246 | (including a physical distribution medium), accompanied by a 247 | written offer, valid for at least three years and valid for as 248 | long as you offer spare parts or customer support for that product 249 | model, to give anyone who possesses the object code either (1) a 250 | copy of the Corresponding Source for all the software in the 251 | product that is covered by this License, on a durable physical 252 | medium customarily used for software interchange, for a price no 253 | more than your reasonable cost of physically performing this 254 | conveying of source, or (2) access to copy the 255 | Corresponding Source from a network server at no charge. 256 | 257 | c) Convey individual copies of the object code with a copy of the 258 | written offer to provide the Corresponding Source. This 259 | alternative is allowed only occasionally and noncommercially, and 260 | only if you received the object code with such an offer, in accord 261 | with subsection 6b. 262 | 263 | d) Convey the object code by offering access from a designated 264 | place (gratis or for a charge), and offer equivalent access to the 265 | Corresponding Source in the same way through the same place at no 266 | further charge. You need not require recipients to copy the 267 | Corresponding Source along with the object code. If the place to 268 | copy the object code is a network server, the Corresponding Source 269 | may be on a different server (operated by you or a third party) 270 | that supports equivalent copying facilities, provided you maintain 271 | clear directions next to the object code saying where to find the 272 | Corresponding Source. Regardless of what server hosts the 273 | Corresponding Source, you remain obligated to ensure that it is 274 | available for as long as needed to satisfy these requirements. 275 | 276 | e) Convey the object code using peer-to-peer transmission, provided 277 | you inform other peers where the object code and Corresponding 278 | Source of the work are being offered to the general public at no 279 | charge under subsection 6d. 280 | 281 | A separable portion of the object code, whose source code is excluded 282 | from the Corresponding Source as a System Library, need not be 283 | included in conveying the object code work. 284 | 285 | A "User Product" is either (1) a "consumer product", which means any 286 | tangible personal property which is normally used for personal, family, 287 | or household purposes, or (2) anything designed or sold for incorporation 288 | into a dwelling. In determining whether a product is a consumer product, 289 | doubtful cases shall be resolved in favor of coverage. For a particular 290 | product received by a particular user, "normally used" refers to a 291 | typical or common use of that class of product, regardless of the status 292 | of the particular user or of the way in which the particular user 293 | actually uses, or expects or is expected to use, the product. A product 294 | is a consumer product regardless of whether the product has substantial 295 | commercial, industrial or non-consumer uses, unless such uses represent 296 | the only significant mode of use of the product. 297 | 298 | "Installation Information" for a User Product means any methods, 299 | procedures, authorization keys, or other information required to install 300 | and execute modified versions of a covered work in that User Product from 301 | a modified version of its Corresponding Source. The information must 302 | suffice to ensure that the continued functioning of the modified object 303 | code is in no case prevented or interfered with solely because 304 | modification has been made. 305 | 306 | If you convey an object code work under this section in, or with, or 307 | specifically for use in, a User Product, and the conveying occurs as 308 | part of a transaction in which the right of possession and use of the 309 | User Product is transferred to the recipient in perpetuity or for a 310 | fixed term (regardless of how the transaction is characterized), the 311 | Corresponding Source conveyed under this section must be accompanied 312 | by the Installation Information. But this requirement does not apply 313 | if neither you nor any third party retains the ability to install 314 | modified object code on the User Product (for example, the work has 315 | been installed in ROM). 316 | 317 | The requirement to provide Installation Information does not include a 318 | requirement to continue to provide support service, warranty, or updates 319 | for a work that has been modified or installed by the recipient, or for 320 | the User Product in which it has been modified or installed. Access to a 321 | network may be denied when the modification itself materially and 322 | adversely affects the operation of the network or violates the rules and 323 | protocols for communication across the network. 324 | 325 | Corresponding Source conveyed, and Installation Information provided, 326 | in accord with this section must be in a format that is publicly 327 | documented (and with an implementation available to the public in 328 | source code form), and must require no special password or key for 329 | unpacking, reading or copying. 330 | 331 | 7. Additional Terms. 332 | 333 | "Additional permissions" are terms that supplement the terms of this 334 | License by making exceptions from one or more of its conditions. 335 | Additional permissions that are applicable to the entire Program shall 336 | be treated as though they were included in this License, to the extent 337 | that they are valid under applicable law. If additional permissions 338 | apply only to part of the Program, that part may be used separately 339 | under those permissions, but the entire Program remains governed by 340 | this License without regard to the additional permissions. 341 | 342 | When you convey a copy of a covered work, you may at your option 343 | remove any additional permissions from that copy, or from any part of 344 | it. (Additional permissions may be written to require their own 345 | removal in certain cases when you modify the work.) You may place 346 | additional permissions on material, added by you to a covered work, 347 | for which you have or can give appropriate copyright permission. 348 | 349 | Notwithstanding any other provision of this License, for material you 350 | add to a covered work, you may (if authorized by the copyright holders of 351 | that material) supplement the terms of this License with terms: 352 | 353 | a) Disclaiming warranty or limiting liability differently from the 354 | terms of sections 15 and 16 of this License; or 355 | 356 | b) Requiring preservation of specified reasonable legal notices or 357 | author attributions in that material or in the Appropriate Legal 358 | Notices displayed by works containing it; or 359 | 360 | c) Prohibiting misrepresentation of the origin of that material, or 361 | requiring that modified versions of such material be marked in 362 | reasonable ways as different from the original version; or 363 | 364 | d) Limiting the use for publicity purposes of names of licensors or 365 | authors of the material; or 366 | 367 | e) Declining to grant rights under trademark law for use of some 368 | trade names, trademarks, or service marks; or 369 | 370 | f) Requiring indemnification of licensors and authors of that 371 | material by anyone who conveys the material (or modified versions of 372 | it) with contractual assumptions of liability to the recipient, for 373 | any liability that these contractual assumptions directly impose on 374 | those licensors and authors. 375 | 376 | All other non-permissive additional terms are considered "further 377 | restrictions" within the meaning of section 10. If the Program as you 378 | received it, or any part of it, contains a notice stating that it is 379 | governed by this License along with a term that is a further 380 | restriction, you may remove that term. If a license document contains 381 | a further restriction but permits relicensing or conveying under this 382 | License, you may add to a covered work material governed by the terms 383 | of that license document, provided that the further restriction does 384 | not survive such relicensing or conveying. 385 | 386 | If you add terms to a covered work in accord with this section, you 387 | must place, in the relevant source files, a statement of the 388 | additional terms that apply to those files, or a notice indicating 389 | where to find the applicable terms. 390 | 391 | Additional terms, permissive or non-permissive, may be stated in the 392 | form of a separately written license, or stated as exceptions; 393 | the above requirements apply either way. 394 | 395 | 8. Termination. 396 | 397 | You may not propagate or modify a covered work except as expressly 398 | provided under this License. Any attempt otherwise to propagate or 399 | modify it is void, and will automatically terminate your rights under 400 | this License (including any patent licenses granted under the third 401 | paragraph of section 11). 402 | 403 | However, if you cease all violation of this License, then your 404 | license from a particular copyright holder is reinstated (a) 405 | provisionally, unless and until the copyright holder explicitly and 406 | finally terminates your license, and (b) permanently, if the copyright 407 | holder fails to notify you of the violation by some reasonable means 408 | prior to 60 days after the cessation. 409 | 410 | Moreover, your license from a particular copyright holder is 411 | reinstated permanently if the copyright holder notifies you of the 412 | violation by some reasonable means, this is the first time you have 413 | received notice of violation of this License (for any work) from that 414 | copyright holder, and you cure the violation prior to 30 days after 415 | your receipt of the notice. 416 | 417 | Termination of your rights under this section does not terminate the 418 | licenses of parties who have received copies or rights from you under 419 | this License. If your rights have been terminated and not permanently 420 | reinstated, you do not qualify to receive new licenses for the same 421 | material under section 10. 422 | 423 | 9. Acceptance Not Required for Having Copies. 424 | 425 | You are not required to accept this License in order to receive or 426 | run a copy of the Program. Ancillary propagation of a covered work 427 | occurring solely as a consequence of using peer-to-peer transmission 428 | to receive a copy likewise does not require acceptance. However, 429 | nothing other than this License grants you permission to propagate or 430 | modify any covered work. These actions infringe copyright if you do 431 | not accept this License. Therefore, by modifying or propagating a 432 | covered work, you indicate your acceptance of this License to do so. 433 | 434 | 10. Automatic Licensing of Downstream Recipients. 435 | 436 | Each time you convey a covered work, the recipient automatically 437 | receives a license from the original licensors, to run, modify and 438 | propagate that work, subject to this License. You are not responsible 439 | for enforcing compliance by third parties with this License. 440 | 441 | An "entity transaction" is a transaction transferring control of an 442 | organization, or substantially all assets of one, or subdividing an 443 | organization, or merging organizations. If propagation of a covered 444 | work results from an entity transaction, each party to that 445 | transaction who receives a copy of the work also receives whatever 446 | licenses to the work the party's predecessor in interest had or could 447 | give under the previous paragraph, plus a right to possession of the 448 | Corresponding Source of the work from the predecessor in interest, if 449 | the predecessor has it or can get it with reasonable efforts. 450 | 451 | You may not impose any further restrictions on the exercise of the 452 | rights granted or affirmed under this License. For example, you may 453 | not impose a license fee, royalty, or other charge for exercise of 454 | rights granted under this License, and you may not initiate litigation 455 | (including a cross-claim or counterclaim in a lawsuit) alleging that 456 | any patent claim is infringed by making, using, selling, offering for 457 | sale, or importing the Program or any portion of it. 458 | 459 | 11. Patents. 460 | 461 | A "contributor" is a copyright holder who authorizes use under this 462 | License of the Program or a work on which the Program is based. The 463 | work thus licensed is called the contributor's "contributor version". 464 | 465 | A contributor's "essential patent claims" are all patent claims 466 | owned or controlled by the contributor, whether already acquired or 467 | hereafter acquired, that would be infringed by some manner, permitted 468 | by this License, of making, using, or selling its contributor version, 469 | but do not include claims that would be infringed only as a 470 | consequence of further modification of the contributor version. For 471 | purposes of this definition, "control" includes the right to grant 472 | patent sublicenses in a manner consistent with the requirements of 473 | this License. 474 | 475 | Each contributor grants you a non-exclusive, worldwide, royalty-free 476 | patent license under the contributor's essential patent claims, to 477 | make, use, sell, offer for sale, import and otherwise run, modify and 478 | propagate the contents of its contributor version. 479 | 480 | In the following three paragraphs, a "patent license" is any express 481 | agreement or commitment, however denominated, not to enforce a patent 482 | (such as an express permission to practice a patent or covenant not to 483 | sue for patent infringement). To "grant" such a patent license to a 484 | party means to make such an agreement or commitment not to enforce a 485 | patent against the party. 486 | 487 | If you convey a covered work, knowingly relying on a patent license, 488 | and the Corresponding Source of the work is not available for anyone 489 | to copy, free of charge and under the terms of this License, through a 490 | publicly available network server or other readily accessible means, 491 | then you must either (1) cause the Corresponding Source to be so 492 | available, or (2) arrange to deprive yourself of the benefit of the 493 | patent license for this particular work, or (3) arrange, in a manner 494 | consistent with the requirements of this License, to extend the patent 495 | license to downstream recipients. "Knowingly relying" means you have 496 | actual knowledge that, but for the patent license, your conveying the 497 | covered work in a country, or your recipient's use of the covered work 498 | in a country, would infringe one or more identifiable patents in that 499 | country that you have reason to believe are valid. 500 | 501 | If, pursuant to or in connection with a single transaction or 502 | arrangement, you convey, or propagate by procuring conveyance of, a 503 | covered work, and grant a patent license to some of the parties 504 | receiving the covered work authorizing them to use, propagate, modify 505 | or convey a specific copy of the covered work, then the patent license 506 | you grant is automatically extended to all recipients of the covered 507 | work and works based on it. 508 | 509 | A patent license is "discriminatory" if it does not include within 510 | the scope of its coverage, prohibits the exercise of, or is 511 | conditioned on the non-exercise of one or more of the rights that are 512 | specifically granted under this License. You may not convey a covered 513 | work if you are a party to an arrangement with a third party that is 514 | in the business of distributing software, under which you make payment 515 | to the third party based on the extent of your activity of conveying 516 | the work, and under which the third party grants, to any of the 517 | parties who would receive the covered work from you, a discriminatory 518 | patent license (a) in connection with copies of the covered work 519 | conveyed by you (or copies made from those copies), or (b) primarily 520 | for and in connection with specific products or compilations that 521 | contain the covered work, unless you entered into that arrangement, 522 | or that patent license was granted, prior to 28 March 2007. 523 | 524 | Nothing in this License shall be construed as excluding or limiting 525 | any implied license or other defenses to infringement that may 526 | otherwise be available to you under applicable patent law. 527 | 528 | 12. No Surrender of Others' Freedom. 529 | 530 | If conditions are imposed on you (whether by court order, agreement or 531 | otherwise) that contradict the conditions of this License, they do not 532 | excuse you from the conditions of this License. If you cannot convey a 533 | covered work so as to satisfy simultaneously your obligations under this 534 | License and any other pertinent obligations, then as a consequence you may 535 | not convey it at all. For example, if you agree to terms that obligate you 536 | to collect a royalty for further conveying from those to whom you convey 537 | the Program, the only way you could satisfy both those terms and this 538 | License would be to refrain entirely from conveying the Program. 539 | 540 | 13. Remote Network Interaction; Use with the GNU General Public License. 541 | 542 | Notwithstanding any other provision of this License, if you modify the 543 | Program, your modified version must prominently offer all users 544 | interacting with it remotely through a computer network (if your version 545 | supports such interaction) an opportunity to receive the Corresponding 546 | Source of your version by providing access to the Corresponding Source 547 | from a network server at no charge, through some standard or customary 548 | means of facilitating copying of software. This Corresponding Source 549 | shall include the Corresponding Source for any work covered by version 3 550 | of the GNU General Public License that is incorporated pursuant to the 551 | following paragraph. 552 | 553 | Notwithstanding any other provision of this License, you have 554 | permission to link or combine any covered work with a work licensed 555 | under version 3 of the GNU General Public License into a single 556 | combined work, and to convey the resulting work. The terms of this 557 | License will continue to apply to the part which is the covered work, 558 | but the work with which it is combined will remain governed by version 559 | 3 of the GNU General Public License. 560 | 561 | 14. Revised Versions of this License. 562 | 563 | The Free Software Foundation may publish revised and/or new versions of 564 | the GNU Affero General Public License from time to time. Such new versions 565 | will be similar in spirit to the present version, but may differ in detail to 566 | address new problems or concerns. 567 | 568 | Each version is given a distinguishing version number. If the 569 | Program specifies that a certain numbered version of the GNU Affero General 570 | Public License "or any later version" applies to it, you have the 571 | option of following the terms and conditions either of that numbered 572 | version or of any later version published by the Free Software 573 | Foundation. If the Program does not specify a version number of the 574 | GNU Affero General Public License, you may choose any version ever published 575 | by the Free Software Foundation. 576 | 577 | If the Program specifies that a proxy can decide which future 578 | versions of the GNU Affero General Public License can be used, that proxy's 579 | public statement of acceptance of a version permanently authorizes you 580 | to choose that version for the Program. 581 | 582 | Later license versions may give you additional or different 583 | permissions. However, no additional obligations are imposed on any 584 | author or copyright holder as a result of your choosing to follow a 585 | later version. 586 | 587 | 15. Disclaimer of Warranty. 588 | 589 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 590 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 591 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 592 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 593 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 594 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 595 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 596 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 597 | 598 | 16. Limitation of Liability. 599 | 600 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 601 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 602 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 603 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 604 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 605 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 606 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 607 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 608 | SUCH DAMAGES. 609 | 610 | 17. Interpretation of Sections 15 and 16. 611 | 612 | If the disclaimer of warranty and limitation of liability provided 613 | above cannot be given local legal effect according to their terms, 614 | reviewing courts shall apply local law that most closely approximates 615 | an absolute waiver of all civil liability in connection with the 616 | Program, unless a warranty or assumption of liability accompanies a 617 | copy of the Program in return for a fee. 618 | 619 | END OF TERMS AND CONDITIONS 620 | 621 | How to Apply These Terms to Your New Programs 622 | 623 | If you develop a new program, and you want it to be of the greatest 624 | possible use to the public, the best way to achieve this is to make it 625 | free software which everyone can redistribute and change under these terms. 626 | 627 | To do so, attach the following notices to the program. It is safest 628 | to attach them to the start of each source file to most effectively 629 | state the exclusion of warranty; and each file should have at least 630 | the "copyright" line and a pointer to where the full notice is found. 631 | 632 | 633 | Copyright (C) 634 | 635 | This program is free software: you can redistribute it and/or modify 636 | it under the terms of the GNU Affero General Public License as published by 637 | the Free Software Foundation, either version 3 of the License, or 638 | (at your option) any later version. 639 | 640 | This program is distributed in the hope that it will be useful, 641 | but WITHOUT ANY WARRANTY; without even the implied warranty of 642 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 643 | GNU Affero General Public License for more details. 644 | 645 | You should have received a copy of the GNU Affero General Public License 646 | along with this program. If not, see . 647 | 648 | Also add information on how to contact you by electronic and paper mail. 649 | 650 | If your software can interact with users remotely through a computer 651 | network, you should also make sure that it provides a way for users to 652 | get its source. For example, if your program is a web application, its 653 | interface could display a "Source" link that leads users to an archive 654 | of the code. There are many ways you could offer source, and different 655 | solutions will be better for different programs; see section 13 for the 656 | specific requirements. 657 | 658 | You should also get your employer (if you work as a programmer) or school, 659 | if any, to sign a "copyright disclaimer" for the program, if necessary. 660 | For more information on this, and how to apply and follow the GNU AGPL, see 661 | . -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Brainfuck JIT 虚拟机教程 2 | 3 | 当我们谈到 JIT 时,通常会想到 V8、JVM 之类的庞然大物,然后望而生畏,觉得 JIT 是一种极其高深复杂的技术。 4 | 5 | 但 JIT 也可以变得非常简单,我们不需要做完善的优化和分析,只要输入源码,输出机器指令,再执行,这和普通的文本处理程序没什么区别。 6 | 7 | 在本教程中,我们将用 Rust 语言实现一个简单的 Brainfuck JIT 虚拟机,逐步理解 JIT 技术。 8 | 9 | 教程地址: 10 | 11 | 如果本教程对您有帮助,请[赞助我的开源工作](https://github.com/Nugine#sponsor)。 12 | -------------------------------------------------------------------------------- /bf/cgbfi2.bf: -------------------------------------------------------------------------------- 1 | Brainfuck Self Interpreter: by Clive Gifford 2 | 3 | Version 1: 01 December 2006 (one trip between code and data per op) 4 | Version 2: 16 December 2006 (virtual codes for various combinations) 5 | 6 | Credits 7 | 8 | A large section of code to load the input to the interpreter is copied 9 | from the the 423 byte dbfi interpreter as described in the November 2003 10 | paper "A Very Small Interpeter" by Oleg Mazonka and Daniel B Cristofani 11 | 12 | Goals 13 | 14 | The goal for this interpreter was to be efficient rather than small and 15 | particularly to allow as many copies of itself as possible to be "stacked" 16 | up with something else on top / In other words to achieve a low "eigenratio" 17 | (See http eigenratios dot blogspot dot com for more information) 18 | 19 | The main idea of the first version was to only make one round trip between 20 | the emulated code and emulated data for each instruction executed instead 21 | of multiple round trips which is what Daniel and Oleg's version does 22 | 23 | The second version does more pre processing of the guest program in order 24 | to map several common sequences to virtual codes thus reducing the memory 25 | footprint and also further reducing the number of round trips between the 26 | emulated code and data 27 | 28 | Other info: 29 | 30 | The input must consist of valid brainfuck code (to be interpreted) which 31 | must always be followed by an exclamation mark and then any associated data 32 | Input can also include "comments" (if desired) except for exclamation mark 33 | 34 | If you are stacking multiple copies of this interpreter then each additional 35 | level also has to appear in the input with a trailing exclamation mark and 36 | then we finally have the input for the very top level to finish things off 37 | 38 | The underlying brainfuck machine determines the possible range of values in 39 | the data cell values and what happens if an attempt is made to go outside the 40 | supported range but this interpreter does not more than 8 bit data itself 41 | 42 | Loops in the emulated code can be nested up to the maximum cell value in the 43 | underlying machine and this interpreter requires that at least 17 levels of 44 | nesting is supported 45 | 46 | Behaviour on end of input is also inherited from the next level down 47 | 48 | >>>> leave a little extra space before the program code 49 | + start in left hand cell of first program code pair 50 | [ 51 | ->>> clear flag and move to the starting position 52 | ++>+>+++++++ setup differences and read char as per dbfi 53 | [<++++>>++<-]++>>+>+> (by Daniel B Cristofani) 54 | +++++[>++>++++++<<-] 55 | +>>>,<++ 56 | [ 57 | [>[->>]<[>>]<<-] see section 3 of dbfi paper 58 | <[<]<+>>[>]> 59 | [ see section 4 of dbfi paper 60 | <+>- 61 | [[<+>-]>] see section 5 of dbfi paper 62 | < see section 6 of dbfi paper 63 | [ 64 | [[-]<] scan left and zero the differences 65 | ++<- see section 7 of dbfi paper 66 | [ 67 | <+++++++++>[<->-]>> 68 | ] 69 | >> 70 | ] 71 | ] 72 | << 73 | ] 74 | 75 | a three way switch to handle possibilities and adjust positioning 76 | 77 | >[-]+<< set "done" flag and position to value 78 | [ 79 | -- 2 means last decode was for a valid instruction 80 | [ so if we still have something left now it was a 9 81 | [-] originally (meaning input should be ignored) 82 | >>- <<<+> so all we do is set the "more input" flag 83 | ] 84 | >> 85 | [ 86 | - 87 | <<<<[>+<-] for valid input move everything right one 88 | 89 | start of processing to find and recode certain combinations of codes 90 | 91 | +<<+ set flags to indicate we want to process 92 | the last two instructions in the loop below 93 | [ 94 | -> clear flag and move to instruction 95 | [<+>>>>>>+<<<<<-] double copy instruction and then 96 | <[>+<-]>>>>>> restore original before moving to copy 97 | 98 | map relevant codes to values giving unique pairwise sums and also 99 | set a flag if we see the end of a loop 100 | 101 | >+< 102 | [ 103 | -[ 104 | -[ 105 | -[ 106 | -[ 107 | -[ 108 | -[ 109 | -[ 110 | -[ 111 | [-] 112 | >-< 113 | ]>[-<<+++++++>>] < 114 | ] 115 | ] 116 | ]>[-]< 117 | ]>[-<<+++>>]< 118 | ]>[-<<+>>]< 119 | ]>[-]< 120 | ]>[-<<<<<<<+>>>>>>>]< set flag if it is end of loop 121 | ]>[-]< 122 | <<<< goto next instruction flag 123 | ] 124 | 125 | add values from above together to get unique sum 126 | 127 | >>>[<<+>>-] 128 | <+< 129 | 130 | setup a switch to figure out what it means 131 | 132 | [ 133 | -[ 134 | -[ 135 | -[ 136 | -[ 137 | -[ 138 | -[ 139 | -[ 140 | -[ 141 | -[ 142 | -[ 143 | [-] 144 | >-<<<[-]<<+>> change code 8 (add 1) to 9 (add 2) 145 | ] 146 | ] 147 | ] 148 | ]>[-]< 149 | ]>[-<<<[-]<<+++++++>>>]< change code 4 (left) to 11 (left 2) 150 | ] 151 | ] 152 | ]>[-]< 153 | ]>[-<<<[-]<<+++++++>>>]< change code 3 (right) to 10 (right 2) 154 | ] 155 | ]>[-]< 156 | 157 | clear flag set if second to last was end of loop 158 | and go to similar flag for last instruction 159 | 160 | <<<<<[-]>> 161 | [ 162 | - 163 | <<<[>+>>+<<<-]>[<+>-]>> copy third to last instruction 164 | 165 | if it is the start of a loop then we can (in some cases) 166 | collapse the last three instructions to single virtual code 167 | 168 | [ 169 | -[ 170 | -[ 171 | [-] 172 | >> 173 | ] 174 | > 175 | [ Now we are ready to check what code is in the loop (must be at least 1) 176 | <<[<+>>+<-]>[<+>-]+<< 177 | - 178 | [ 179 | -[ 180 | -[ 181 | -[ 182 | -[ 183 | -[ 184 | -[ 185 | -[ 186 | -[ 187 | -[ 188 | - 189 | <+> fall through & code as 16 (double skip left) 190 | ]<+++++++++++++>>[-]>->-<< code as 15 (double skip right) 191 | ] 192 | ] 193 | ]>>[->>>>>]<< 194 | ]>>[-<<<++++++++++++>>[-]>>-]<< code as 14 (zero) 195 | ]>>[->>>>>]<< 196 | ]>>[-<<<+++++++++++>>[-]>>-]<< code as 13 (skip left) 197 | ]>>[-<<<++++++++++>>[-]>>-]<< code as 12 (skip right) 198 | ] 199 | ]>>[->>>>>]<< 200 | ] 201 | < 202 | ] 203 | ]>[>>]< 204 | << 205 | ] 206 | >>+>>> 207 | ] 208 | << 209 | ] 210 | >> [->+>] << end of input so clear "done" and set data mark and 211 | finally position to a zero cell ready for next phase 212 | 213 | < move to "more input" flag 214 | 215 | ] 216 | 217 | <<[<<]>> go to the first instruction 218 | 219 | ******** MAIN INTERPRETER LOOPS STARTS HERE ******** 220 | 221 | [ start on current instruction code 222 | setup a big switch statement to decode instructions 223 | [<+>>+<-] move/copy instruction code and set "done" flag to 224 | +<- start with '(i less 1) (1) (i) for i = instruction 225 | [ 226 | -[ 227 | -[ 228 | -[ 229 | -[ 230 | -[ 231 | -[ 232 | -[ 233 | -[ 234 | -[ 235 | -[ 236 | -[ 237 | -[ 238 | -[ 239 | -[ 240 | - can't be anything but 1 so bracketing not needed 241 | >->> [>>] >> [>>]< [<-<<-<] <[<<] << [<<]< double skip left (code 16) 242 | ] 243 | >[->> [>>] >> [>>]< [>+>>+>] <[<<] << [<<]]< double skip right (code 15) 244 | ] 245 | >[->> [>>] >> [>>] <[-]< [<<] << [<<]]< zero (code 14) 246 | ] 247 | >[->> [>>] >> [>>]< [<-<] <[<<] << [<<]]< skip left (code 13) 248 | ] 249 | >[->> [>>] >> [>>]< [>+>] <[<<] << [<<]]< skip right (code 12) 250 | ] 251 | >[->> [>>] >> [>>]< <-<<-< <[<<] << [<<]]< double move left (code 11) 252 | ] 253 | >[->> [>>] >> [>>] +>>+ [<<] << [<<]]< double move right (code 10) 254 | ] 255 | >[->> [>>] >> [>>]< ++ <[<<] << [<<]]< add 2 (code 9) 256 | ] 257 | >[->> [>>] >> [>>]< + <[<<] << [<<]]< increment 258 | ] 259 | >[->> [>>] >> [>>]< , <[<<] << [<<]]< input 260 | ] 261 | >[->> [>>] >> [>>]< - <[<<] << [<<]]< decrement 262 | ] 263 | >[->> [>>] >> [>>]< . <[<<] << [<<]]< output 264 | ] 265 | >[->> [>>] >> [>>] <<-<< [<<] << [<<]]< move left 266 | ] 267 | >[->> [>>] >> [>>] + [<<] << [<<]]< move right 268 | ] 269 | > 270 | [- left hand bracket 271 | >> [>>] >> [>>]< move to data cell 272 | [>+>>+<<<-]> make double copy and move to first 273 | [<+>-] restore original data cell value 274 | >>[<<+>>[-]]+ This and the following achieves 275 | <<[>>-<<-] x = not x 276 | >> go to flag cell (0 or 1) 277 | 278 | Some tricky stuff here: set up (not flag) also so we can later choose 279 | appropriate instruction sequence to get back to code area in one pass 280 | In one case we set flags at the other end (data greater than 0) but 281 | for the other we just go back without setting any flags (data equals 0) 282 | 283 | [<<+>>>>+<<-] make two copies of flag 284 | >>[<<+>>-] 285 | <<[>>+<<-]+ This and the following achieves 286 | >>[<<->>-]<< x = not x 287 | 288 | << so we now have (data) '(flag) (?) (not flag) 289 | 290 | [ if flag set then 291 | -<< [<<] << [<<]< clear and return to code section where we save 292 | << << ++ a 2 meaning we need (later) to match left bracket 293 | >> stop in zero cell for now 294 | ] 295 | 296 | >> if we executed code above then now at switch flag 297 | else it will put us ready to return from data area 298 | 299 | [-<<<<<<[<<]<<[<<]<] move back to switch flag without setting anything 300 | 301 | > 302 | ] 303 | < 304 | ] 305 | > 306 | [- right hand bracket 307 | >> [>>] >> [>>]< move to data cell 308 | [>+>>+<<<-]> make double copy and move to first 309 | [[<+>-]>>[-]+<<] restore data from one then zero second and set flag 310 | >> go to flag cell (0 or 1) 311 | 312 | Some tricky stuff here: set up (not flag) also so we can later choose 313 | appropriate instruction sequence to get back to code area in one pass 314 | In one case we set flags at the other end (data greater than 0) but 315 | for the other we just go back without setting any flags (data equals 0) 316 | 317 | [<<+>>>>+<<-] make two copes of flag 318 | >>[<<+>>-] 319 | <<[>>+<<-]+ This and the following achieves 320 | >>[<<->>-]<< x = not x 321 | 322 | << so we now have (data) '(flag) (?) (not flag) 323 | 324 | [ if flag set then 325 | -<< [<<] << [<<]< clear and return to code section where we save 326 | << << + a 1 meaning we need (later) to match right bracket 327 | >> stop in zero cell for now 328 | ] 329 | 330 | >> if we executed code above then now at switch flag 331 | else it will put us ready to return from data area 332 | 333 | [-<<<<<<[<<]<<[<<]<] move back to switch flag without setting anything 334 | 335 | > 336 | ] 337 | 338 | >[<+>-] restore original instruction code 339 | 340 | *** We are positioned in the cell immediately to the right of the *** 341 | *** instruction that has just been "executed" in the switch above *** 342 | *** The following code is to handle finding matching brackets *** 343 | *** because code above has only set a cell value to 1 or 2 to show *** 344 | *** what kind of loop scanning is required (1=scan left 2=right) *** 345 | 346 | << << << position to cell showing if matching required 347 | [ if non zero we need to find a matching bracket 348 | >> + set up "done" flag for switch and 349 | << - decrement switch value so now is 0 or 1 350 | [ if 1 we are looking for matching right bracket 351 | - >> - >> + clear switch value & "done" & set level to 1 352 | [ while level is more than 0 353 | >>>[-<+>>+<] make double copy of instruction code 354 | +<- set flag and prepare for switch 355 | [ 356 | -[ 357 | [-] clear whatever is left of code 358 | > - < do nothing except clear flag 359 | ] 360 | > [- <<< + >>>] < increment level 361 | ] 362 | > [- <<< - >>>] decrement level 363 | 364 | >[-<+>]<< restore instruction code 365 | 366 | << go to level 367 | [>>+<<-] if level then move right one instruction 368 | >> 369 | ] 370 | << << << go back to switch value cell 371 | ] 372 | >> go to switch done flag and if still set then 373 | [ we must be looking for a matching left bracket 374 | - << + clear switch value & "done" & set level to 1 375 | [ repeat while level is more than 0 376 | >>>[-<+>>+<] make double copy of instruction code 377 | +<- set flag and prepare for switch 378 | [ 379 | -[ 380 | [-] clear whatever is left of code 381 | > - < do nothing except clear flag 382 | ] 383 | > [- <<< - >>>] < decrement level 384 | ] 385 | > [- <<< + >>>] increment level 386 | 387 | >[-<+>]<< restore instruction code 388 | 389 | << go to level 390 | [<<+>>-] if level then move left one instruction 391 | << 392 | ] 393 | ] 394 | ] 395 | 396 | >> >> >> 397 | 398 | > move forward to next instruction 399 | ] 400 | -------------------------------------------------------------------------------- /bf/hello.bf: -------------------------------------------------------------------------------- 1 | [ This program prints "Hello World" and a newline to the screen, its 2 | length is 106 active command characters. [It is not the shortest.] 3 | 4 | This loop is an "initial comment loop", a simple way of adding a comment 5 | to a BF program such that you don't have to worry about any command 6 | characters. Any ".", ",", "+", "-", "<" and ">" characters are simply 7 | ignored, the "[" and "]" characters just have to be balanced. This 8 | loop and the commands it contains are ignored because the current cell 9 | defaults to a value of 0; the 0 value causes this loop to be skipped. 10 | ] 11 | ++++++++ Set Cell #0 to 8 12 | [ 13 | >++++ Add 4 to Cell #1; this will always set Cell #1 to 4 14 | [ as the cell will be cleared by the loop 15 | >++ Add 2 to Cell #2 16 | >+++ Add 3 to Cell #3 17 | >+++ Add 3 to Cell #4 18 | >+ Add 1 to Cell #5 19 | <<<<- Decrement the loop counter in Cell #1 20 | ] Loop till Cell #1 is zero; number of iterations is 4 21 | >+ Add 1 to Cell #2 22 | >+ Add 1 to Cell #3 23 | >- Subtract 1 from Cell #4 24 | >>+ Add 1 to Cell #6 25 | [<] Move back to the first zero cell you find; this will 26 | be Cell #1 which was cleared by the previous loop 27 | <- Decrement the loop Counter in Cell #0 28 | ] Loop till Cell #0 is zero; number of iterations is 8 29 | 30 | The result of this is: 31 | Cell No : 0 1 2 3 4 5 6 32 | Contents: 0 0 72 104 88 32 8 33 | Pointer : ^ 34 | 35 | >>. Cell #2 has value 72 which is 'H' 36 | >---. Subtract 3 from Cell #3 to get 101 which is 'e' 37 | +++++++..+++. Likewise for 'llo' from Cell #3 38 | >>. Cell #5 is 32 for the space 39 | <-. Subtract 1 from Cell #4 for 87 to give a 'W' 40 | <. Cell #3 was set to 'o' from the end of 'Hello' 41 | +++.------.--------. Cell #3 for 'rl' and 'd' 42 | >>+. Add 1 to Cell #5 gives us an exclamation point 43 | >++. And finally a newline from Cell #6 -------------------------------------------------------------------------------- /bf/mendelbrot.bf: -------------------------------------------------------------------------------- 1 | [A mandelbrot set fractal viewer in brainf*** written by Erik Bosman] 2 | +++++++++++++[->++>>>+++++>++>+<<<<<<]>>>>>++++++>--->>>>>>>>>>+++++++++++++++[[ 3 | >>>>>>>>>]+[<<<<<<<<<]>>>>>>>>>-]+[>>>>>>>>[-]>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>[-]+ 4 | <<<<<<<+++++[-[->>>>>>>>>+<<<<<<<<<]>>>>>>>>>]>>>>>>>+>>>>>>>>>>>>>>>>>>>>>>>>>> 5 | >+<<<<<<<<<<<<<<<<<[<<<<<<<<<]>>>[-]+[>>>>>>[>>>>>>>[-]>>]<<<<<<<<<[<<<<<<<<<]>> 6 | >>>>>[-]+<<<<<<++++[-[->>>>>>>>>+<<<<<<<<<]>>>>>>>>>]>>>>>>+<<<<<<+++++++[-[->>> 7 | >>>>>>+<<<<<<<<<]>>>>>>>>>]>>>>>>+<<<<<<<<<<<<<<<<[<<<<<<<<<]>>>[[-]>>>>>>[>>>>> 8 | >>[-<<<<<<+>>>>>>]<<<<<<[->>>>>>+<<+<<<+<]>>>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>> 9 | [>>>>>>>>[-<<<<<<<+>>>>>>>]<<<<<<<[->>>>>>>+<<+<<<+<<]>>>>>>>>]<<<<<<<<<[<<<<<<< 10 | <<]>>>>>>>[-<<<<<<<+>>>>>>>]<<<<<<<[->>>>>>>+<<+<<<<<]>>>>>>>>>+++++++++++++++[[ 11 | >>>>>>>>>]+>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>-]+[ 12 | >+>>>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>[>->>>>[-<<<<+>>>>]<<<<[->>>>+<<<<<[->>[ 13 | -<<+>>]<<[->>+>>+<<<<]+>>>>>>>>>]<<<<<<<<[<<<<<<<<<]]>>>>>>>>>[>>>>>>>>>]<<<<<<< 14 | <<[>[->>>>>>>>>+<<<<<<<<<]<<<<<<<<<<]>[->>>>>>>>>+<<<<<<<<<]<+>>>>>>>>]<<<<<<<<< 15 | [>[-]<->>>>[-<<<<+>[<->-<<<<<<+>>>>>>]<[->+<]>>>>]<<<[->>>+<<<]<+<<<<<<<<<]>>>>> 16 | >>>>[>+>>>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>[>->>>>>[-<<<<<+>>>>>]<<<<<[->>>>>+ 17 | <<<<<<[->>>[-<<<+>>>]<<<[->>>+>+<<<<]+>>>>>>>>>]<<<<<<<<[<<<<<<<<<]]>>>>>>>>>[>> 18 | >>>>>>>]<<<<<<<<<[>>[->>>>>>>>>+<<<<<<<<<]<<<<<<<<<<<]>>[->>>>>>>>>+<<<<<<<<<]<< 19 | +>>>>>>>>]<<<<<<<<<[>[-]<->>>>[-<<<<+>[<->-<<<<<<+>>>>>>]<[->+<]>>>>]<<<[->>>+<< 20 | <]<+<<<<<<<<<]>>>>>>>>>[>>>>[-<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>> 21 | >>>>>>>>>>>>>>>>>>>>>>>]>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>+++++++++++++++[[>>>> 22 | >>>>>]<<<<<<<<<-<<<<<<<<<[<<<<<<<<<]>>>>>>>>>-]+>>>>>>>>>>>>>>>>>>>>>+<<<[<<<<<< 23 | <<<]>>>>>>>>>[>>>[-<<<->>>]+<<<[->>>->[-<<<<+>>>>]<<<<[->>>>+<<<<<<<<<<<<<[<<<<< 24 | <<<<]>>>>[-]+>>>>>[>>>>>>>>>]>+<]]+>>>>[-<<<<->>>>]+<<<<[->>>>-<[-<<<+>>>]<<<[-> 25 | >>+<<<<<<<<<<<<[<<<<<<<<<]>>>[-]+>>>>>>[>>>>>>>>>]>[-]+<]]+>[-<[>>>>>>>>>]<<<<<< 26 | <<]>>>>>>>>]<<<<<<<<<[<<<<<<<<<]<<<<<<<[->+>>>-<<<<]>>>>>>>>>+++++++++++++++++++ 27 | +++++++>>[-<<<<+>>>>]<<<<[->>>>+<<[-]<<]>>[<<<<<<<+<[-<+>>>>+<<[-]]>[-<<[->+>>>- 28 | <<<<]>>>]>>>>>>>>>>>>>[>>[-]>[-]>[-]>>>>>]<<<<<<<<<[<<<<<<<<<]>>>[-]>>>>>>[>>>>> 29 | [-<<<<+>>>>]<<<<[->>>>+<<<+<]>>>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>[>>[-<<<<<<<< 30 | <+>>>>>>>>>]>>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>+++++++++++++++[[>>>>>>>>>]+>[- 31 | ]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>-]+[>+>>>>>>>>]<<< 32 | <<<<<<[<<<<<<<<<]>>>>>>>>>[>->>>>>[-<<<<<+>>>>>]<<<<<[->>>>>+<<<<<<[->>[-<<+>>]< 33 | <[->>+>+<<<]+>>>>>>>>>]<<<<<<<<[<<<<<<<<<]]>>>>>>>>>[>>>>>>>>>]<<<<<<<<<[>[->>>> 34 | >>>>>+<<<<<<<<<]<<<<<<<<<<]>[->>>>>>>>>+<<<<<<<<<]<+>>>>>>>>]<<<<<<<<<[>[-]<->>> 35 | [-<<<+>[<->-<<<<<<<+>>>>>>>]<[->+<]>>>]<<[->>+<<]<+<<<<<<<<<]>>>>>>>>>[>>>>>>[-< 36 | <<<<+>>>>>]<<<<<[->>>>>+<<<<+<]>>>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>[>+>>>>>>>> 37 | ]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>[>->>>>>[-<<<<<+>>>>>]<<<<<[->>>>>+<<<<<<[->>[-<<+ 38 | >>]<<[->>+>>+<<<<]+>>>>>>>>>]<<<<<<<<[<<<<<<<<<]]>>>>>>>>>[>>>>>>>>>]<<<<<<<<<[> 39 | [->>>>>>>>>+<<<<<<<<<]<<<<<<<<<<]>[->>>>>>>>>+<<<<<<<<<]<+>>>>>>>>]<<<<<<<<<[>[- 40 | ]<->>>>[-<<<<+>[<->-<<<<<<+>>>>>>]<[->+<]>>>>]<<<[->>>+<<<]<+<<<<<<<<<]>>>>>>>>> 41 | [>>>>[-<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 42 | ]>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>[>>>[-<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+> 43 | >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>]>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>++++++++ 44 | +++++++[[>>>>>>>>>]<<<<<<<<<-<<<<<<<<<[<<<<<<<<<]>>>>>>>>>-]+[>>>>>>>>[-<<<<<<<+ 45 | >>>>>>>]<<<<<<<[->>>>>>>+<<<<<<+<]>>>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>[>>>>>>[ 46 | -]>>>]<<<<<<<<<[<<<<<<<<<]>>>>+>[-<-<<<<+>>>>>]>[-<<<<<<[->>>>>+<++<<<<]>>>>>[-< 47 | <<<<+>>>>>]<->+>]<[->+<]<<<<<[->>>>>+<<<<<]>>>>>>[-]<<<<<<+>>>>[-<<<<->>>>]+<<<< 48 | [->>>>->>>>>[>>[-<<->>]+<<[->>->[-<<<+>>>]<<<[->>>+<<<<<<<<<<<<[<<<<<<<<<]>>>[-] 49 | +>>>>>>[>>>>>>>>>]>+<]]+>>>[-<<<->>>]+<<<[->>>-<[-<<+>>]<<[->>+<<<<<<<<<<<[<<<<< 50 | <<<<]>>>>[-]+>>>>>[>>>>>>>>>]>[-]+<]]+>[-<[>>>>>>>>>]<<<<<<<<]>>>>>>>>]<<<<<<<<< 51 | [<<<<<<<<<]>>>>[-<<<<+>>>>]<<<<[->>>>+>>>>>[>+>>[-<<->>]<<[->>+<<]>>>>>>>>]<<<<< 52 | <<<+<[>[->>>>>+<<<<[->>>>-<<<<<<<<<<<<<<+>>>>>>>>>>>[->>>+<<<]<]>[->>>-<<<<<<<<< 53 | <<<<<+>>>>>>>>>>>]<<]>[->>>>+<<<[->>>-<<<<<<<<<<<<<<+>>>>>>>>>>>]<]>[->>>+<<<]<< 54 | <<<<<<<<<<]>>>>[-]<<<<]>>>[-<<<+>>>]<<<[->>>+>>>>>>[>+>[-<->]<[->+<]>>>>>>>>]<<< 55 | <<<<<+<[>[->>>>>+<<<[->>>-<<<<<<<<<<<<<<+>>>>>>>>>>[->>>>+<<<<]>]<[->>>>-<<<<<<< 56 | <<<<<<<+>>>>>>>>>>]<]>>[->>>+<<<<[->>>>-<<<<<<<<<<<<<<+>>>>>>>>>>]>]<[->>>>+<<<< 57 | ]<<<<<<<<<<<]>>>>>>+<<<<<<]]>>>>[-<<<<+>>>>]<<<<[->>>>+>>>>>[>>>>>>>>>]<<<<<<<<< 58 | [>[->>>>>+<<<<[->>>>-<<<<<<<<<<<<<<+>>>>>>>>>>>[->>>+<<<]<]>[->>>-<<<<<<<<<<<<<< 59 | +>>>>>>>>>>>]<<]>[->>>>+<<<[->>>-<<<<<<<<<<<<<<+>>>>>>>>>>>]<]>[->>>+<<<]<<<<<<< 60 | <<<<<]]>[-]>>[-]>[-]>>>>>[>>[-]>[-]>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>[>>>>>[-< 61 | <<<+>>>>]<<<<[->>>>+<<<+<]>>>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>+++++++++++++++[ 62 | [>>>>>>>>>]+>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>-]+ 63 | [>+>>>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>[>->>>>[-<<<<+>>>>]<<<<[->>>>+<<<<<[->> 64 | [-<<+>>]<<[->>+>+<<<]+>>>>>>>>>]<<<<<<<<[<<<<<<<<<]]>>>>>>>>>[>>>>>>>>>]<<<<<<<< 65 | <[>[->>>>>>>>>+<<<<<<<<<]<<<<<<<<<<]>[->>>>>>>>>+<<<<<<<<<]<+>>>>>>>>]<<<<<<<<<[ 66 | >[-]<->>>[-<<<+>[<->-<<<<<<<+>>>>>>>]<[->+<]>>>]<<[->>+<<]<+<<<<<<<<<]>>>>>>>>>[ 67 | >>>[-<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>]> 68 | >>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>[-]>>>>+++++++++++++++[[>>>>>>>>>]<<<<<<<<<-<<<<< 69 | <<<<[<<<<<<<<<]>>>>>>>>>-]+[>>>[-<<<->>>]+<<<[->>>->[-<<<<+>>>>]<<<<[->>>>+<<<<< 70 | <<<<<<<<[<<<<<<<<<]>>>>[-]+>>>>>[>>>>>>>>>]>+<]]+>>>>[-<<<<->>>>]+<<<<[->>>>-<[- 71 | <<<+>>>]<<<[->>>+<<<<<<<<<<<<[<<<<<<<<<]>>>[-]+>>>>>>[>>>>>>>>>]>[-]+<]]+>[-<[>> 72 | >>>>>>>]<<<<<<<<]>>>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>[-<<<+>>>]<<<[->>>+>>>>>>[>+>>> 73 | [-<<<->>>]<<<[->>>+<<<]>>>>>>>>]<<<<<<<<+<[>[->+>[-<-<<<<<<<<<<+>>>>>>>>>>>>[-<< 74 | +>>]<]>[-<<-<<<<<<<<<<+>>>>>>>>>>>>]<<<]>>[-<+>>[-<<-<<<<<<<<<<+>>>>>>>>>>>>]<]> 75 | [-<<+>>]<<<<<<<<<<<<<]]>>>>[-<<<<+>>>>]<<<<[->>>>+>>>>>[>+>>[-<<->>]<<[->>+<<]>> 76 | >>>>>>]<<<<<<<<+<[>[->+>>[-<<-<<<<<<<<<<+>>>>>>>>>>>[-<+>]>]<[-<-<<<<<<<<<<+>>>> 77 | >>>>>>>]<<]>>>[-<<+>[-<-<<<<<<<<<<+>>>>>>>>>>>]>]<[-<+>]<<<<<<<<<<<<]>>>>>+<<<<< 78 | ]>>>>>>>>>[>>>[-]>[-]>[-]>>>>]<<<<<<<<<[<<<<<<<<<]>>>[-]>[-]>>>>>[>>>>>>>[-<<<<< 79 | <+>>>>>>]<<<<<<[->>>>>>+<<<<+<<]>>>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>+>[-<-<<<<+>>>> 80 | >]>>[-<<<<<<<[->>>>>+<++<<<<]>>>>>[-<<<<<+>>>>>]<->+>>]<<[->>+<<]<<<<<[->>>>>+<< 81 | <<<]+>>>>[-<<<<->>>>]+<<<<[->>>>->>>>>[>>>[-<<<->>>]+<<<[->>>-<[-<<+>>]<<[->>+<< 82 | <<<<<<<<<[<<<<<<<<<]>>>>[-]+>>>>>[>>>>>>>>>]>+<]]+>>[-<<->>]+<<[->>->[-<<<+>>>]< 83 | <<[->>>+<<<<<<<<<<<<[<<<<<<<<<]>>>[-]+>>>>>>[>>>>>>>>>]>[-]+<]]+>[-<[>>>>>>>>>]< 84 | <<<<<<<]>>>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>[-<<<+>>>]<<<[->>>+>>>>>>[>+>[-<->]<[->+ 85 | <]>>>>>>>>]<<<<<<<<+<[>[->>>>+<<[->>-<<<<<<<<<<<<<+>>>>>>>>>>[->>>+<<<]>]<[->>>- 86 | <<<<<<<<<<<<<+>>>>>>>>>>]<]>>[->>+<<<[->>>-<<<<<<<<<<<<<+>>>>>>>>>>]>]<[->>>+<<< 87 | ]<<<<<<<<<<<]>>>>>[-]>>[-<<<<<<<+>>>>>>>]<<<<<<<[->>>>>>>+<<+<<<<<]]>>>>[-<<<<+> 88 | >>>]<<<<[->>>>+>>>>>[>+>>[-<<->>]<<[->>+<<]>>>>>>>>]<<<<<<<<+<[>[->>>>+<<<[->>>- 89 | <<<<<<<<<<<<<+>>>>>>>>>>>[->>+<<]<]>[->>-<<<<<<<<<<<<<+>>>>>>>>>>>]<<]>[->>>+<<[ 90 | ->>-<<<<<<<<<<<<<+>>>>>>>>>>>]<]>[->>+<<]<<<<<<<<<<<<]]>>>>[-]<<<<]>>>>[-<<<<+>> 91 | >>]<<<<[->>>>+>[-]>>[-<<<<<<<+>>>>>>>]<<<<<<<[->>>>>>>+<<+<<<<<]>>>>>>>>>[>>>>>> 92 | >>>]<<<<<<<<<[>[->>>>+<<<[->>>-<<<<<<<<<<<<<+>>>>>>>>>>>[->>+<<]<]>[->>-<<<<<<<< 93 | <<<<<+>>>>>>>>>>>]<<]>[->>>+<<[->>-<<<<<<<<<<<<<+>>>>>>>>>>>]<]>[->>+<<]<<<<<<<< 94 | <<<<]]>>>>>>>>>[>>[-]>[-]>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>[-]>[-]>>>>>[>>>>>[-<<<<+ 95 | >>>>]<<<<[->>>>+<<<+<]>>>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>[>>>>>>[-<<<<<+>>>>> 96 | ]<<<<<[->>>>>+<<<+<<]>>>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>+++++++++++++++[[>>>> 97 | >>>>>]+>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>-]+[>+>> 98 | >>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>[>->>>>[-<<<<+>>>>]<<<<[->>>>+<<<<<[->>[-<<+ 99 | >>]<<[->>+>>+<<<<]+>>>>>>>>>]<<<<<<<<[<<<<<<<<<]]>>>>>>>>>[>>>>>>>>>]<<<<<<<<<[> 100 | [->>>>>>>>>+<<<<<<<<<]<<<<<<<<<<]>[->>>>>>>>>+<<<<<<<<<]<+>>>>>>>>]<<<<<<<<<[>[- 101 | ]<->>>>[-<<<<+>[<->-<<<<<<+>>>>>>]<[->+<]>>>>]<<<[->>>+<<<]<+<<<<<<<<<]>>>>>>>>> 102 | [>+>>>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>[>->>>>>[-<<<<<+>>>>>]<<<<<[->>>>>+<<<< 103 | <<[->>>[-<<<+>>>]<<<[->>>+>+<<<<]+>>>>>>>>>]<<<<<<<<[<<<<<<<<<]]>>>>>>>>>[>>>>>> 104 | >>>]<<<<<<<<<[>>[->>>>>>>>>+<<<<<<<<<]<<<<<<<<<<<]>>[->>>>>>>>>+<<<<<<<<<]<<+>>> 105 | >>>>>]<<<<<<<<<[>[-]<->>>>[-<<<<+>[<->-<<<<<<+>>>>>>]<[->+<]>>>>]<<<[->>>+<<<]<+ 106 | <<<<<<<<<]>>>>>>>>>[>>>>[-<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>> 107 | >>>>>>>>>>>>>>>>>>>]>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>+++++++++++++++[[>>>>>>>> 108 | >]<<<<<<<<<-<<<<<<<<<[<<<<<<<<<]>>>>>>>>>-]+>>>>>>>>>>>>>>>>>>>>>+<<<[<<<<<<<<<] 109 | >>>>>>>>>[>>>[-<<<->>>]+<<<[->>>->[-<<<<+>>>>]<<<<[->>>>+<<<<<<<<<<<<<[<<<<<<<<< 110 | ]>>>>[-]+>>>>>[>>>>>>>>>]>+<]]+>>>>[-<<<<->>>>]+<<<<[->>>>-<[-<<<+>>>]<<<[->>>+< 111 | <<<<<<<<<<<[<<<<<<<<<]>>>[-]+>>>>>>[>>>>>>>>>]>[-]+<]]+>[-<[>>>>>>>>>]<<<<<<<<]> 112 | >>>>>>>]<<<<<<<<<[<<<<<<<<<]>>->>[-<<<<+>>>>]<<<<[->>>>+<<[-]<<]>>]<<+>>>>[-<<<< 113 | ->>>>]+<<<<[->>>>-<<<<<<.>>]>>>>[-<<<<<<<.>>>>>>>]<<<[-]>[-]>[-]>[-]>[-]>[-]>>>[ 114 | >[-]>[-]>[-]>[-]>[-]>[-]>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>[>>>>>[-]>>>>]<<<<<<<<< 115 | [<<<<<<<<<]>+++++++++++[-[->>>>>>>>>+<<<<<<<<<]>>>>>>>>>]>>>>+>>>>>>>>>+<<<<<<<< 116 | <<<<<<[<<<<<<<<<]>>>>>>>[-<<<<<<<+>>>>>>>]<<<<<<<[->>>>>>>+[-]>>[>>>>>>>>>]<<<<< 117 | <<<<[>>>>>>>[-<<<<<<+>>>>>>]<<<<<<[->>>>>>+<<<<<<<[<<<<<<<<<]>>>>>>>[-]+>>>]<<<< 118 | <<<<<<]]>>>>>>>[-<<<<<<<+>>>>>>>]<<<<<<<[->>>>>>>+>>[>+>>>>[-<<<<->>>>]<<<<[->>> 119 | >+<<<<]>>>>>>>>]<<+<<<<<<<[>>>>>[->>+<<]<<<<<<<<<<<<<<]>>>>>>>>>[>>>>>>>>>]<<<<< 120 | <<<<[>[-]<->>>>>>>[-<<<<<<<+>[<->-<<<+>>>]<[->+<]>>>>>>>]<<<<<<[->>>>>>+<<<<<<]< 121 | +<<<<<<<<<]>>>>>>>-<<<<[-]+<<<]+>>>>>>>[-<<<<<<<->>>>>>>]+<<<<<<<[->>>>>>>->>[>> 122 | >>>[->>+<<]>>>>]<<<<<<<<<[>[-]<->>>>>>>[-<<<<<<<+>[<->-<<<+>>>]<[->+<]>>>>>>>]<< 123 | <<<<[->>>>>>+<<<<<<]<+<<<<<<<<<]>+++++[-[->>>>>>>>>+<<<<<<<<<]>>>>>>>>>]>>>>+<<< 124 | <<[<<<<<<<<<]>>>>>>>>>[>>>>>[-<<<<<->>>>>]+<<<<<[->>>>>->>[-<<<<<<<+>>>>>>>]<<<< 125 | <<<[->>>>>>>+<<<<<<<<<<<<<<<<[<<<<<<<<<]>>>>[-]+>>>>>[>>>>>>>>>]>+<]]+>>>>>>>[-< 126 | <<<<<<->>>>>>>]+<<<<<<<[->>>>>>>-<<[-<<<<<+>>>>>]<<<<<[->>>>>+<<<<<<<<<<<<<<[<<< 127 | <<<<<<]>>>[-]+>>>>>>[>>>>>>>>>]>[-]+<]]+>[-<[>>>>>>>>>]<<<<<<<<]>>>>>>>>]<<<<<<< 128 | <<[<<<<<<<<<]>>>>[-]<<<+++++[-[->>>>>>>>>+<<<<<<<<<]>>>>>>>>>]>>>>-<<<<<[<<<<<<< 129 | <<]]>>>]<<<<.>>>>>>>>>>[>>>>>>[-]>>>]<<<<<<<<<[<<<<<<<<<]>++++++++++[-[->>>>>>>> 130 | >+<<<<<<<<<]>>>>>>>>>]>>>>>+>>>>>>>>>+<<<<<<<<<<<<<<<[<<<<<<<<<]>>>>>>>>[-<<<<<< 131 | <<+>>>>>>>>]<<<<<<<<[->>>>>>>>+[-]>[>>>>>>>>>]<<<<<<<<<[>>>>>>>>[-<<<<<<<+>>>>>> 132 | >]<<<<<<<[->>>>>>>+<<<<<<<<[<<<<<<<<<]>>>>>>>>[-]+>>]<<<<<<<<<<]]>>>>>>>>[-<<<<< 133 | <<<+>>>>>>>>]<<<<<<<<[->>>>>>>>+>[>+>>>>>[-<<<<<->>>>>]<<<<<[->>>>>+<<<<<]>>>>>> 134 | >>]<+<<<<<<<<[>>>>>>[->>+<<]<<<<<<<<<<<<<<<]>>>>>>>>>[>>>>>>>>>]<<<<<<<<<[>[-]<- 135 | >>>>>>>>[-<<<<<<<<+>[<->-<<+>>]<[->+<]>>>>>>>>]<<<<<<<[->>>>>>>+<<<<<<<]<+<<<<<< 136 | <<<]>>>>>>>>-<<<<<[-]+<<<]+>>>>>>>>[-<<<<<<<<->>>>>>>>]+<<<<<<<<[->>>>>>>>->[>>> 137 | >>>[->>+<<]>>>]<<<<<<<<<[>[-]<->>>>>>>>[-<<<<<<<<+>[<->-<<+>>]<[->+<]>>>>>>>>]<< 138 | <<<<<[->>>>>>>+<<<<<<<]<+<<<<<<<<<]>+++++[-[->>>>>>>>>+<<<<<<<<<]>>>>>>>>>]>>>>> 139 | +>>>>>>>>>>>>>>>>>>>>>>>>>>>+<<<<<<[<<<<<<<<<]>>>>>>>>>[>>>>>>[-<<<<<<->>>>>>]+< 140 | <<<<<[->>>>>>->>[-<<<<<<<<+>>>>>>>>]<<<<<<<<[->>>>>>>>+<<<<<<<<<<<<<<<<<[<<<<<<< 141 | <<]>>>>[-]+>>>>>[>>>>>>>>>]>+<]]+>>>>>>>>[-<<<<<<<<->>>>>>>>]+<<<<<<<<[->>>>>>>> 142 | -<<[-<<<<<<+>>>>>>]<<<<<<[->>>>>>+<<<<<<<<<<<<<<<[<<<<<<<<<]>>>[-]+>>>>>>[>>>>>> 143 | >>>]>[-]+<]]+>[-<[>>>>>>>>>]<<<<<<<<]>>>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>[-]<<<++++ 144 | +[-[->>>>>>>>>+<<<<<<<<<]>>>>>>>>>]>>>>>->>>>>>>>>>>>>>>>>>>>>>>>>>>-<<<<<<[<<<< 145 | <<<<<]]>>>] 146 | -------------------------------------------------------------------------------- /book/book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | authors = ["Nugine"] 3 | language = "zh-CN" 4 | multilingual = false 5 | src = "src" 6 | title = "Brainfuck JIT 虚拟机教程" 7 | 8 | [build] 9 | build-dir = "../dist" 10 | -------------------------------------------------------------------------------- /book/src/1.md: -------------------------------------------------------------------------------- 1 | # 1. 背景知识 2 | 3 | ## Brainfuck 简介 4 | 5 | 6 | 7 | Brainfuck 仅包含八个指令,却是图灵完备的,理论上它可以做到任何用图灵机能做到的事情。 8 | 9 | 机器模型:一个字节数组,一个数据指针,一个指令指针,输入流,输出流。 10 | 11 | 数组初始化为全零,数据指针初始时指向数组的第一个字节,指令指针指向第一条指令。 12 | 13 | 字符 '>':将数据指针加一。 14 | 15 | 字符 '<':将数据指针减一。 16 | 17 | 字符 '+':将数据指针所指的单元加一。 18 | 19 | 字符 '-':将数据指针所指的单元减一。 20 | 21 | 字符 ',':从输入流中读取一个字节,存入数据指针所指单元。 22 | 23 | 字符 '.':输出数据指针所指单元的字节。 24 | 25 | 字符 '[':如果当前单元是 0,那么跳转到对应的 ']' 的下一条指令,否则继续执行。 26 | 27 | 字符 ']':如果当前单元不是 0,那么跳转到对应的 '[' 的下一条指令,否则继续执行。 28 | 29 | 30 | Brainfuck 可以直接对应到 C 代码,仅用几十行就能写个从 bf 到 c 的编译器。 31 | 32 | 初始化 33 | 34 | ```c 35 | char array[30000] = {0}; 36 | char *ptr=array; 37 | ``` 38 | 39 | | 指令 | C 代码 | 40 | | ---- | --------------- | 41 | | > | ++ptr; | 42 | | < | --ptr; | 43 | | + | ++*ptr; | 44 | | - | --*ptr; | 45 | | . | putchar(*ptr); | 46 | | , | *ptr=getchar(); | 47 | | [ | while (*ptr) { | 48 | | ] | } | 49 | 50 | Brainfuck 可视化 51 | 52 | 53 | 54 | ## Rust 资源 55 | 56 | Rust 官网 57 | 58 | Rust 一键安装 59 | 60 | Rust 并不是一个能立即上手的语言,如果您是一位 Rust 新手,最好先全面了解 Rust 语言特性。 61 | 62 | Rust 官网入门教程 63 | 64 | 中文书《Rust编程之道》 65 | -------------------------------------------------------------------------------- /book/src/2.md: -------------------------------------------------------------------------------- 1 | # 2. 启动项目 2 | 3 | 初始化项目 4 | 5 | ```bash 6 | cargo new bfjit 7 | cd bfjit 8 | cargo run 9 | ``` 10 | 11 | 空白项目默认为 Hello world 12 | 13 | ``` 14 | Finished dev [unoptimized + debuginfo] target(s) in 0.06s 15 | Running `target/debug/bfjit` 16 | Hello, world! 17 | ``` 18 | 19 | 一切就绪。 -------------------------------------------------------------------------------- /book/src/3.md: -------------------------------------------------------------------------------- 1 | # 3. 中间表示 2 | 3 | 为了方便后期处理,我们先将 Brainfuck 代码转换为一种中间表示 (IR). 4 | 5 | 在 src 目录下添加文件 bfir.rs 6 | 7 | 此时目录结构 8 | 9 | ``` 10 | . 11 | ├── Cargo.lock 12 | ├── Cargo.toml 13 | └── src 14 | ├── bfir.rs 15 | └── main.rs 16 | ``` 17 | 18 | main.rs 19 | 20 | ```rust ,noplaypen 21 | mod bfir; 22 | 23 | fn main() { 24 | println!("Hello, world!"); 25 | } 26 | ``` 27 | 28 | 本节以下代码都将写入 bfir.rs 29 | 30 | ## IR 定义 31 | 32 | AddVal, SubVal 表示将当前单元加减某一数值。 33 | 34 | AddPtr, SubPtr 表示将数据指针加减某一数值。 35 | 36 | Jz,Jnz 表示跳转指令,带有跳转地址。 37 | 38 | ```rust ,noplaypen 39 | {{#include ../../src/bfir.rs:3:13}} 40 | ``` 41 | 42 | ## 错误处理 43 | 44 | 45 | 我们使用 thiserror 库,用来轻松定义错误类型。 46 | 47 | 48 | 49 | 导入第三方库时: 50 | 51 | 若 Rust 版本 >= 1.62,可直接 [cargo add](https://doc.rust-lang.org/cargo/commands/cargo-add.html) 。 52 | 53 | 若 Rust 版本 < 1.62,推荐用 cargo-edit 插件。安装 cargo-edit 插件后,可以用命令导入第三方库。 54 | 55 | 56 | 57 | 也可手动编辑 Cargo.toml。 58 | 59 | ```bash 60 | cargo install cargo-edit 61 | ``` 62 | 63 | ```bash 64 | cargo add thiserror 65 | ``` 66 | 67 | 错误定义 68 | 69 | ```rust ,noplaypen 70 | {{#include ../../src/bfir.rs:15:28}} 71 | ``` 72 | 73 | 为 CompileError 实现 Display 和 Error,Display 用于对人友好的信息,Error 表明它是一个错误类型。 74 | 75 | ```rust ,noplaypen 76 | {{#include ../../src/bfir.rs:23:34}} 77 | ``` 78 | 79 | ## 编译为 IR 80 | 81 | 82 | ```rust ,noplaypen 83 | {{#include ../../src/bfir.rs:38:44}} 84 | ``` 85 | 86 | compile 函数接收一个字符串,返回 IR 序列。 87 | 88 | code 存储已解析的 IR,stk 作为栈,存储左括号的IR位置、源代码行位置、源代码列位置,line 和 col 分别记录行号和列号。 89 | 90 | 主循环结构 91 | 92 | ```rust ,noplaypen 93 | for ch in src.chars() { 94 | col += 1; 95 | match ch { 96 | ... 97 | } 98 | } 99 | ``` 100 | 101 | 处理换行 102 | 103 | ```rust ,noplaypen 104 | '\n' => { 105 | line += 1; 106 | col = 0; 107 | } 108 | ``` 109 | 110 | 处理普通指令 111 | 112 | ```rust ,noplaypen 113 | '+' => code.push(BfIR::AddVal(1)), 114 | '-' => code.push(BfIR::SubVal(1)), 115 | '>' => code.push(BfIR::AddPtr(1)), 116 | '<' => code.push(BfIR::SubPtr(1)), 117 | ',' => code.push(BfIR::GetByte), 118 | '.' => code.push(BfIR::PutByte), 119 | ``` 120 | 121 | 处理左括号,将左括号位置入栈。 122 | 123 | ```rust ,noplaypen 124 | '[' => { 125 | let pos = code.len() as u32; 126 | stk.push((pos, line, col)); 127 | code.push(BfIR::Jz) 128 | } 129 | ``` 130 | 131 | 处理右括号,从栈中弹出左括号位置,如果栈为空,说明没有匹配的左括号,生成一个编译错误并返回。如果有匹配的左括号,则正常生成 IR。 132 | 133 | ```rust ,noplaypen 134 | ']' => { 135 | stk.pop().ok_or(CompileError { 136 | line, 137 | col, 138 | kind: CompileErrorKind::UnexpectedRightBracket, 139 | })?; 140 | 141 | code.push(BfIR::Jnz) 142 | } 143 | ``` 144 | 145 | 忽略其他字符 146 | 147 | ```rust ,noplaypen 148 | _ => {} 149 | ``` 150 | 151 | 循环结束后,如果栈不为空,说明有左括号没有匹配到右括号,弹出左括号位置,生成编译错误。最后返回生成的IR. 152 | 153 | ```rust ,noplaypen 154 | if let Some((_, line, col)) = stk.pop() { 155 | return Err(CompileError { 156 | line, 157 | col, 158 | kind: CompileErrorKind::UnclosedLeftBracket, 159 | }); 160 | } 161 | Ok(code) 162 | ``` 163 | 164 | 完整代码 165 | 166 | ```rust,noplaypen 167 | {{#include ../../src/bfir.rs:38:84}} 168 | ``` 169 | 170 | ## 简单优化 171 | 172 | brainfuck 代码中经常有连续的算术加减和指针移动,这些操作完全可以折叠起来。 173 | 174 | 优化函数直接操作 IR 序列,用一次遍历完成折叠,原地操作。时间复杂度 O(n),空间复杂度 O(1)。 175 | 176 | 这里定义两个宏来避免复制粘贴大量同样的代码。 177 | 178 | ```rust,noplaypen 179 | {{#include ../../src/bfir.rs:88:132}} 180 | ``` 181 | 182 | ## 简单测试 183 | 184 | Rust 内置一套单元测试框架,在模块内随手写个函数,标上 `#[test]`,然后运行命令 `cargo test`。cargo 会收集所有测试,逐个运行,并报告结果。 185 | 186 | ```rust,noplaypen 187 | {{#include ../../src/bfir.rs:134:161}} 188 | ``` 189 | -------------------------------------------------------------------------------- /book/src/4.md: -------------------------------------------------------------------------------- 1 | # 4. Hello, JIT 2 | 3 | 通常来说,Just In Time (JIT) 编译器 是指在某段高阶代码即将运行时将其编译到机器码再执行的程序。下文中,我们把这样的程序叫做 "JIT"。 4 | 5 | 一个最简单的 JIT 工作方式是这样的: 6 | 7 | 1. 将源代码编译为机器码。 8 | 2. 申请一段可写可执行内存,写入机器码。 9 | 3. 跳转到这段内存,执行机器码。 10 | 4. 执行完毕,稍作清理,退出。 11 | 12 | 以下代码引用自 [Hello, JIT World: The Joy of Simple JITs](http://blog.reverberate.org/2012/12/hello-jit-world-joy-of-simple-jits.html) 13 | 14 | ```c 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | int main(int argc, char *argv[]) { 21 | // 机器码: 22 | // mov eax, 0 23 | // ret 24 | unsigned char code[] = {0xb8, 0x00, 0x00, 0x00, 0x00, 0xc3}; 25 | 26 | if (argc < 2) { 27 | fprintf(stderr, "Usage: jit1 \n"); 28 | return 1; 29 | } 30 | 31 | // 把用户给出的数值写入机器码,覆盖立即数 "0" 32 | // mov eax, 33 | // ret 34 | int num = atoi(argv[1]); 35 | memcpy(&code[1], &num, 4); 36 | 37 | // 分配可写可执行内存 38 | // 注意:真实的程序不应该映射同时可写可执行的内存, 39 | // 这里有安全风险。 40 | void *mem = mmap(NULL, sizeof(code), PROT_WRITE | PROT_EXEC, 41 | MAP_ANON | MAP_PRIVATE, -1, 0); 42 | memcpy(mem, code, sizeof(code)); 43 | 44 | // 定义一个函数指针指向机器码内存,再执行函数 45 | int (*func)() = mem; 46 | return func(); 47 | } 48 | ``` 49 | 50 | 编写 JIT 的一大难点是如何生成机器码,这里通常有跨平台问题、可读性消失问题。 51 | 52 | 最笨的方法:写一段汇编,用汇编器生成机器码,再复制到高级代码里。但这样不具有通用性,开发效率非常低。 53 | 54 | ## dynasm 55 | 56 | [DynAsm](http://luajit.org/dynasm.html) 是 LuaJIT 的一部分,它用预处理器把混合汇编的 C 文件转换成 纯 C 文件,还包含一个微型运行时,用来执行运行时工作。 57 | 58 | [dynasm-rs](https://crates.io/crates/dynasm) 是对应的 Rust 实现,用过程宏在编译期解析汇编语法,也包含微型运行时。 59 | 60 | Rust 过程宏作为编译器插件几乎是万能的,不光是汇编,html、shell、cpp 等语言都能嵌入,转换成对应的 Rust 结构,给人一种相当纯粹的感觉。过程宏还有很多用途,感兴趣的可以自行研究。 61 | 62 | ```bash 63 | cargo add dynasm dynasmrt 64 | ``` 65 | 66 | 修改 main.rs,导入 dynasm. 67 | 68 | ```rust ,noplaypen 69 | mod bfir; 70 | 71 | use dynasm::dynasm; 72 | use dynasmrt::{DynasmApi, DynasmLabelApi}; 73 | 74 | use std::io::{stdout, Write}; 75 | ``` 76 | 77 | 编写 print 函数,使用 "sysv64" ABI。 78 | 79 | x86-64 Linux 系统上默认为 System V ABI. [相关文档](https://github.com/hjl-tools/x86-psABI/wiki/X86-psABI) 80 | 81 | ```rust ,noplaypen 82 | unsafe extern "sysv64" fn print(buf: *const u8, len: u64) -> u8 { 83 | let buf = std::slice::from_raw_parts(buf, len as usize); 84 | stdout().write_all(buf).is_err() as u8 85 | } 86 | ``` 87 | 88 | 首先初始化汇编器,指定架构为 x64,全局标签 hello 指向字符串。 89 | 90 | 91 | ```rust ,noplaypen 92 | fn main() { 93 | let mut ops = dynasmrt::x64::Assembler::new().unwrap(); 94 | let string = b"Hello, JIT!\n"; 95 | 96 | dynasm!(ops 97 | ; .arch x64 98 | ; ->hello: 99 | ; .bytes string 100 | ); 101 | ``` 102 | 103 | dynasm 使用 nasm 的方言,左操作数为目标,右操作数为源。 104 | 105 | sysv64 调用约定规定 rdi, rsi, rdx, rcx 存放前四个整数参数,rax 存放返回值。 106 | 107 | ```rust ,noplaypen 108 | let hello = ops.offset(); 109 | dynasm!(ops 110 | ; lea rdi, [->hello] // 将 hello 字符串地址放入 rdi 111 | ; mov rsi, QWORD string.len() as _ // 将 字符串长度放入 rsi 112 | ; mov rax, QWORD print as _ // 将 print 函数地址放入 rax 113 | ; call rax // 调用函数 114 | ; ret // 返回 115 | ); 116 | ``` 117 | 118 | 完成汇编,取得可执行缓冲区。根据偏移拿到函数地址,强制转换为函数指针。最后调用机器码,得到结果。 119 | 120 | ```rust ,noplaypen 121 | let buf = ops.finalize().unwrap(); 122 | 123 | let hello_fn: unsafe extern "sysv64" fn() -> u8 = 124 | unsafe { std::mem::transmute(buf.ptr(hello)) }; 125 | 126 | let ret = unsafe { hello_fn() }; 127 | 128 | assert_eq!(ret, 0); 129 | } 130 | ``` 131 | 132 | 运行结果 133 | 134 | ```bash 135 | $ cargo run 136 | Finished dev [unoptimized + debuginfo] target(s) in 1.30s 137 | Running `target/debug/bfjit` 138 | Hello, JIT! 139 | ``` 140 | 141 | 142 | 完整代码 143 | 144 | ```rust ,noplaypen 145 | mod bfir; 146 | 147 | use dynasm::dynasm; 148 | use dynasmrt::{DynasmApi, DynasmLabelApi}; 149 | 150 | use std::io::{stdout, Write}; 151 | 152 | unsafe extern "sysv64" fn print(buf: *const u8, len: u64) -> u8 { 153 | let buf = std::slice::from_raw_parts(buf, len as usize); 154 | stdout().write_all(buf).is_err() as u8 155 | } 156 | 157 | fn main() { 158 | let mut ops = dynasmrt::x64::Assembler::new().unwrap(); 159 | let string = b"Hello, JIT!\n"; 160 | 161 | dynasm!(ops 162 | ; .arch x64 163 | ; ->hello: 164 | ; .bytes string 165 | ); 166 | 167 | let hello = ops.offset(); 168 | dynasm!(ops 169 | ; lea rdi, [->hello] 170 | ; mov rsi, QWORD string.len() as _ 171 | ; mov rax, QWORD print as _ 172 | ; call rax 173 | ; ret 174 | ); 175 | 176 | let buf = ops.finalize().unwrap(); 177 | 178 | let hello_fn: unsafe extern "sysv64" fn() -> u8 = 179 | unsafe { std::mem::transmute(buf.ptr(hello)) }; 180 | 181 | let ret = unsafe { hello_fn() }; 182 | 183 | assert_eq!(ret, 0); 184 | } 185 | ``` 186 | 187 | ## 如何处理 panic 188 | 189 | 跨越 Rust 边界的 panic 是未定义行为,我们很难让汇编去匹配 unwind ABI。 190 | 191 | 暴露给外部调用的 Rust 函数最好捕获 panic,用其他方式去处理。 192 | 193 | 例如这样 194 | 195 | ```rust 196 | unsafe extern "sysv64" fn print(buf: *const u8, len: u64) -> u8 { 197 | let ret = std::panic::catch_unwind(|| { 198 | let buf = std::slice::from_raw_parts(buf, len as usize); 199 | stdout().write_all(buf).is_err() 200 | }); 201 | match ret { 202 | Ok(false) => 0, 203 | Ok(true) => 1, 204 | Err(_) => 2, 205 | } 206 | } 207 | ``` -------------------------------------------------------------------------------- /book/src/5.md: -------------------------------------------------------------------------------- 1 | # 5. 来点错误 2 | 3 | 添加一个错误模块 4 | 5 | src/main.rs 6 | 7 | ```rust ,noplaypen 8 | mod error; 9 | ``` 10 | 11 | src/error.rs 12 | 13 | ```rust ,noplaypen 14 | {{#include ../../src/error.rs}} 15 | ``` 16 | 17 | 虚拟机运行时可能出现两种错误,IO 错误和指针溢出。 18 | 19 | 虚拟机首先需要读取源代码,可能出现IO错误,然后将源代码编译为 IR,可能发生编译错误,最后运行程序,可能有运行时错误。 20 | -------------------------------------------------------------------------------- /book/src/6.md: -------------------------------------------------------------------------------- 1 | # 6. 实现虚拟机 2 | 3 | 添加模块 src/bfjit.rs 4 | 5 | 导入必要的类型 6 | 7 | ```rust,noplaypen 8 | {{#include ../../src/bfjit.rs:1:9}} 9 | ``` 10 | 11 | 12 | 虚拟机定义与 Brainfuck 机器模型一致。 13 | 14 | 机器码缓冲区,起始偏移,字节数组,输入输出流。 15 | 16 | 字节数组大小至少为 30000,这里设置为 4 MiB. 17 | 18 | ```rust,noplaypen 19 | {{#include ../../src/bfjit.rs:13:19}} 20 | ``` 21 | 22 | 准备汇编可调用的函数,brainfuck 通过 unsafe 函数与虚拟机交互。 23 | 24 | 为了传出可能的错误,把错误移动到堆上,返回裸指针。您也可以选择其他方式来传出错误。 25 | 26 | 在 FFI 时完善地处理 panic 和 backtrace 是一个复杂的问题。为了不增加过多处理代码,这里选择忽略 unsafe 函数中的 panic 问题。在代码正确的情况下,panic 应该不会发生。 27 | 28 | 请注意:跨语言的栈展开 (stack unwind) 是未定义行为,有可能引发段错误,您需要仔细研究 ABI 才能解决它。 29 | 30 | ```rust,noplaypen 31 | {{#include ../../src/bfjit.rs:21:52}} 32 | ``` 33 | 34 | 用输入流、输出流、源文件路径初始化虚拟机,优化选项也由外部提供。 35 | 36 | compile 方法暂时留空。 37 | 38 | ```rust ,noplaypen 39 | impl BfVM<'_> { 40 | fn compile(code: &[BfIR]) -> Result<(dynasmrt::ExecutableBuffer, dynasmrt::AssemblyOffset)> { 41 | todo!() 42 | } 43 | ``` 44 | 45 | ```rust,noplaypen 46 | {{#include ../../src/bfjit.rs:54:79}} 47 | ``` 48 | 49 | 即时编译出的裸函数接收虚拟机指针和字节数组的边界指针,返回错误指针。 50 | 51 | 执行函数后检查错误指针,如果非空,就把错误从堆上移动到栈上再返回。 52 | 53 | ```rust,noplaypen 54 | {{#include ../../src/bfjit.rs:81:102}} 55 | ``` -------------------------------------------------------------------------------- /book/src/7.md: -------------------------------------------------------------------------------- 1 | # 7. 生成机器码 2 | 3 | 完成上一节留空的 compile 方法。 4 | 5 | 整个 brainfuck 程序将被编译为一个大函数,在上一节的 run 方法中我们已经指定了该函数的签名。 6 | 7 | ```rust,noplaypen 8 | type RawFn = unsafe extern "sysv64" fn( 9 | this: *mut BfVM<'_>, 10 | memory_start: *mut u8, 11 | memory_end: *const u8, 12 | ) -> *mut VMError; 13 | ``` 14 | 15 | 在 compile 方法中,首先初始化汇编器,函数起始地址就是最开始的偏移。 16 | 17 | loops 作为栈,用来存放动态标签,指引跳转。 18 | 19 | ```rust,noplaypen 20 | {{#include ../../src/bfjit.rs:104:110}} 21 | ``` 22 | 23 | sysv64 调用约定规定 rdi, rsi, rdx, rcx 存放前四个整数参数,rax 存放返回值,这些属于易失性寄存器,在调用子函数时其内容可能丢失。 24 | 25 | rbp, rbx, r12 ~ r15 是非易失性寄存器,在调用子函数时不会丢失。如果函数中会占用这些寄存器,也要在开头和结尾相应地保存和恢复它们的内容。 26 | 27 | 我们在注释里记录函数会用到的所有寄存器。rdi, rsi, rdx 对应函数的三个参数,我们将其保存到 r12, r13, r14 寄存器。函数中需要一个 ptr 变量记录 brainfuck 程序的当前指针,我们使用 r15 寄存器。 28 | 29 | ```rust,noplaypen 30 | {{#include ../../src/bfjit.rs:112:115}} 31 | ``` 32 | 33 | 汇编函数开始,首先把 rax 压栈。x86-64-psABI 规定参数区域的结尾按16字节对齐。函数开始时返回地址压栈,此时 栈指针+8 是 16 的倍数,因此再把 rax 压栈,使栈指针对齐,以便之后的函数调用,rax 的内容没有意义。 34 | 35 | 由于函数中用到 r12 ~ r15 非易失性寄存器,将其压栈保存。注意这里压入 4 个 8 字节寄存器,栈指针仍然对齐。 36 | 37 | ```rust,noplaypen 38 | {{#include ../../src/bfjit.rs:117:127}} 39 | ``` 40 | 41 | 每个 IR 依次映射到汇编。 42 | 43 | 指针移动,需要检查算术溢出和数组边界溢出,出错即跳转到全局标签所指的错误处理区域。 44 | 45 | ```rust,noplaypen 46 | {{#include ../../src/bfjit.rs:129:143}} 47 | ``` 48 | 49 | 单个字节的算术加减,允许溢出。 50 | 51 | ```rust,noplaypen 52 | {{#include ../../src/bfjit.rs:144:149}} 53 | ``` 54 | 55 | IO 操作。首先保存当前数据指针寄存器,将虚拟机函数所需的各参数和函数地址放入寄存器,调用函数。 56 | 57 | 如果函数返回的不是空指针,说明出错,应该跳转到IO错误处理区域。 58 | 59 | 最后恢复数据指针寄存器。 60 | 61 | ```rust,noplaypen 62 | {{#include ../../src/bfjit.rs:150:165}} 63 | ``` 64 | 65 | 跳转指令。利用 dynasm 提供的 api, 创建两个动态标签,分别生成跳转汇编。由于编译到 IR 时已经验证过跳转指令的对应关系,这里的栈可以直接弹出。 66 | 67 | ```rust,noplaypen 68 | {{#include ../../src/bfjit.rs:166:186}} 69 | ``` 70 | 71 | 正常退出函数时应该返回空指针,表示没有错误。 72 | 73 | 溢出时生成一个溢出错误,IO错误时错误指针已经存入 rax,无需处理。 74 | 75 | 最后退栈,与函数开始时的压栈对应,维持栈平衡。注意退栈时不能破坏 rax 中的返回值。 76 | 77 | ```rust,noplaypen 78 | {{#include ../../src/bfjit.rs:188:203}} 79 | ``` 80 | 81 | 完成汇编,取出可执行缓冲区,返回。 82 | 83 | ```rust,noplaypen 84 | {{#include ../../src/bfjit.rs:205:210}} 85 | ``` 86 | -------------------------------------------------------------------------------- /book/src/8.md: -------------------------------------------------------------------------------- 1 | # 8. 命令行界面 2 | 3 | 虚拟机已经完成,剩下的就是把它包装成命令行界面 (CLI) 了。 4 | 5 | 我们使用 clap 库,用结构体定义命令行参数解析。 6 | 7 | 8 | 9 | ```bash 10 | cargo add clap --features derive 11 | ``` 12 | 13 | 这就是 main.rs 的全部代码。 14 | 15 | ```rust,noplaypen 16 | {{#include ../../src/main.rs}} 17 | ``` 18 | 19 | 把写好的命令行应用安装到系统。 20 | 21 | ```bash 22 | $ cargo install --path . 23 | $ bfjit --help 24 | Usage: bfjit [OPTIONS] 25 | 26 | Arguments: 27 | 28 | 29 | Options: 30 | -o, --optimize Optimize code 31 | -h, --help Print help 32 | -V, --version Print version 33 | ``` 34 | 35 | 从 github 上找一些 brainfuck 程序运行,观察 JIT 与解释器的效率差别。 36 | 37 | ```bash 38 | $ bfjit bf/hello.bf 39 | Hello World! 40 | ``` 41 | 42 | 观察折叠操作对速度的影响。 43 | 44 | ```bash 45 | $ time bfjit bf/mendelbrot.bf > m.txt 46 | 47 | real 0m5.858s 48 | user 0m5.840s 49 | sys 0m0.012s 50 | 51 | $ time bfjit bf/mendelbrot.bf -o > m.txt 52 | 53 | real 0m1.921s 54 | user 0m1.903s 55 | sys 0m0.010s 56 | ``` 57 | 58 | 甚至可以运行一层 brainfuck 自解释器。 59 | 60 | 注意:多层自解释器的效率会严重下降,短时间内无法得出结果。 61 | 62 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /book/src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | [介绍](introduction.md) 4 | 5 | - [背景知识](./1.md) 6 | - [启动项目](./2.md) 7 | - [中间表示](./3.md) 8 | - [Hello, JIT](./4.md) 9 | - [来点错误](./5.md) 10 | - [实现虚拟机](./6.md) 11 | - [生成机器码](./7.md) 12 | - [命令行界面](./8.md) 13 | 14 | [结语](./conclusion.md) 15 | -------------------------------------------------------------------------------- /book/src/conclusion.md: -------------------------------------------------------------------------------- 1 | # 结语 2 | 3 | 本教程基于 dynasm 实现了一个 Brainfuck JIT 虚拟机,主要功能有 Brainfuck 解析编译、简单优化、动态生成机器码,并提供了友好的命令行界面。 4 | 5 | 希望本项目对有兴趣深入研究 JIT 技术的人有所帮助。 6 | 7 | 欢迎 PR 来进一步改进本项目。 8 | 9 | 第三方依赖: 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /book/src/introduction.md: -------------------------------------------------------------------------------- 1 | # Brainfuck JIT 虚拟机教程 2 | 3 | 当我们谈到 JIT 时,通常会想到 V8、JVM 之类的庞然大物,然后望而生畏,觉得 JIT 是一种极其高深复杂的技术。 4 | 5 | 但 JIT 也可以变得非常简单,我们不需要做完善的优化和分析,只要输入源码,输出机器指令,再执行,这和普通的文本处理程序没什么区别。 6 | 7 | 在本教程中,我们将用 Rust 语言实现一个简单的 Brainfuck JIT 虚拟机,逐步理解 JIT 技术。 8 | -------------------------------------------------------------------------------- /justfile: -------------------------------------------------------------------------------- 1 | build: 2 | cd book && mdbook build 3 | 4 | serve: 5 | cd book && mdbook serve --open 6 | 7 | dev: 8 | just fmt 9 | just lint 10 | just test 11 | 12 | fmt *ARGS: 13 | cargo fmt --all {{ARGS}} 14 | 15 | lint *ARGS: 16 | cargo clippy --all-features --tests --benches {{ARGS}} 17 | 18 | test *ARGS: 19 | cargo test --all-features {{ARGS}} 20 | cargo run --release -- bf/hello.bf 21 | cargo run --release -- bf/hello.bf -o 22 | cargo run --release -- bf/mendelbrot.bf 23 | cargo run --release -- bf/mendelbrot.bf -o 24 | 25 | ci: 26 | just fmt --check 27 | just lint -- -D warnings 28 | just test 29 | -------------------------------------------------------------------------------- /src/bfir.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 4 | pub enum BfIR { 5 | AddVal(u8), // + 6 | SubVal(u8), // - 7 | AddPtr(u32), // > 8 | SubPtr(u32), // < 9 | GetByte, // , 10 | PutByte, // . 11 | Jz, // [ 12 | Jnz, // ] 13 | } 14 | 15 | #[derive(Debug, thiserror::Error)] 16 | pub enum CompileErrorKind { 17 | #[error("Unclosed left bracket")] 18 | UnclosedLeftBracket, 19 | #[error("Unexpected right bracket")] 20 | UnexpectedRightBracket, 21 | } 22 | 23 | #[derive(Debug)] 24 | pub struct CompileError { 25 | line: u32, 26 | col: u32, 27 | kind: CompileErrorKind, 28 | } 29 | 30 | impl fmt::Display for CompileError { 31 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 32 | write!(f, "{} at line {}:{}", self.kind, self.line, self.col) 33 | } 34 | } 35 | 36 | impl std::error::Error for CompileError {} 37 | 38 | pub fn compile(src: &str) -> Result, CompileError> { 39 | let mut code: Vec = vec![]; 40 | 41 | let mut stk: Vec<(u32, u32, u32)> = vec![]; 42 | 43 | let mut line: u32 = 1; 44 | let mut col: u32 = 0; 45 | 46 | for ch in src.chars() { 47 | col += 1; 48 | match ch { 49 | '\n' => { 50 | line += 1; 51 | col = 0; 52 | } 53 | '+' => code.push(BfIR::AddVal(1)), 54 | '-' => code.push(BfIR::SubVal(1)), 55 | '>' => code.push(BfIR::AddPtr(1)), 56 | '<' => code.push(BfIR::SubPtr(1)), 57 | ',' => code.push(BfIR::GetByte), 58 | '.' => code.push(BfIR::PutByte), 59 | '[' => { 60 | let pos = code.len() as u32; 61 | stk.push((pos, line, col)); 62 | code.push(BfIR::Jz) 63 | } 64 | ']' => { 65 | stk.pop().ok_or(CompileError { 66 | line, 67 | col, 68 | kind: CompileErrorKind::UnexpectedRightBracket, 69 | })?; 70 | 71 | code.push(BfIR::Jnz) 72 | } 73 | _ => {} 74 | } 75 | } 76 | if let Some((_, line, col)) = stk.pop() { 77 | return Err(CompileError { 78 | line, 79 | col, 80 | kind: CompileErrorKind::UnclosedLeftBracket, 81 | }); 82 | } 83 | Ok(code) 84 | } 85 | 86 | pub fn optimize(code: &mut Vec) { 87 | let len = code.len(); 88 | let mut i = 0; 89 | let mut pc = 0; 90 | 91 | macro_rules! _fold_ir { 92 | ($variant:ident, $x:ident) => {{ 93 | let mut j = i + 1; 94 | while j < len { 95 | if let $variant(d) = code[j] { 96 | $x = $x.wrapping_add(d); 97 | } else { 98 | break; 99 | } 100 | j += 1; 101 | } 102 | i = j; 103 | code[pc] = $variant($x); 104 | pc += 1; 105 | }}; 106 | } 107 | 108 | macro_rules! _normal_ir { 109 | () => {{ 110 | code[pc] = code[i]; 111 | pc += 1; 112 | i += 1; 113 | }}; 114 | } 115 | 116 | use BfIR::*; 117 | while i < len { 118 | match code[i] { 119 | AddPtr(mut x) => _fold_ir!(AddPtr, x), 120 | SubPtr(mut x) => _fold_ir!(SubPtr, x), 121 | AddVal(mut x) => _fold_ir!(AddVal, x), 122 | SubVal(mut x) => _fold_ir!(SubVal, x), 123 | GetByte => _normal_ir!(), 124 | PutByte => _normal_ir!(), 125 | Jz => _normal_ir!(), 126 | Jnz => _normal_ir!(), 127 | } 128 | } 129 | 130 | code.truncate(pc); 131 | code.shrink_to_fit(); 132 | } 133 | 134 | #[test] 135 | fn test_compile() { 136 | assert_eq!( 137 | compile("+[,.]").unwrap(), 138 | vec![ 139 | BfIR::AddVal(1), 140 | BfIR::Jz, 141 | BfIR::GetByte, 142 | BfIR::PutByte, 143 | BfIR::Jnz, 144 | ] 145 | ); 146 | 147 | match compile("[").unwrap_err().kind { 148 | CompileErrorKind::UnclosedLeftBracket => {} 149 | _ => panic!(), 150 | }; 151 | 152 | match compile("]").unwrap_err().kind { 153 | CompileErrorKind::UnexpectedRightBracket => {} 154 | _ => panic!(), 155 | }; 156 | 157 | let mut code = compile("[+++++]").unwrap(); 158 | optimize(&mut code); 159 | assert_eq!(code, vec![BfIR::Jz, BfIR::AddVal(5), BfIR::Jnz]); 160 | } 161 | -------------------------------------------------------------------------------- /src/bfjit.rs: -------------------------------------------------------------------------------- 1 | use crate::bfir::{self, BfIR}; 2 | use crate::error::{Result, RuntimeError, VMError}; 3 | 4 | use std::io::{Read, Write}; 5 | use std::path::Path; 6 | use std::ptr; 7 | 8 | use dynasm::dynasm; 9 | use dynasmrt::{DynasmApi, DynasmLabelApi}; 10 | 11 | const MEMORY_SIZE: usize = 4 * 1024 * 1024; 12 | 13 | pub struct BfVM<'io> { 14 | code: dynasmrt::ExecutableBuffer, 15 | start: dynasmrt::AssemblyOffset, 16 | memory: Box<[u8]>, 17 | input: Box, 18 | output: Box, 19 | } 20 | 21 | #[inline(always)] 22 | fn vm_error(re: RuntimeError) -> *mut VMError { 23 | let e = Box::new(VMError::from(re)); 24 | Box::into_raw(e) 25 | } 26 | 27 | impl BfVM<'_> { 28 | unsafe extern "sysv64" fn getbyte(this: *mut Self, ptr: *mut u8) -> *mut VMError { 29 | let mut buf = [0_u8]; 30 | let this = &mut *this; 31 | match this.input.read(&mut buf) { 32 | Ok(0) => {} 33 | Ok(1) => *ptr = buf[0], 34 | Err(e) => return vm_error(RuntimeError::IO(e)), 35 | _ => unreachable!(), 36 | } 37 | ptr::null_mut() 38 | } 39 | 40 | unsafe extern "sysv64" fn putbyte(this: *mut Self, ptr: *const u8) -> *mut VMError { 41 | let buf = std::slice::from_ref(&*ptr); 42 | let this = &mut *this; 43 | match this.output.write_all(buf) { 44 | Ok(()) => ptr::null_mut(), 45 | Err(e) => vm_error(RuntimeError::IO(e)), 46 | } 47 | } 48 | 49 | unsafe extern "sysv64" fn overflow_error() -> *mut VMError { 50 | vm_error(RuntimeError::PointerOverflow) 51 | } 52 | } 53 | 54 | impl<'io> BfVM<'io> { 55 | pub fn new( 56 | file_path: &Path, 57 | input: Box, 58 | output: Box, 59 | optimize: bool, 60 | ) -> Result { 61 | let src = std::fs::read_to_string(file_path)?; 62 | let mut ir = bfir::compile(&src)?; 63 | drop(src); 64 | 65 | if optimize { 66 | bfir::optimize(&mut ir); 67 | } 68 | let (code, start) = Self::compile(&ir)?; 69 | drop(ir); 70 | 71 | let memory = vec![0; MEMORY_SIZE].into_boxed_slice(); 72 | Ok(Self { 73 | code, 74 | start, 75 | memory, 76 | input, 77 | output, 78 | }) 79 | } 80 | 81 | pub fn run(&mut self) -> Result<()> { 82 | type RawFn = unsafe extern "sysv64" fn( 83 | this: *mut BfVM<'_>, 84 | memory_start: *mut u8, 85 | memory_end: *const u8, 86 | ) -> *mut VMError; 87 | 88 | let raw_fn: RawFn = unsafe { std::mem::transmute(self.code.ptr(self.start)) }; 89 | 90 | let this: *mut Self = self; 91 | let memory_start = self.memory.as_mut_ptr(); 92 | let memory_end = unsafe { memory_start.add(MEMORY_SIZE) }; 93 | 94 | let ret: *mut VMError = unsafe { raw_fn(this, memory_start, memory_end) }; 95 | 96 | if ret.is_null() { 97 | Ok(()) 98 | } else { 99 | Err(*unsafe { Box::from_raw(ret) }) 100 | } 101 | } 102 | } 103 | 104 | impl BfVM<'_> { 105 | #[allow(clippy::fn_to_numeric_cast)] 106 | fn compile(code: &[BfIR]) -> Result<(dynasmrt::ExecutableBuffer, dynasmrt::AssemblyOffset)> { 107 | let mut ops = dynasmrt::x64::Assembler::new()?; 108 | let start = ops.offset(); 109 | 110 | let mut loops = vec![]; 111 | 112 | // this: rdi r12 113 | // memory_start: rsi r13 114 | // memory_end: rdx r14 115 | // ptr: r15 116 | 117 | dynasm!(ops 118 | ; push rax 119 | ; push r12 120 | ; push r13 121 | ; push r14 122 | ; push r15 123 | ; mov r12, rdi // save this 124 | ; mov r13, rsi // save memory_start 125 | ; mov r14, rdx // save memory_end 126 | ; mov r15, rsi // ptr = memory_start 127 | ); 128 | 129 | use BfIR::*; 130 | for &ir in code { 131 | match ir { 132 | AddPtr(x) => dynasm!(ops 133 | ; add r15, x as i32 // ptr += x 134 | ; jc ->overflow // jmp if overflow 135 | ; cmp r15, r14 // ptr - memory_end 136 | ; jnb ->overflow // jmp if ptr >= memory_end 137 | ), 138 | SubPtr(x) => dynasm!(ops 139 | ; sub r15, x as i32 // ptr -= x 140 | ; jc ->overflow // jmp if overflow 141 | ; cmp r15, r13 // ptr - memory_start 142 | ; jb ->overflow // jmp if ptr < memory_start 143 | ), 144 | AddVal(x) => dynasm!(ops 145 | ; add BYTE [r15], x as i8 // *ptr += x 146 | ), 147 | SubVal(x) => dynasm!(ops 148 | ; sub BYTE [r15], x as i8 // *ptr -= x 149 | ), 150 | GetByte => dynasm!(ops 151 | ; mov rdi, r12 // arg0: this 152 | ; mov rsi, r15 // arg1: ptr 153 | ; mov rax, QWORD BfVM::getbyte as _ 154 | ; call rax // getbyte(this, ptr) 155 | ; test rax, rax 156 | ; jnz ->io_error // jmp if rax != 0 157 | ), 158 | PutByte => dynasm!(ops 159 | ; mov rdi, r12 // arg0: this 160 | ; mov rsi, r15 // arg1: ptr 161 | ; mov rax, QWORD BfVM::putbyte as _ 162 | ; call rax // putbyte(this, ptr) 163 | ; test rax, rax 164 | ; jnz ->io_error // jmp if rax != 0 165 | ), 166 | Jz => { 167 | let left = ops.new_dynamic_label(); 168 | let right = ops.new_dynamic_label(); 169 | loops.push((left, right)); 170 | 171 | dynasm!(ops 172 | ; cmp BYTE [r15], 0 173 | ; jz => right // jmp if *ptr == 0 174 | ; => left 175 | ) 176 | } 177 | Jnz => { 178 | let (left, right) = loops.pop().unwrap(); 179 | dynasm!(ops 180 | ; cmp BYTE [r15], 0 181 | ; jnz => left // jmp if *ptr != 0 182 | ; => right 183 | ) 184 | } 185 | } 186 | } 187 | 188 | dynasm!(ops 189 | ; xor rax, rax 190 | ; jmp >exit 191 | ; -> overflow: 192 | ; mov rax, QWORD BfVM::overflow_error as _ 193 | ; call rax 194 | ; jmp >exit 195 | ; -> io_error: 196 | ; exit: 197 | ; pop r15 198 | ; pop r14 199 | ; pop r13 200 | ; pop r12 201 | ; pop rdx 202 | ; ret 203 | ); 204 | 205 | let code = ops.finalize().unwrap(); 206 | 207 | Ok((code, start)) 208 | } 209 | } 210 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug, thiserror::Error)] 2 | pub enum RuntimeError { 3 | #[error("IO: {0}")] 4 | IO(#[from] std::io::Error), 5 | 6 | #[error("Pointer overflow")] 7 | PointerOverflow, 8 | } 9 | 10 | #[derive(Debug, thiserror::Error)] 11 | pub enum VMError { 12 | #[error("IO: {0}")] 13 | IO(#[from] std::io::Error), 14 | 15 | #[error("Compile: {0}")] 16 | Compile(#[from] crate::bfir::CompileError), 17 | 18 | #[error("Runtime: {0}")] 19 | Runtime(#[from] RuntimeError), 20 | } 21 | 22 | pub type Result = std::result::Result; 23 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | mod bfir; 2 | mod bfjit; 3 | mod error; 4 | 5 | use crate::bfjit::BfVM; 6 | 7 | use std::io::{stdin, stdout}; 8 | use std::path::PathBuf; 9 | 10 | use clap::Parser; 11 | 12 | #[derive(Debug, clap::Parser)] 13 | #[clap(version)] 14 | struct Opt { 15 | #[clap(name = "FILE")] 16 | file_path: PathBuf, 17 | 18 | #[clap(short = 'o', long = "optimize", help = "Optimize code")] 19 | optimize: bool, 20 | } 21 | 22 | fn main() { 23 | let opt = Opt::parse(); 24 | 25 | let stdin = stdin(); 26 | let stdout = stdout(); 27 | 28 | let ret = BfVM::new( 29 | &opt.file_path, 30 | Box::new(stdin.lock()), 31 | Box::new(stdout.lock()), 32 | opt.optimize, 33 | ) 34 | .and_then(|mut vm| vm.run()); 35 | 36 | if let Err(e) = &ret { 37 | eprintln!("bfjit: {e}"); 38 | } 39 | 40 | std::process::exit(ret.is_err() as i32) 41 | } 42 | --------------------------------------------------------------------------------