├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE_APACHE ├── LICENSE_MIT ├── README.md ├── compare_tests.py ├── convert.sh ├── mcinterface.h ├── servertests.sh ├── src ├── bin │ └── main.rs ├── block_id_map.rs ├── graph.rs ├── intrinsic │ ├── and.mcfunction │ ├── and_inner.mcfunction │ ├── ashr_i64.mcfunction │ ├── bcmp.mcfunction │ ├── bcmp_inner.mcfunction │ ├── clz.mcfunction │ ├── clz_inner.mcfunction │ ├── ctz.mcfunction │ ├── ctz_inner.mcfunction │ ├── doubleword │ │ ├── load_aligned.mcfunction │ │ └── load_unaligned.mcfunction │ ├── i64_sdivrem │ │ ├── div.mcfunction │ │ ├── negate_lhs.mcfunction │ │ ├── negate_quot.mcfunction │ │ ├── negate_rhs.mcfunction │ │ └── rem.mcfunction │ ├── i64_udivrem │ │ ├── does_fit.mcfunction │ │ ├── loop.mcfunction │ │ ├── main.mcfunction │ │ └── unsigned_less_than.mcfunction │ ├── llvm_ctlz_i32.mcfunction │ ├── llvm_ctlz_i32_inner.mcfunction │ ├── llvm_fshr_i32.mcfunction │ ├── load_byte.mcfunction │ ├── load_doubleword.mcfunction │ ├── load_halfword.mcfunction │ ├── load_halfword_aligned.mcfunction │ ├── load_halfword_unaligned.mcfunction │ ├── load_word.mcfunction │ ├── load_word_unaligned.mcfunction │ ├── lshr.mcfunction │ ├── lshr │ │ ├── getshift.mcfunction │ │ └── inner.mcfunction │ ├── lshr_i64.mcfunction │ ├── lshr_i64 │ │ └── shift_once.mcfunction │ ├── lshr_unopt.mcfunction │ ├── memset.mcfunction │ ├── memset │ │ ├── body_words.mcfunction │ │ ├── head_1_byte.mcfunction │ │ ├── head_2_byte.mcfunction │ │ ├── head_3_byte.mcfunction │ │ ├── mid_2_byte.mcfunction │ │ ├── mid_hi_byte.mcfunction │ │ ├── mid_lo_byte.mcfunction │ │ ├── normal.mcfunction │ │ ├── tail_1_byte.mcfunction │ │ ├── tail_2_byte.mcfunction │ │ └── tail_3_byte.mcfunction │ ├── mul_32_to_64.mcfunction │ ├── or.mcfunction │ ├── or_inner.mcfunction │ ├── or_normal.mcfunction │ ├── pop_and_branch.mcfunction │ ├── popcnt.mcfunction │ ├── put_char.mcfunction │ ├── put_char │ │ └── flush.mcfunction │ ├── rotl.mcfunction │ ├── rotl_64.mcfunction │ ├── rotl_64_once.mcfunction │ ├── rotr.mcfunction │ ├── rotr │ │ └── rotr_inner.mcfunction │ ├── rotr_64.mcfunction │ ├── rotr_64_once.mcfunction │ ├── setptr.mcfunction │ ├── shift_from_ptr.mcfunction │ ├── shift_from_ptr_inner.mcfunction │ ├── shl.mcfunction │ ├── shl_64.mcfunction │ ├── store_byte.mcfunction │ ├── store_halfword.mcfunction │ ├── store_halfword_aligned.mcfunction │ ├── store_halfword_unaligned.mcfunction │ ├── store_word.mcfunction │ ├── store_word_halfaligned.mcfunction │ ├── store_word_unaligned.mcfunction │ ├── xor.mcfunction │ ├── xor_inner.mcfunction │ └── xor_normal.mcfunction ├── lib.rs ├── lir │ ├── interp.rs │ └── mod.rs ├── pack_emitter │ └── mod.rs ├── runner.rs ├── set.rs ├── ssa │ ├── call_graph.rs │ ├── const_prop.rs │ ├── copy_prop.rs │ ├── dce.rs │ ├── interp.rs │ ├── lir_emitter.rs │ ├── liveness.rs │ ├── mod.rs │ ├── opt │ │ ├── coalesce.rs │ │ ├── dead_block_removal.rs │ │ └── mod.rs │ └── reg_alloc.rs ├── validator.rs └── wasm_file.rs └── tests ├── server_test.rs ├── sexpr.rs └── test_common.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | tests/wasm_suite.rs 3 | tests/wasm_suite/ 4 | tests/server_test/ -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "wasmcraft" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [features] 9 | servertests = [] 10 | gui = ["datapack_vm/gui"] 11 | 12 | [[bin]] 13 | name = "wasmcraft" 14 | path = "src/bin/main.rs" 15 | 16 | [lib] 17 | name = "wasmcraft" 18 | path = "src/lib.rs" 19 | 20 | [dependencies] 21 | wasmparser = "*" 22 | datapack_vm = { git = "https://github.com/SuperTails/datapackvm.git", branch = "interning" } 23 | #datapack_vm = { path = "../../datapackvm" } 24 | datapack_common = { git = "https://github.com/SuperTails/datapack_common.git" } 25 | #datapack_common = { path = "../../datapack_common" } 26 | command_parser = { git = "https://github.com/Inky-developer/command-parser" } 27 | hashers = "*" 28 | clap = { version = "3.2.11", features = ["derive"] } 29 | bit-set = "0.5.3" 30 | disjoint-sets = "0.4.2" 31 | union-find-rs = "0.2.1" 32 | 33 | [dev-dependencies] 34 | rcon = { version = "0", features = ["rt-async-std"] } 35 | async-std = "*" 36 | async-recursion = "1.0" 37 | -------------------------------------------------------------------------------- /LICENSE_APACHE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS -------------------------------------------------------------------------------- /LICENSE_MIT: -------------------------------------------------------------------------------- 1 | Copyright 2022 Carson Swoveland 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Wasmcraft 2 | 3 | Have you ever wanted to run C/C++/Rust/WebAssembly applications in Minecraft? No? Well, now you can! 4 | 5 | Wasmcraft is a compiler from [WebAssembly](https://webassembly.org/) to Minecraft Java Edition datapacks. 6 | Since WebAssembly is a well-supported target for many languages, this means that you can run code 7 | written in e.g. C in Minecraft. 8 | 9 | This was inspired by [Sethbling's Atari 2600 Datapack](https://youtu.be/mq7T5_xH24M) and is the (much, much improved) spiritual successor to [Langcraft](https://github.com/SuperTails/langcraft). 10 | 11 | ## Demonstrations 12 | 13 | [Here](https://www.youtube.com/watch?v=wCHB1UgwM9o) is a short overview video 14 | that includes demonstrations of four different games compiled using Wasmcraft: 15 | - Doom 1993 16 | - Celeste classic 17 | - [Minecraft 4k](https://github.com/SuperTails/Minecraft4k-For-Wasmcraft) 18 | - NES Emulator (running Super Mario Bros) 19 | 20 | Additionally, there is [another video](https://youtu.be/jrMrde9tQlg) of the CHIP-8 emulator 21 | from [here](https://github.com/JamesGriffin/CHIP-8-Emulator) running Pong. 22 | 23 | ## Features 24 | 25 | * Minimal porting required 26 | * Most non-hosted/freestanding code that doesn't use floats is compatible 27 | * Compatible with [newlib](https://sourceware.org/newlib/) to provide libc features like `printf` and `malloc` 28 | * Common, well-supported, and stable source language 29 | * Can be targeted from C, C++, and Rust 30 | * All WASM integer and control flow operations supported (and tested against the WebAssembly test suite) 31 | * SSA-based optimizations 32 | * Strength reduction 33 | * Constant folding 34 | * Dead code elimination 35 | * Register allocation 36 | * No game modification required 37 | * Compatible with vanilla Java Edition Minecraft 1.16+ (tested on 1.19+) 38 | * Simulation and debugging tools for datapack developers 39 | 40 | ## Usage 41 | 42 | Let's say we want to run the following program: 43 | 44 | ```c 45 | /* foo.c */ 46 | 47 | #include "mcinterface.h" 48 | 49 | // Since there is no standard library, we use `_start` instead of `main` 50 | int _start() { 51 | for (int i = 0; i < 10; ++i) { 52 | print(i); 53 | } 54 | 55 | return 0; 56 | } 57 | ``` 58 | 59 | First, compile it to an object file: 60 | 61 | ```bash 62 | clang foo.c -target wasm32 -nostdlib -c -o foo.o 63 | wasm-ld foo.o --lto-O3 --gc-sections --import-undefined -o foo.wasm 64 | wasm2wat foo.wasm -o foo.wat # Optional, useful for debugging 65 | ``` 66 | 67 | Ensure you have Rust version >= 1.58 installed, which is very recent. To update Rust, run `rustup update`. 68 | Then, simply navigate to the wasmcraft2 directory and run: 69 | 70 | ```bash 71 | cargo run --release -- ../foo.wasm -O1 -o ../nameofdatapack` 72 | ``` 73 | 74 | This will create a datapack in the folder `nameofdatapack`, which can be directly placed in the datapacks folder 75 | of any Minecraft Java Edition world (this has only been tested on 1.18/1.19, but should work on older and newer versions as well). 76 | 77 | *Warning: all of the contents of the folder `../nameofdatapack` will be deleted. 78 | Do **NOT** point it to a folder with important files!* 79 | 80 | *Warning: due to limitations with Minecraft commands, this needs to fill a few chunks near 0, 0 with jukeboxes, 81 | so do **NOT** run this in a world with builds you don't want destroyed!* 82 | 83 | Run these commands: 84 | 85 | `/gamerule maxCommandChainLength 100000000` (This only needs to be run once per world) 86 | 87 | `/reload` (This only needs to be run when the datapack is changed while the world is open) 88 | 89 | `/datapack enable "name/of/my/datapack"` (If it is not already enabled) 90 | 91 | `/function wasmrunner:init` 92 | 93 | `/function wasmrunner:_start` 94 | 95 | And you should see the numbers 0 to 9 printed out in the chat. 96 | 97 | To adjust the number of commands run per tick, you can set a value any time while the datapack is running: 98 | ``` 99 | scoreboard players set %%max_commands reg 30000 100 | ``` 101 | This number can be adjusted higher or lower depending on your system's performance. 102 | 103 | ## Inserting Sleep Calls 104 | 105 | Currently, programs compiled under Wasmcraft have to have sleep calls manually inserted into them under certain circumstances. 106 | This is because Minecraft tries to execute all commands in a datapack function call within a single game tick, 107 | so in the worst case a very long-running function will freeze the game world indefinitely. 108 | 109 | Wasmcraft currently inserts sleep checks before functions and inside of loops, 110 | but long stretches of code that don't have any loops or function calls can end up having too many commands to work properly, 111 | and some instructions are not command-counted properly (like memset). 112 | 113 | In these cases, the `mc_sleep()` function provided in `mcinterface.h` will pause execution and resume it on the next game tick. 114 | If sleep calls are inserted too frequently, the datapack will run very slowly. 115 | However, if sleep calls are not inserted frequently enough, the game will lag or command execution will be canceled by the game entirely. 116 | 117 | In order to determine if some code has sleep calls inserted frequently enough, 118 | Wasmcraft can simulate the code using the following command: 119 | 120 | ```bash 121 | cargo run --release -- ../foo.wasm -O1 -o ../nameofdatapack --no-persist-output --run-output 122 | ``` 123 | 124 | If `MaxTickCommandsRun` appears in the output, 125 | too many commands were executed in a single tick and `mc_sleep()` must be inserted somewhere. 126 | The provided stacktrace, combined with `print` statements or the `.wat` file, can be used to find the problematic code. 127 | (Wasmcraft function IDs always match the ID of the corresponding function in the WebAssembly file). 128 | 129 | When using the datapack in-game, press Ctrl-Alt-F3 to get a tick time graph (on the right side). 130 | This can be used to find ticks that are running too slowly or are lagging the game. 131 | 132 | Manual sleep calls are planned to be fixed in a future update. 133 | 134 | ## Using the C Standard Library 135 | 136 | In order to use C library functions like `printf`, `malloc`, `open`, etc., 137 | projects can be compiled with the Newlib C stdlib implementation. 138 | 139 | An example of how to do that can be found [here](https://github.com/SuperTails/wasmcraft-newlib-example) 140 | 141 | ## Simulation 142 | 143 | Minecraft will silently ignore most unintended datapack behaviors 144 | (e.g. accessing undefined variables, division by zero, etc), which makes debugging difficult. 145 | In addition, Wasmcraft's compile times can make iterating and bugfixing more tedious. 146 | 147 | For the first stage of development, the [Wasmcraft Preview Simulator](https://github.com/SuperTails/wasmcraft-simulator) 148 | is useful for prototyping and quickly testing programs before actually providing them to the Wasmcraft compiler. 149 | See its repository for details on how to use it. 150 | 151 | Once programs work under the preview simulator, they can also be tested using Wasmcraft itself. 152 | This interprets the actual generated commands, so it should behave almost identically to the program 153 | in a real Minecraft world. The Wasmcraft simulator can be used by passing the `--run-output` flag: 154 | 155 | ```bash 156 | # --no-persist-output doesn't save the datapack to disk, so it makes testing a bit faster 157 | cargo run --release -- /my/input/file.wasm --no-persist-output -o /ignored --run-output 158 | ``` 159 | 160 | By default, the Wasmcraft simulator runs in text mode. 161 | Setting the `gui` feature flag will cause it to also display a window that shows placed blocks. 162 | The GUI mode supports most of the same flags as the Preview Simulator, which can be passed using `--sim-flags`: 163 | 164 | ```bash 165 | cargo run --release --features gui -- --sim-flags="--z-plane=-5 --frame-sleep=100" 166 | ``` 167 | 168 | ## Limitations 169 | 170 | * Floating point operations are not supported (yet). 171 | Use fixed point operations instead, e.g. [libfixmath](https://github.com/PetteriAimonen/libfixmath) 172 | * Bitwise operations and 64-bit divisions have to be emulated and are very slow. 173 | * 8-bit and 16-bit accesses are fairly slow, and all memory accesses have not-insignificant overhead. 174 | * Manual calls to `mc_sleep()` have to be inserted in some cases. 175 | * Only a limited subset of Minecraft commands are available in the interface. 176 | 177 | ## License 178 | 179 | Licensed under either of 180 | * MIT License 181 | * Apache License, Version 2.0 182 | -------------------------------------------------------------------------------- /compare_tests.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | from typing import List 3 | 4 | def list_integ_tests() -> List[str]: 5 | lister = subprocess.run(['cargo', 'test', '--', '--list'], capture_output=True, text=True) 6 | 7 | assert lister.returncode == 0 8 | 9 | idx = lister.stdout.index('\n\n') 10 | after = lister.stdout[idx:] 11 | idx = after[:after.index(': test')].rindex('\n') 12 | 13 | integ_tests = after[idx:] 14 | integ_tests = integ_tests[:integ_tests.index('\n\n')] 15 | 16 | test_names = integ_tests.split('\n') 17 | 18 | assert test_names[0] == '' 19 | 20 | test_names = [t.removesuffix(': test') for t in test_names[1:]] 21 | 22 | return test_names 23 | 24 | TEST_PASSED = 0 25 | TEST_FAILED = 1 26 | TEST_IGNORED = 2 27 | TEST_UNKNOWN = 3 28 | 29 | def run_normal_test(name: str) -> int: 30 | tester = subprocess.run(['cargo', 'test', name, '--', '--exact'], capture_output=True, text=True) 31 | 32 | passed = tester.stdout.count('1 passed') 33 | failed = tester.stdout.count('1 failed') 34 | ignored = tester.stdout.count('1 ignored') 35 | assert passed in (0, 1) 36 | assert failed in (0, 1) 37 | assert ignored in (0, 1) 38 | 39 | passed = passed == 1 40 | failed = failed == 1 41 | ignored = ignored == 1 42 | 43 | assert passed + failed + ignored == 1 44 | 45 | if passed: 46 | return TEST_PASSED 47 | elif failed: 48 | return TEST_FAILED 49 | elif ignored: 50 | return TEST_IGNORED 51 | else: 52 | raise Exception("what") 53 | 54 | def run_server_test(name: str) -> int: 55 | tester = subprocess.run(['cargo', 'test', name, '--features=servertests', '--', '--exact', '--test-threads=1'], capture_output=True, text=True) 56 | 57 | passed = tester.stdout.count('1 passed') 58 | failed = tester.stdout.count('1 failed') 59 | ignored = tester.stdout.count('1 ignored') 60 | assert passed in (0, 1) 61 | assert failed in (0, 1) 62 | assert ignored in (0, 1) 63 | 64 | passed = passed == 1 65 | failed = failed == 1 66 | ignored = ignored == 1 67 | 68 | assert passed + failed + ignored == 1 69 | 70 | if passed: 71 | return TEST_PASSED 72 | elif failed: 73 | return TEST_FAILED 74 | elif ignored: 75 | return TEST_IGNORED 76 | else: 77 | raise Exception("what") 78 | 79 | 80 | def main(): 81 | tests = list_integ_tests() 82 | 83 | passed = set() 84 | failed = set() 85 | ignored = set() 86 | 87 | for test in tests: 88 | result = run_normal_test(test) 89 | if result == TEST_PASSED: 90 | passed.add(test) 91 | elif result == TEST_FAILED: 92 | failed.add(test) 93 | elif result == TEST_IGNORED: 94 | ignored.add(test) 95 | 96 | print(passed) 97 | print(len(passed)) 98 | 99 | for test in passed: 100 | result = run_server_test(test) 101 | if result != TEST_PASSED: 102 | print(f'TEST {result} DIFFERED!!') 103 | 104 | print('Done') 105 | 106 | if __name__ == '__main__': 107 | main() 108 | -------------------------------------------------------------------------------- /convert.sh: -------------------------------------------------------------------------------- 1 | for f in ./src/intrinsic/i64divrem/*.mcfunction ; do sed -i -E 's/function wasmrunner:(.*)/function intrinsic:i64divrem\/\1/' $f ; done 2 | for f in ./src/intrinsic/i64divrem/*.mcfunction ; do sed -i -E 's/(%work%[0123]%([0-9]*)%(lo|hi))/\1%temp/g' $f ; done -------------------------------------------------------------------------------- /mcinterface.h: -------------------------------------------------------------------------------- 1 | #ifndef MCINTERFACE_H_DEFINED 2 | #define MCINTERFACE_H_DEFINED 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | extern void print(int value); 9 | 10 | enum Block { 11 | AIR, 12 | COBBLESTONE, 13 | GRANITE, 14 | ANDESITE, 15 | DIORITE, 16 | LAPIS_BLOCK, 17 | IRON_BLOCK, 18 | GOLD_BLOCK, 19 | DIAMOND_BLOCK, 20 | REDSTONE_BLOCK, 21 | EMERALD_BLOCK, 22 | DIRT_BLOCK, 23 | OAK_LOG_BLOCK, 24 | OAK_LEAVES_BLOCK, 25 | }; 26 | 27 | // ptr must be aligned to 32 bytes 28 | extern void store_8(int *ptr, int value); 29 | 30 | extern void turtle_x(int value); 31 | extern void turtle_y(int value); 32 | extern void turtle_z(int value); 33 | 34 | // Fills a volume relative to the turtle's postion. 35 | // The x, y, and z span arguments are effectively the size of the region minus one, 36 | // so `turtle_fill(block, 0, 0, 0)` is equivalent to `turtle_set(block)` 37 | // This function is UNSTABLE and MAY NOT COMPILE. 38 | extern void turtle_fill(enum Block block, int x_span, int y_span, int z_span); 39 | 40 | // Sets the block at the turtle's position. 41 | extern void turtle_set(enum Block block); 42 | 43 | // Returns the block at the turtle's position 44 | extern enum Block turtle_get(void); 45 | 46 | // Returns 1 if the block at the turtle's position matches the argument 47 | inline int turtle_check(enum Block block) { 48 | return block == turtle_get(); 49 | } 50 | 51 | extern int turtle_get_char(void); 52 | 53 | // Pauses execution and continues it on the next tick 54 | extern void mc_sleep(); 55 | 56 | extern void mc_putc(int ch); 57 | 58 | #ifdef __cplusplus 59 | } 60 | #endif 61 | 62 | #endif -------------------------------------------------------------------------------- /servertests.sh: -------------------------------------------------------------------------------- 1 | cargo test --features=servertests -- --test-threads=1 -------------------------------------------------------------------------------- /src/bin/main.rs: -------------------------------------------------------------------------------- 1 | use clap::Parser; 2 | use wasmcraft::Args; 3 | 4 | fn main() { 5 | let args = Args::parse(); 6 | 7 | wasmcraft::run(args); 8 | } 9 | -------------------------------------------------------------------------------- /src/block_id_map.rs: -------------------------------------------------------------------------------- 1 | use crate::ssa::BlockId; 2 | 3 | 4 | /// Only allows block IDs from within a single function. 5 | /// 6 | /// Stores values as a Vec>, because BlockIds are usually densely packed. 7 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] 8 | pub struct LocalBlockMap { 9 | func_id: Option, 10 | data: Vec>, 11 | } 12 | 13 | impl LocalBlockMap { 14 | pub fn new(func_id: usize) -> Self { 15 | LocalBlockMap { func_id: Some(func_id), data: Vec::new() } 16 | } 17 | 18 | pub fn func_id(&self) -> usize { 19 | self.func_id.unwrap() 20 | } 21 | 22 | pub fn len(&self) -> usize { 23 | self.data.iter().filter(|d| d.is_some()).count() 24 | } 25 | 26 | pub fn is_empty(&self) -> bool { 27 | self.len() == 0 28 | } 29 | 30 | fn check_func_id(&mut self, func_id: usize) { 31 | if let Some(id) = self.func_id { 32 | assert_eq!(id, func_id); 33 | } else { 34 | self.func_id = Some(func_id); 35 | } 36 | } 37 | 38 | pub fn insert(&mut self, k: BlockId, v: T) -> Option { 39 | self.check_func_id(k.func); 40 | 41 | if k.block >= self.data.len() { 42 | self.data.resize_with(k.block + 1, Default::default); 43 | } 44 | 45 | std::mem::replace(&mut self.data[k.block], Some(v)) 46 | } 47 | 48 | pub fn get(&self, k: BlockId) -> Option<&T> { 49 | if let Some(f) = self.func_id { assert_eq!(k.func, f); } 50 | 51 | self.data.get(k.block).and_then(|v| v.as_ref()) 52 | } 53 | 54 | pub fn get_mut(&mut self, k: BlockId) -> Option<&mut T> { 55 | if let Some(f) = self.func_id { assert_eq!(k.func, f); } 56 | 57 | self.data.get_mut(k.block).and_then(|v| v.as_mut()) 58 | } 59 | 60 | pub fn remove(&mut self, k: BlockId) -> Option { 61 | if let Some(f) = self.func_id { assert_eq!(k.func, f); } 62 | 63 | if let Some(entry) = self.data.get_mut(k.block) { 64 | entry.take() 65 | } else { 66 | None 67 | } 68 | } 69 | 70 | pub fn entry(&mut self, k: BlockId) -> &mut Option { 71 | if let Some(f) = self.func_id { assert_eq!(k.func, f); } 72 | 73 | if k.block >= self.data.len() { 74 | self.data.resize_with(k.block + 1, Default::default); 75 | } 76 | 77 | &mut self.data[k.block] 78 | } 79 | 80 | pub fn contains_key(&self, k: BlockId) -> bool { 81 | self.get(k).is_some() 82 | } 83 | 84 | pub fn iter(&self) -> impl Iterator + '_ { 85 | let func = self.func_id.unwrap_or(usize::MAX); 86 | 87 | self.data.iter().enumerate().flat_map(move |(idx, v)| { 88 | let block_id = BlockId { func, block: idx }; 89 | v.as_ref().map(|v| (block_id, v)) 90 | }) 91 | } 92 | 93 | pub fn iter_mut(&mut self) -> impl Iterator { 94 | let func = self.func_id.unwrap_or(usize::MAX); 95 | 96 | self.data.iter_mut().enumerate().flat_map(move |(idx, v)| { 97 | let block_id = BlockId { func, block: idx }; 98 | v.as_mut().map(|v| (block_id, v)) 99 | }) 100 | } 101 | 102 | pub fn keys(&self) -> impl Iterator + '_ { 103 | self.iter().map(|(k, _v)| k) 104 | } 105 | 106 | pub fn values(&self) -> impl Iterator { 107 | self.iter().map(|(_k, v)| v) 108 | } 109 | 110 | /// Includes keys that don't have a corresponding value in the map. 111 | pub fn iter_all_keys(&self) -> impl Iterator { 112 | let func = self.func_id.unwrap(); 113 | 114 | (0..self.data.len()).map(move |i| { 115 | BlockId { func, block: i } 116 | }) 117 | } 118 | } 119 | 120 | pub struct LocalBlockMapIntoIter(LocalBlockMap, usize); 121 | 122 | impl Iterator for LocalBlockMapIntoIter { 123 | type Item = (BlockId, V); 124 | 125 | fn next(&mut self) -> Option { 126 | while self.1 < self.0.data.len() { 127 | let old_idx = self.1; 128 | self.1 += 1; 129 | 130 | if let Some(value) = self.0.data[old_idx].take() { 131 | return Some((BlockId { func: self.0.func_id.unwrap(), block: old_idx }, value)) 132 | } 133 | } 134 | 135 | None 136 | } 137 | } 138 | 139 | impl IntoIterator for LocalBlockMap { 140 | type Item = (BlockId, V); 141 | 142 | type IntoIter = LocalBlockMapIntoIter; 143 | 144 | fn into_iter(self) -> Self::IntoIter { 145 | LocalBlockMapIntoIter(self, 0) 146 | } 147 | } 148 | 149 | impl FromIterator<(BlockId, V)> for LocalBlockMap { 150 | fn from_iter>(iter: T) -> Self { 151 | let mut data = Vec::new(); 152 | 153 | let mut func_id = None; 154 | 155 | for (block_id, block) in iter.into_iter() { 156 | if let Some(func_id) = func_id { 157 | assert_eq!(func_id, block_id.func); 158 | } else { 159 | func_id = Some(block_id.func); 160 | } 161 | 162 | if block_id.block >= data.len() { 163 | data.resize_with(block_id.block + 1, || None); 164 | } 165 | 166 | assert!(data[block_id.block].is_none()); 167 | data[block_id.block] = Some(block); 168 | } 169 | 170 | LocalBlockMap { func_id, data } 171 | } 172 | } -------------------------------------------------------------------------------- /src/graph.rs: -------------------------------------------------------------------------------- 1 | use std::{iter::zip, collections::{HashSet, HashMap}}; 2 | 3 | use wasmparser::ValType; 4 | 5 | use crate::ssa::{SsaVar, liveness::FullLivenessInfo, SsaFunction, SsaInstr}; 6 | 7 | #[derive(Default, Debug, Clone)] 8 | pub struct Graph { 9 | pub edges: HashMap>, 10 | } 11 | 12 | impl Graph { 13 | pub fn new(func: &SsaFunction, lv_info: &FullLivenessInfo) -> Graph { 14 | let mut graph = Graph::default(); 15 | 16 | for (block_id, block_info) in lv_info.0.iter() { 17 | let block = func.get(block_id); 18 | 19 | for &d in &block.phi_node.dests { 20 | graph.add_vertex(d.into_untyped()); 21 | } 22 | for instr in &block.body { 23 | for d in instr.defs() { 24 | graph.add_vertex(d.into_untyped()); 25 | } 26 | for u in instr.uses() { 27 | graph.add_vertex(u.into_untyped()); 28 | } 29 | } 30 | for u in block.term.uses() { 31 | graph.add_vertex(u.into_untyped()); 32 | } 33 | 34 | println!("{:?}", func.func_id()); 35 | println!("{:?}", block_id); 36 | for i in &block.body { 37 | println!("{:?}", i); 38 | } 39 | println!("{:?}", block.term); 40 | 41 | assert_eq!(block_info.iter_live_out().count(), block.body.len() + 2); 42 | 43 | let mut live_out = block_info.iter_live_out(); 44 | 45 | let defs = Some(block.phi_node.dests.clone()).into_iter().chain( 46 | block.body.iter().map(|i| i.defs()) 47 | ).chain( 48 | Vec::new() // terminators do not define vars 49 | ); 50 | 51 | let zipped = zip(live_out.by_ref(), defs); 52 | 53 | for (live_outs, defs) in zipped { 54 | for x in live_outs.iter() { 55 | for &y in &defs { 56 | graph.add_edge(x.into_untyped(), y.into_untyped()); 57 | } 58 | } 59 | } 60 | } 61 | 62 | for (_, block) in func.iter() { 63 | for instr in block.body.iter() { 64 | match instr { 65 | SsaInstr::Add(dst, lhs, rhs) if dst.ty() == ValType::I64 => { 66 | if let Some(l) = lhs.get_var() { 67 | graph.add_edge(dst.into_untyped(), l.into_untyped()) 68 | } 69 | if let Some(r) = rhs.get_var() { 70 | graph.add_edge(dst.into_untyped(), r.into_untyped()); 71 | } 72 | if let (Some(l), Some(r)) = (lhs.get_var(), rhs.get_var()) { 73 | graph.add_edge(l.into_untyped(), r.into_untyped()); 74 | } 75 | } 76 | SsaInstr::Ctz(dst, src) if dst.ty() == ValType::I64 => { 77 | graph.add_edge(dst.into_untyped(), src.into_untyped()); 78 | } 79 | SsaInstr::GeU(dst, lhs, rhs) | 80 | SsaInstr::GtU(dst, lhs, rhs) | 81 | SsaInstr::LeU(dst, lhs, rhs) | 82 | SsaInstr::LtU(dst, lhs, rhs) => { 83 | if let Some(l) = lhs.get_var() { 84 | graph.add_edge(dst.into_untyped(), l.into_untyped()); 85 | } 86 | if let Some(r) = rhs.get_var() { 87 | graph.add_edge(dst.into_untyped(), r.into_untyped()); 88 | } 89 | } 90 | SsaInstr::GeS(dst, lhs, rhs) | 91 | SsaInstr::GtS(dst, lhs, rhs) | 92 | SsaInstr::LeS(dst, lhs, rhs) | 93 | SsaInstr::LtS(dst, lhs, rhs) if dst.ty() == ValType::I64 => { 94 | if let Some(l) = lhs.get_var() { 95 | graph.add_edge(dst.into_untyped(), l.into_untyped()); 96 | } 97 | if let Some(r) = rhs.get_var() { 98 | graph.add_edge(dst.into_untyped(), r.into_untyped()); 99 | } 100 | } 101 | SsaInstr::RemU(dst, lhs, rhs) if dst.ty() == ValType::I32 => { 102 | graph.add_edge(dst.into_untyped(), lhs.into_untyped()); 103 | if let Some(r) = rhs.get_var() { 104 | graph.add_edge(dst.into_untyped(), r.into_untyped()); 105 | } 106 | } 107 | _ => {} 108 | } 109 | } 110 | } 111 | 112 | graph 113 | } 114 | 115 | pub fn add_vertex(&mut self, x: SsaVar) { 116 | self.edges.entry(x).or_default(); 117 | } 118 | 119 | pub fn add_edge(&mut self, x: SsaVar, y: SsaVar) { 120 | if x == y { 121 | return; 122 | } 123 | 124 | self.insert(x, y); 125 | self.insert(y, x); 126 | } 127 | 128 | fn insert(&mut self, src: SsaVar, dest: SsaVar) { 129 | self.edges.entry(src).or_default().insert(dest); 130 | } 131 | 132 | pub fn get_edges(&self) -> &HashMap> { 133 | &self.edges 134 | } 135 | 136 | pub fn interferes(&self, a: SsaVar, b: SsaVar) -> bool { 137 | self.edges.get(&a).unwrap().contains(&b) 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /src/intrinsic/and.mcfunction: -------------------------------------------------------------------------------- 1 | scoreboard players set %return%0 reg 0 2 | 3 | # First half: 4 | # 1-4 5 | function intrinsic:and_inner 6 | function intrinsic:and_inner 7 | function intrinsic:and_inner 8 | function intrinsic:and_inner 9 | 10 | # 5-8 11 | function intrinsic:and_inner 12 | function intrinsic:and_inner 13 | function intrinsic:and_inner 14 | function intrinsic:and_inner 15 | 16 | # 9-12 17 | function intrinsic:and_inner 18 | function intrinsic:and_inner 19 | function intrinsic:and_inner 20 | function intrinsic:and_inner 21 | 22 | # 13-16 23 | function intrinsic:and_inner 24 | function intrinsic:and_inner 25 | function intrinsic:and_inner 26 | function intrinsic:and_inner 27 | 28 | # Second half: 29 | # 1-4 30 | function intrinsic:and_inner 31 | function intrinsic:and_inner 32 | function intrinsic:and_inner 33 | function intrinsic:and_inner 34 | 35 | # 5-8 36 | function intrinsic:and_inner 37 | function intrinsic:and_inner 38 | function intrinsic:and_inner 39 | function intrinsic:and_inner 40 | 41 | # 9-12 42 | function intrinsic:and_inner 43 | function intrinsic:and_inner 44 | function intrinsic:and_inner 45 | function intrinsic:and_inner 46 | 47 | # 13-16 48 | function intrinsic:and_inner 49 | function intrinsic:and_inner 50 | function intrinsic:and_inner 51 | function intrinsic:and_inner -------------------------------------------------------------------------------- /src/intrinsic/and_inner.mcfunction: -------------------------------------------------------------------------------- 1 | # return <<= 1 2 | scoreboard players operation %return%0 reg += %return%0 reg 3 | 4 | # if param0 < 0 && param1 < 0 { c += 1 } 5 | execute if score %param0%0 reg matches ..-1 if score %param1%0 reg matches ..-1 run scoreboard players add %return%0 reg 1 6 | 7 | scoreboard players operation %param0%0 reg += %param0%0 reg 8 | scoreboard players operation %param1%0 reg += %param1%0 reg -------------------------------------------------------------------------------- /src/intrinsic/ashr_i64.mcfunction: -------------------------------------------------------------------------------- 1 | 2 | 3 | execute store success score %%temp_sign reg if score %param0%1 reg matches ..-1 4 | scoreboard players operation %%temp_sign reg *= %%-2147483648 reg 5 | 6 | execute if score %param1%0 reg matches 1.. run function intrinsic:lshr_i64/shift_once 7 | execute if score %param1%0 reg matches 1.. run scoreboard players operation %param0%1 reg += %%temp_sign reg 8 | 9 | scoreboard players remove %param1%0 reg 1 10 | execute if score %param1%0 reg matches 1.. run function intrinsic:ashr_i64 -------------------------------------------------------------------------------- /src/intrinsic/bcmp.mcfunction: -------------------------------------------------------------------------------- 1 | # Arguments: 2 | # %param0%0 - pointer s1 3 | # %param1%0 - pointer s2 4 | # %param2%0 - integer len 5 | 6 | scoreboard players operation %%temp0_bcmp rust = %param0%0 rust 7 | scoreboard players operation %%temp1_bcmp rust = %param1%0 rust 8 | scoreboard players operation %%temp2_bcmp rust = %param2%0 rust 9 | 10 | scoreboard players set %return%0 rust 0 11 | 12 | execute if score %%temp2_bcmp rust matches 1.. run function intrinsic:bcmp_inner -------------------------------------------------------------------------------- /src/intrinsic/bcmp_inner.mcfunction: -------------------------------------------------------------------------------- 1 | scoreboard players operation %ptr rust = %%temp0_bcmp rust 2 | function intrinsic:setptr 3 | function intrinsic:load_byte 4 | scoreboard players operation %%temp3_bcmp rust = %param0%0 rust 5 | 6 | scoreboard players operation %ptr rust = %%temp1_bcmp rust 7 | function intrinsic:setptr 8 | function intrinsic:load_byte 9 | scoreboard players operation %%temp4_bcmp rust = %param0%0 rust 10 | 11 | scoreboard players add %%temp0_bcmp rust 1 12 | scoreboard players add %%temp1_bcmp rust 1 13 | scoreboard players remove %%temp2_bcmp rust 1 14 | 15 | execute unless score %%temp3_bcmp rust = %%temp4_bcmp rust run scoreboard players set %return%0 rust 1 16 | execute if score %%temp2_bcmp rust matches 1.. run function intrinsic:bcmp_inner 17 | -------------------------------------------------------------------------------- /src/intrinsic/clz.mcfunction: -------------------------------------------------------------------------------- 1 | # %param0%0 - input 2 | # %return%0 - output 3 | 4 | # start: 5 | # return0 = 0 6 | # if param0 >= 0 { goto inner } 7 | # 8 | # inner: 9 | # ++return0 10 | # param0 <<= 1 11 | # if return0 <= 31 && param0 >= 0 { goto inner } 12 | 13 | scoreboard players set %return%0 reg 0 14 | 15 | execute if score %param0%0 reg matches 0..65535 run scoreboard players add %return%0 reg 16 16 | execute if score %param0%0 reg matches 0..65535 run scoreboard players operation %param0%0 reg *= %%65536 reg 17 | 18 | execute if score %param0%0 reg matches 0..255 run scoreboard players add %return%0 reg 8 19 | execute if score %param0%0 reg matches 0..255 run scoreboard players operation %param0%0 reg *= %%256 reg 20 | 21 | # TODO: Do these make it faster? 22 | 23 | # execute if score %param0%0 reg matches 0..15 run scoreboard players add %return%0 reg 4 24 | # execute if score %param0%0 reg matches 0..15 run scoreboard players operation %param0%0 reg *= %%16 reg 25 | 26 | # execute if score %param0%0 reg matches 0..3 run scoreboard players add %return%0 reg 2 27 | # execute if score %param0%0 reg matches 0..3 run scoreboard players operation %param0%0 reg *= %%4 reg 28 | 29 | execute if score %param0%0 reg matches 0.. run function intrinsic:clz_inner -------------------------------------------------------------------------------- /src/intrinsic/clz_inner.mcfunction: -------------------------------------------------------------------------------- 1 | scoreboard players add %return%0 reg 1 2 | 3 | scoreboard players operation %param0%0 reg += %param0%0 reg 4 | 5 | execute if score %return%0 reg matches ..31 if score %param0%0 reg matches 0.. run function intrinsic:clz_inner -------------------------------------------------------------------------------- /src/intrinsic/ctz.mcfunction: -------------------------------------------------------------------------------- 1 | # %param0%0 - input 2 | # %return%0 - output 3 | 4 | scoreboard players set %return%0 reg -1 5 | 6 | function intrinsic:ctz_inner -------------------------------------------------------------------------------- /src/intrinsic/ctz_inner.mcfunction: -------------------------------------------------------------------------------- 1 | scoreboard players operation %temp%0 reg = %param0%0 reg 2 | scoreboard players operation %temp%0 reg %= %%2 reg 3 | 4 | scoreboard players operation %param0%0 reg /= %%2 reg 5 | 6 | scoreboard players add %return%0 reg 1 7 | 8 | execute if score %return%0 reg matches ..31 if score %temp%0 reg matches 0..0 run function intrinsic:ctz_inner -------------------------------------------------------------------------------- /src/intrinsic/doubleword/load_aligned.mcfunction: -------------------------------------------------------------------------------- 1 | 2 | function intrinsic:setptr 3 | execute at 44453000-0-0-0-1 store result score %return%0%lo reg run data get block ~ ~ ~ RecordItem.tag.Memory 1 4 | execute at 44453000-0-0-0-1 store result score %return%0%hi reg run data get block ~ ~ ~1 RecordItem.tag.Memory 1 -------------------------------------------------------------------------------- /src/intrinsic/doubleword/load_unaligned.mcfunction: -------------------------------------------------------------------------------- 1 | 2 | scoreboard players operation %%ldw_ptr reg = %ptr reg 3 | 4 | execute if score %%ldw_align reg matches 4 run function intrinsic:setptr 5 | function intrinsic:load_word 6 | scoreboard players operation %return%0%lo reg = %return%0 reg 7 | 8 | scoreboard players operation %ptr reg = %%ldw_ptr reg 9 | scoreboard players add %ptr reg 4 10 | execute if score %%ldw_align reg matches 4 run function intrinsic:setptr 11 | function intrinsic:load_word 12 | scoreboard players operation %return%0%hi reg = %return%0 reg 13 | -------------------------------------------------------------------------------- /src/intrinsic/i64_sdivrem/div.mcfunction: -------------------------------------------------------------------------------- 1 | # int is_neg = 0; 2 | # 3 | # int64_t abs_lhs = lhs; 4 | # int64_t abs_rhs = rhs; 5 | # 6 | # if (lhs < 0) { 7 | # abs_lhs *= -1; 8 | # is_neg = !is_neg; 9 | # } 10 | # if (rhs < 0) { 11 | # abs_rhs *= -1; 12 | # is_neg = !is_neg; 13 | # } 14 | # 15 | # //udiv_result result = i64_udivrem(abs_lhs, abs_rhs); 16 | # int64_t quotient = i64_udiv(abs_lhs, abs_rhs); 17 | # 18 | # if (is_neg) { 19 | # quotient *= -1; 20 | # } 21 | # 22 | # return quotient; 23 | 24 | # %sdiv_lhs -> 25 | # %sdiv_rhs -> 26 | # -> %sdiv_q 27 | 28 | scoreboard players set %sdiv_sign reg 1 29 | 30 | execute if score %sdiv_lhs_hi reg matches ..-1 run function intrinsic:i64_sdivrem/negate_lhs 31 | execute if score %sdiv_rhs_hi reg matches ..-1 run function intrinsic:i64_sdivrem/negate_rhs 32 | 33 | scoreboard players operation %udiv_lhs_lo reg = %sdiv_lhs_lo reg 34 | scoreboard players operation %udiv_lhs_hi reg = %sdiv_lhs_hi reg 35 | scoreboard players operation %udiv_rhs_lo reg = %sdiv_rhs_lo reg 36 | scoreboard players operation %udiv_rhs_hi reg = %sdiv_rhs_hi reg 37 | function intrinsic:i64_udivrem/main 38 | scoreboard players operation %sdiv_q_lo reg = %udiv_q_lo reg 39 | scoreboard players operation %sdiv_q_hi reg = %udiv_q_hi reg 40 | 41 | execute if score %sdiv_sign reg matches -1 run function intrinsic:i64_sdivrem/negate_quot -------------------------------------------------------------------------------- /src/intrinsic/i64_sdivrem/negate_lhs.mcfunction: -------------------------------------------------------------------------------- 1 | scoreboard players operation %sdiv_lhs_lo reg *= %%-1 reg 2 | scoreboard players remove %sdiv_lhs_lo reg 1 3 | 4 | scoreboard players operation %sdiv_lhs_hi reg *= %%-1 reg 5 | scoreboard players remove %sdiv_lhs_hi reg 1 6 | 7 | execute if score %sdiv_lhs_lo reg matches -1 run scoreboard players add %sdiv_lhs_hi reg 1 8 | scoreboard players add %sdiv_lhs_lo reg 1 9 | 10 | scoreboard players operation %sdiv_sign reg *= %%-1 reg -------------------------------------------------------------------------------- /src/intrinsic/i64_sdivrem/negate_quot.mcfunction: -------------------------------------------------------------------------------- 1 | scoreboard players operation %sdiv_q_lo reg *= %%-1 reg 2 | scoreboard players remove %sdiv_q_lo reg 1 3 | 4 | scoreboard players operation %sdiv_q_hi reg *= %%-1 reg 5 | scoreboard players remove %sdiv_q_hi reg 1 6 | 7 | execute if score %sdiv_q_lo reg matches -1 run scoreboard players add %sdiv_q_hi reg 1 8 | scoreboard players add %sdiv_q_lo reg 1 -------------------------------------------------------------------------------- /src/intrinsic/i64_sdivrem/negate_rhs.mcfunction: -------------------------------------------------------------------------------- 1 | scoreboard players operation %sdiv_rhs_lo reg *= %%-1 reg 2 | scoreboard players remove %sdiv_rhs_lo reg 1 3 | 4 | scoreboard players operation %sdiv_rhs_hi reg *= %%-1 reg 5 | scoreboard players remove %sdiv_rhs_hi reg 1 6 | 7 | execute if score %sdiv_rhs_lo reg matches -1 run scoreboard players add %sdiv_rhs_hi reg 1 8 | scoreboard players add %sdiv_rhs_lo reg 1 9 | 10 | scoreboard players operation %sdiv_sign reg *= %%-1 reg -------------------------------------------------------------------------------- /src/intrinsic/i64_sdivrem/rem.mcfunction: -------------------------------------------------------------------------------- 1 | scoreboard players operation %sdiv_lhs_lo_2 reg = %sdiv_lhs_lo reg 2 | scoreboard players operation %sdiv_lhs_hi_2 reg = %sdiv_lhs_hi reg 3 | scoreboard players operation %sdiv_rhs_lo_2 reg = %sdiv_rhs_lo reg 4 | scoreboard players operation %sdiv_rhs_hi_2 reg = %sdiv_rhs_hi reg 5 | 6 | function intrinsic:i64_sdivrem/div 7 | 8 | # sdiv_trunc = %sdiv_q * %sdiv_rhs 9 | 10 | scoreboard players operation %param0%0 reg = %sdiv_q_lo reg 11 | scoreboard players operation %param1%0 reg = %sdiv_rhs_lo_2 reg 12 | function intrinsic:mul_32_to_64 13 | scoreboard players operation %sdiv_trunc_lo reg = %return%0 reg 14 | scoreboard players operation %sdiv_trunc_hi reg = %return%1 reg 15 | 16 | scoreboard players operation %temp%1000%lo reg = %sdiv_q_lo reg 17 | scoreboard players operation %temp%1000%lo reg *= %sdiv_rhs_hi_2 reg 18 | scoreboard players operation %sdiv_trunc_hi reg += %temp%1000%lo reg 19 | scoreboard players operation %temp%1000%lo reg = %sdiv_q_hi reg 20 | scoreboard players operation %temp%1000%lo reg *= %sdiv_rhs_lo_2 reg 21 | scoreboard players operation %sdiv_trunc_hi reg += %temp%1000%lo reg 22 | 23 | # sdiv_trunc *= -1 24 | 25 | scoreboard players operation %sdiv_trunc_lo reg *= %%-1 reg 26 | scoreboard players remove %sdiv_trunc_lo reg 1 27 | scoreboard players operation %sdiv_trunc_hi reg *= %%-1 reg 28 | scoreboard players remove %sdiv_trunc_hi reg 1 29 | execute if score %sdiv_trunc_lo reg matches -1 run scoreboard players add %sdiv_trunc_hi reg 1 30 | scoreboard players add %sdiv_trunc_lo reg 1 31 | 32 | # sdiv_r = sdiv_lhs_2 + sdiv_trunc 33 | 34 | scoreboard players operation %sdiv_r_lo reg = %sdiv_lhs_lo_2 reg 35 | scoreboard players operation %sdiv_r_hi reg = %sdiv_lhs_hi_2 reg 36 | scoreboard players operation %sdiv_r_lo reg += %sdiv_trunc_lo reg 37 | scoreboard players operation %sdiv_r_hi reg += %sdiv_trunc_hi reg 38 | scoreboard players set %sdiv_carry reg 0 39 | execute if score %sdiv_lhs_lo_2 reg matches ..-1 if score %sdiv_trunc_lo reg matches ..-1 run scoreboard players set %sdiv_carry reg 1 40 | execute if score %sdiv_lhs_lo_2 reg matches ..-1 if score %sdiv_trunc_lo reg matches 0.. if score %sdiv_r_lo reg matches 0.. run scoreboard players set %sdiv_carry reg 1 41 | execute if score %sdiv_lhs_lo_2 reg matches 0.. if score %sdiv_trunc_lo reg matches ..-1 if score %sdiv_r_lo reg matches 0.. run scoreboard players set %sdiv_carry reg 1 42 | scoreboard players operation %sdiv_r_hi reg += %sdiv_carry reg -------------------------------------------------------------------------------- /src/intrinsic/i64_udivrem/does_fit.mcfunction: -------------------------------------------------------------------------------- 1 | 2 | # r += neg_rhs; 3 | 4 | scoreboard players operation %udiv_new_r_lo reg = %udiv_r_lo reg 5 | scoreboard players operation %udiv_new_r_hi reg = %udiv_r_hi reg 6 | 7 | scoreboard players operation %udiv_new_r_lo reg += %udiv_neg_rhs_lo reg 8 | scoreboard players operation %udiv_new_r_hi reg += %udiv_neg_rhs_hi reg 9 | 10 | scoreboard players set %udiv_carry reg 0 11 | 12 | execute if score %udiv_r_lo reg matches ..-1 if score %udiv_neg_rhs_lo reg matches ..-1 run scoreboard players set %udiv_carry reg 1 13 | execute if score %udiv_r_lo reg matches ..-1 if score %udiv_neg_rhs_lo reg matches 0.. if score %udiv_new_r_lo reg matches 0.. run scoreboard players set %udiv_carry reg 1 14 | execute if score %udiv_r_lo reg matches 0.. if score %udiv_neg_rhs_lo reg matches ..-1 if score %udiv_new_r_lo reg matches 0.. run scoreboard players set %udiv_carry reg 1 15 | 16 | scoreboard players operation %udiv_new_r_hi reg += %udiv_carry reg 17 | 18 | scoreboard players operation %udiv_r_lo reg = %udiv_new_r_lo reg 19 | scoreboard players operation %udiv_r_hi reg = %udiv_new_r_hi reg 20 | 21 | # q |= q_bit; 22 | 23 | scoreboard players operation %udiv_q_lo reg += %udiv_q_bit_lo reg 24 | scoreboard players operation %udiv_q_hi reg += %udiv_q_bit_hi reg -------------------------------------------------------------------------------- /src/intrinsic/i64_udivrem/loop.mcfunction: -------------------------------------------------------------------------------- 1 | # uint64_t q = 0; 2 | # uint64_t r = 0; 3 | # uint64_t q_bit = 1; 4 | 5 | # uint64_t neg_rhs = -rhs; 6 | 7 | # for (int i = 63; i >= 0; --i) { 8 | # r <<= 1; 9 | # r |= ((lhs & 1ull) != 0); 10 | # if (rhs <= r) { 11 | # r += neg_rhs; 12 | # q |= q_bit; 13 | # } 14 | # 15 | # lhs >>= 1; 16 | # q_bit <<= 1; 17 | # } 18 | 19 | # r <<= 1 20 | execute store success score %udiv_r_lo_is_neg reg if score %udiv_r_lo reg matches ..-1 21 | scoreboard players operation %udiv_r_lo reg += %udiv_r_lo reg 22 | scoreboard players operation %udiv_r_hi reg += %udiv_r_hi reg 23 | execute if score %udiv_r_lo_is_neg reg matches 1 run scoreboard players add %udiv_r_hi reg 1 24 | 25 | # r |= ((lhs & (1ull << 63)) != 0) 26 | execute if score %udiv_lhs_hi reg matches ..-1 run scoreboard players add %udiv_r_lo reg 1 27 | 28 | # %udiv_does_fit = (rhs < r) 29 | 30 | scoreboard players operation %udiv_ult_lhs reg = %udiv_rhs_hi reg 31 | scoreboard players operation %udiv_ult_rhs reg = %udiv_r_hi reg 32 | function intrinsic:i64_udivrem/unsigned_less_than 33 | scoreboard players operation %udiv_hi_is_lesser reg = %udiv_ult_is_less reg 34 | 35 | scoreboard players operation %udiv_ult_lhs reg = %udiv_r_hi reg 36 | scoreboard players operation %udiv_ult_rhs reg = %udiv_rhs_hi reg 37 | function intrinsic:i64_udivrem/unsigned_less_than 38 | scoreboard players operation %udiv_hi_is_greater reg = %udiv_ult_is_less reg 39 | 40 | execute store success score %udiv_hi_is_equal reg if score %udiv_r_hi reg = %udiv_rhs_hi reg 41 | 42 | scoreboard players operation %udiv_ult_lhs reg = %udiv_rhs_lo reg 43 | scoreboard players operation %udiv_ult_rhs reg = %udiv_r_lo reg 44 | function intrinsic:i64_udivrem/unsigned_less_than 45 | scoreboard players operation %udiv_lo_is_less_eq reg = %udiv_ult_is_less reg 46 | execute if score %udiv_rhs_lo reg = %udiv_r_lo reg run scoreboard players set %udiv_lo_is_less_eq reg 1 47 | 48 | execute if score %udiv_hi_is_lesser reg matches 1 run scoreboard players set %udiv_does_fit reg 1 49 | execute if score %udiv_hi_is_greater reg matches 1 run scoreboard players set %udiv_does_fit reg 0 50 | execute if score %udiv_hi_is_equal reg matches 1 run scoreboard players operation %udiv_does_fit reg = %udiv_lo_is_less_eq reg 51 | 52 | # if (rhs <= r) { 53 | # r += neg_rhs; 54 | # q |= q_bit; 55 | # } 56 | execute if score %udiv_does_fit reg matches 1 run function intrinsic:i64_udivrem/does_fit 57 | 58 | 59 | # lhs >>= 1; 60 | 61 | scoreboard players operation %udiv_carry reg = %udiv_lhs_lo reg 62 | scoreboard players operation %udiv_carry reg %= %%2 reg 63 | 64 | #execute store success score %udiv_lhs_top_bit reg if score %udiv_lhs_lo reg matches ..-1 65 | #execute if score %udiv_lhs_top_bit reg matches 1 run scoreboard players operation %udiv_lhs_lo reg += %%-2147483648 reg 66 | #scoreboard players operation %udiv_lhs_lo reg /= %%2 reg 67 | #execute if score %udiv_lhs_top_bit reg matches 1 run scoreboard players operation %udiv_lhs_lo reg += %%1073741824 reg 68 | #execute if score %udiv_carry reg matches 1 run scoreboard players operation %udiv_lhs_lo reg += %%-2147483648 reg 69 | 70 | #execute store success score %udiv_lhs_top_bit reg if score %udiv_lhs_hi reg matches ..-1 71 | #execute if score %udiv_lhs_top_bit reg matches 1 run scoreboard players operation %udiv_lhs_hi reg += %%-2147483648 reg 72 | #scoreboard players operation %udiv_lhs_hi reg /= %%2 reg 73 | #execute if score %udiv_lhs_top_bit reg matches 1 run scoreboard players operation %udiv_lhs_hi reg += %%1073741824 reg 74 | 75 | scoreboard players operation %udiv_lhs_hi reg += %udiv_lhs_hi reg 76 | execute if score %udiv_lhs_lo reg matches ..-1 run scoreboard players add %udiv_lhs_hi reg 1 77 | scoreboard players operation %udiv_lhs_lo reg += %udiv_lhs_lo reg 78 | 79 | # q_bit >>= 1; 80 | 81 | execute store success score %udiv_q_bit_max reg if score %udiv_q_bit_lo reg matches ..-1 82 | scoreboard players operation %udiv_q_bit_lo reg /= %%2 reg 83 | execute if score %udiv_q_bit_max reg matches 1 run scoreboard players set %udiv_q_bit_lo reg 1073741824 84 | 85 | execute if score %udiv_q_bit_hi reg matches 1 run scoreboard players operation %udiv_q_bit_lo reg = %%-2147483648 reg 86 | 87 | execute store success score %udiv_q_bit_max reg if score %udiv_q_bit_hi reg matches ..-1 88 | scoreboard players operation %udiv_q_bit_hi reg /= %%2 reg 89 | execute if score %udiv_q_bit_max reg matches 1 run scoreboard players set %udiv_q_bit_hi reg 1073741824 90 | 91 | # rest of the loop 92 | 93 | scoreboard players remove %udiv_iter reg 1 94 | execute if score %udiv_iter reg matches 1.. run function intrinsic:i64_udivrem/loop -------------------------------------------------------------------------------- /src/intrinsic/i64_udivrem/main.mcfunction: -------------------------------------------------------------------------------- 1 | # uint64_t q = 0; 2 | # uint64_t r = 0; 3 | # uint64_t q_bit = 1; 4 | 5 | # uint64_t neg_rhs = -rhs; 6 | 7 | # for (int i = 63; i >= 0; --i) { 8 | # r <<= 1; 9 | # r |= ((lhs & (1ull << 63)) != 0); 10 | # if (rhs <= r) { 11 | # r += neg_rhs; 12 | # q += q_bit; 13 | # } 14 | # 15 | # lhs <<= 1; 16 | # q_bit >>= 1; 17 | # } 18 | 19 | # returns variables in (%udiv_q_lo reg %udiv_q_hi reg), (%udiv_r_lo reg %udiv_r_hi reg) 20 | 21 | # Initialize loop variables 22 | 23 | scoreboard players set %udiv_q_lo reg 0 24 | scoreboard players set %udiv_q_hi reg 0 25 | 26 | scoreboard players set %udiv_r_lo reg 0 27 | scoreboard players set %udiv_r_hi reg 0 28 | 29 | scoreboard players set %udiv_q_bit_lo reg 0 30 | scoreboard players operation %udiv_q_bit_hi reg = %%-2147483648 reg 31 | 32 | # udiv_neg_rhs = -udiv_rhs 33 | 34 | scoreboard players operation %udiv_neg_rhs_lo reg = %udiv_rhs_lo reg 35 | scoreboard players operation %udiv_neg_rhs_hi reg = %udiv_rhs_hi reg 36 | 37 | scoreboard players operation %udiv_neg_rhs_lo reg *= %%-1 reg 38 | scoreboard players remove %udiv_neg_rhs_lo reg 1 39 | scoreboard players operation %udiv_neg_rhs_hi reg *= %%-1 reg 40 | scoreboard players remove %udiv_neg_rhs_hi reg 1 41 | 42 | execute if score %udiv_neg_rhs_lo reg matches -1 run scoreboard players add %udiv_neg_rhs_hi reg 1 43 | scoreboard players add %udiv_neg_rhs_lo reg 1 44 | 45 | # the entire for loop ... 46 | 47 | scoreboard players set %udiv_iter reg 64 48 | function intrinsic:i64_udivrem/loop -------------------------------------------------------------------------------- /src/intrinsic/i64_udivrem/unsigned_less_than.mcfunction: -------------------------------------------------------------------------------- 1 | # %udiv_ult_lhs reg -> 2 | # %udiv_ult_rhs reg -> 3 | # -> %udiv_ult_is_less reg 4 | 5 | scoreboard players set %udiv_ult_is_less reg 0 6 | execute if score %udiv_ult_lhs reg matches 0.. if score %udiv_ult_rhs reg matches ..-1 run scoreboard players set %udiv_ult_is_less reg 1 7 | execute if score %udiv_ult_lhs reg matches ..-1 if score %udiv_ult_rhs reg matches ..-1 if score %udiv_ult_lhs reg < %udiv_ult_rhs reg run scoreboard players set %udiv_ult_is_less reg 1 8 | execute if score %udiv_ult_lhs reg matches 0.. if score %udiv_ult_rhs reg matches 0.. if score %udiv_ult_lhs reg < %udiv_ult_rhs reg run scoreboard players set %udiv_ult_is_less reg 1 -------------------------------------------------------------------------------- /src/intrinsic/llvm_ctlz_i32.mcfunction: -------------------------------------------------------------------------------- 1 | scoreboard players set %return%0 rust 0 2 | execute if score %param0%0 rust matches 0.. run function intrinsic:llvm_ctlz_i32_inner -------------------------------------------------------------------------------- /src/intrinsic/llvm_ctlz_i32_inner.mcfunction: -------------------------------------------------------------------------------- 1 | scoreboard players operation %param0%0 rust *= %%2 rust 2 | scoreboard players add %return%0 rust 1 3 | 4 | execute if score %return%0 rust matches ..31 if score %param0%0 rust matches 0.. run function intrinsic:llvm_ctlz_i32_inner -------------------------------------------------------------------------------- /src/intrinsic/llvm_fshr_i32.mcfunction: -------------------------------------------------------------------------------- 1 | scoreboard players operation %%tempfshr_%0%0 rust = %param0%0 rust 2 | scoreboard players operation %%tempfshr_%1%0 rust = %param1%0 rust 3 | scoreboard players operation %%tempfshr_%2%0 rust = %param2%0 rust 4 | 5 | scoreboard players operation %%tempfshr_%4%0 rust = %%tempfshr_%2%0 rust 6 | scoreboard players operation %%tempfshr_%4%0 rust %= %%32 rust 7 | 8 | execute store success score %%tempfshr_%5%0 rust if score %%tempfshr_%4%0 rust matches 0..0 9 | 10 | scoreboard players set %%tempfshr_%6%0 rust 32 11 | scoreboard players operation %%tempfshr_%6%0 rust -= %%tempfshr_%4%0 rust 12 | 13 | scoreboard players operation %param0%0 rust = %%tempfshr_%0%0 rust 14 | scoreboard players operation %param1%0 rust = %%tempfshr_%6%0 rust 15 | function intrinsic:shl 16 | scoreboard players operation %%tempfshr_%7%0 rust = %param0%0 rust 17 | 18 | scoreboard players operation %param0%0 rust = %%tempfshr_%1%0 rust 19 | scoreboard players operation %param1%0 rust = %%tempfshr_%4%0 rust 20 | function intrinsic:lshr 21 | scoreboard players operation %%tempfshr_%8%0 rust = %param0%0 rust 22 | 23 | scoreboard players operation %%tempfshr_%9%0 rust = %%tempfshr_%7%0 rust 24 | scoreboard players operation %%tempfshr_%9%0 rust += %%tempfshr_%8%0 rust 25 | execute if score %%tempfshr_%5%0 rust matches 1..1 run scoreboard players operation %%tempfshr_%10%0 rust = %%tempfshr_%1%0 rust 26 | execute unless score %%tempfshr_%5%0 rust matches 1..1 run scoreboard players operation %%tempfshr_%10%0 rust = %%tempfshr_%9%0 rust 27 | 28 | scoreboard players operation %return%0 rust = %%tempfshr_%10%0 rust -------------------------------------------------------------------------------- /src/intrinsic/load_byte.mcfunction: -------------------------------------------------------------------------------- 1 | # Assumes the memory pointer is already at the correct position 2 | # %ptr - The location to read from 3 | # %param0%0 - The return value 4 | # Clobbers %param1%0 5 | 6 | execute at 44453000-0-0-0-1 store result score %param0%0 reg run data get block ~ ~ ~ RecordItem.tag.Memory 1 7 | 8 | scoreboard players operation %param1%0 reg = %ptr reg 9 | scoreboard players operation %param1%0 reg %= %%4 reg 10 | # %param0%0 <<= (8 * 3) 11 | execute if score %param1%0 reg matches 0..0 run scoreboard players operation %param0%0 reg *= %%16777216 reg 12 | # %param0%0 <<= (8 * 2) 13 | execute if score %param1%0 reg matches 1..1 run scoreboard players operation %param0%0 reg *= %%65536 reg 14 | # %param0%0 <<= (8 * 1) 15 | execute if score %param1%0 reg matches 2..2 run scoreboard players operation %param0%0 reg *= %%256 reg 16 | # %param0%0 <<= (8 * 0) 17 | # No-op: 18 | # execute if score %param1%0 reg matches 3..3 run scoreboard players set %param1%0 reg 1 19 | 20 | # -- %param0%0 >>= 24 -- 21 | 22 | execute store success score %%temp0_load_byte reg if score %param0%0 reg matches ..-1 23 | 24 | # Have to split this in two because you can't actually subtract i32::MAX 25 | execute if score %%temp0_load_byte reg matches 1..1 run scoreboard players remove %param0%0 reg 2147483647 26 | execute if score %%temp0_load_byte reg matches 1..1 run scoreboard players remove %param0%0 reg 1 27 | 28 | scoreboard players operation %param0%0 reg /= %%16777216 reg 29 | 30 | execute if score %%temp0_load_byte reg matches 1..1 run scoreboard players add %param0%0 reg 128 -------------------------------------------------------------------------------- /src/intrinsic/load_doubleword.mcfunction: -------------------------------------------------------------------------------- 1 | # %ptr reg - Address to load 2 | # %return%0%lo - Low word of value value 3 | # %return%0%hi - High word of return value 4 | 5 | scoreboard players operation %%ldw_align reg = %ptr reg 6 | scoreboard players operation %%ldw_align reg %= %%8 reg 7 | 8 | execute if score %%ldw_align reg matches 0 run function intrinsic:doubleword/load_aligned 9 | 10 | execute if score %%ldw_align reg matches 1..7 run function intrinsic:doubleword/load_unaligned -------------------------------------------------------------------------------- /src/intrinsic/load_halfword.mcfunction: -------------------------------------------------------------------------------- 1 | # %ptr - The location to read from 2 | # %return%0 - The return value 3 | # Clobbers %param1%0 4 | 5 | scoreboard players operation %%load_halfword_align reg = %ptr reg 6 | scoreboard players operation %%load_halfword_align reg %= %%2 reg 7 | 8 | execute if score %%load_halfword_align reg matches 0 run function intrinsic:load_halfword_aligned 9 | execute if score %%load_halfword_align reg matches 1 run function intrinsic:load_halfword_unaligned -------------------------------------------------------------------------------- /src/intrinsic/load_halfword_aligned.mcfunction: -------------------------------------------------------------------------------- 1 | # %ptr - The location to read from 2 | # %param0%0 - The return value 3 | # Clobbers %param1%0 4 | 5 | function intrinsic:setptr 6 | 7 | execute at 44453000-0-0-0-1 store result score %param0%0 reg run data get block ~ ~ ~ RecordItem.tag.Memory 1 8 | 9 | scoreboard players operation %param1%0 reg = %ptr reg 10 | scoreboard players operation %param1%0 reg %= %%4 reg 11 | 12 | # %param0%0 <<= 16 13 | execute if score %param1%0 reg matches 0 run scoreboard players operation %param0%0 reg *= %%65536 reg 14 | 15 | # %param0%0 >>= 16 (logical) 16 | execute store success score %%templshr_sign reg if score %param0%0 reg matches ..-1 17 | execute if score %%templshr_sign reg matches 1 run scoreboard players operation %param0%0 reg -= %%-2147483648 reg 18 | scoreboard players operation %%templshr_sign reg *= %%32768 reg 19 | scoreboard players operation %param0%0 reg /= %%65536 reg 20 | scoreboard players operation %param0%0 reg += %%templshr_sign reg 21 | 22 | scoreboard players operation %return%0 reg = %param0%0 reg -------------------------------------------------------------------------------- /src/intrinsic/load_halfword_unaligned.mcfunction: -------------------------------------------------------------------------------- 1 | # Arguments 2 | # %ptr 3 | # Return value is %return%0 4 | 5 | function intrinsic:setptr 6 | function intrinsic:load_byte 7 | scoreboard players operation %return%0 reg = %param0%0 reg 8 | scoreboard players add %ptr reg 1 9 | 10 | function intrinsic:setptr 11 | function intrinsic:load_byte 12 | scoreboard players operation %param0%0 reg *= %%256 reg 13 | scoreboard players operation %return%0 reg += %param0%0 reg -------------------------------------------------------------------------------- /src/intrinsic/load_word.mcfunction: -------------------------------------------------------------------------------- 1 | scoreboard players operation %%align reg = %ptr reg 2 | scoreboard players operation %%align reg %= %%4 reg 3 | execute if score %%align reg matches 0..0 run execute at 44453000-0-0-0-1 store result score %return%0 reg run data get block ~ ~ ~ RecordItem.tag.Memory 1 4 | execute unless score %%align reg matches 0..0 run function intrinsic:load_word_unaligned -------------------------------------------------------------------------------- /src/intrinsic/load_word_unaligned.mcfunction: -------------------------------------------------------------------------------- 1 | # Arguments 2 | # %ptr 3 | # Return value is %return%0 4 | 5 | function intrinsic:setptr 6 | function intrinsic:load_byte 7 | scoreboard players operation %return%0 reg = %param0%0 reg 8 | scoreboard players add %ptr reg 1 9 | 10 | function intrinsic:setptr 11 | function intrinsic:load_byte 12 | scoreboard players operation %param0%0 reg *= %%256 reg 13 | scoreboard players operation %return%0 reg += %param0%0 reg 14 | scoreboard players add %ptr reg 1 15 | 16 | function intrinsic:setptr 17 | function intrinsic:load_byte 18 | scoreboard players operation %param0%0 reg *= %%65536 reg 19 | scoreboard players operation %return%0 reg += %param0%0 reg 20 | scoreboard players add %ptr reg 1 21 | 22 | function intrinsic:setptr 23 | function intrinsic:load_byte 24 | scoreboard players operation %param0%0 reg *= %%16777216 reg 25 | scoreboard players operation %return%0 reg += %param0%0 reg -------------------------------------------------------------------------------- /src/intrinsic/lshr.mcfunction: -------------------------------------------------------------------------------- 1 | # Code (from stack overflow post that I need to link in a moment) 2 | # // logical shift right (unsigned) 3 | # if (shift > 15) { 4 | # a = 0; // more than 15, becomes zero 5 | # } else if (shift > 0) { 6 | # if (a < 0) { 7 | # // deal with the sign bit (15) 8 | # a += -32768; 9 | # a /= powtab[shift]; 10 | # a += powtab[15 - shift]; 11 | # } else { 12 | # a /= powtab[shift]; 13 | # } 14 | # } 15 | # 16 | # Pseudo-datapack code (for 32-bit integers) 17 | # 18 | # lshr: 19 | # if (shift > 31) { a = 0 } 20 | # if (shift > 0) { call inner } 21 | # 22 | # inner: 23 | # pow = powtab[shift] # but how to calculate this?? 24 | # cond = a < 0 25 | # if (cond) { a += i32::MIN } 26 | # a /= pow 27 | # if (cond) { a += powtab[15 - shift] } 28 | 29 | # %param0%0 : a (mutated, also the return value) 30 | # %param1%0 : shift (clobbered) 31 | 32 | execute store success score %%templshr_sign reg if score %param0%0 reg matches ..-1 33 | execute if score %%templshr_sign reg matches 1 run scoreboard players operation %param0%0 reg -= %%-2147483648 reg 34 | scoreboard players operation %%templshr_sign reg *= %%-2147483648 reg 35 | 36 | execute if score %param1%0 reg matches 16.. run scoreboard players operation %%templshr_sign reg /= %%65536 reg 37 | execute if score %param1%0 reg matches 16.. run scoreboard players operation %param0%0 reg /= %%65536 reg 38 | execute if score %param1%0 reg matches 16.. run scoreboard players remove %param1%0 reg 16 39 | 40 | execute if score %param1%0 reg matches 8.. run scoreboard players operation %%templshr_sign reg /= %%256 reg 41 | execute if score %param1%0 reg matches 8.. run scoreboard players operation %param0%0 reg /= %%256 reg 42 | execute if score %param1%0 reg matches 8.. run scoreboard players remove %param1%0 reg 8 43 | 44 | execute if score %param1%0 reg matches 4.. run scoreboard players operation %%templshr_sign reg /= %%16 reg 45 | execute if score %param1%0 reg matches 4.. run scoreboard players operation %param0%0 reg /= %%16 reg 46 | execute if score %param1%0 reg matches 4.. run scoreboard players remove %param1%0 reg 4 47 | 48 | execute if score %param1%0 reg matches 2.. run scoreboard players operation %%templshr_sign reg /= %%4 reg 49 | execute if score %param1%0 reg matches 2.. run scoreboard players operation %param0%0 reg /= %%4 reg 50 | execute if score %param1%0 reg matches 2.. run scoreboard players remove %param1%0 reg 2 51 | 52 | execute if score %param1%0 reg matches 1.. run scoreboard players operation %%templshr_sign reg /= %%2 reg 53 | execute if score %param1%0 reg matches 1.. run scoreboard players operation %param0%0 reg /= %%2 reg 54 | #execute if score %param1%0 reg matches 1.. run scoreboard players remove %param1%0 reg 1 55 | 56 | scoreboard players operation %%templshr_sign reg *= %%-1 reg 57 | scoreboard players operation %param0%0 reg += %%templshr_sign reg -------------------------------------------------------------------------------- /src/intrinsic/lshr/getshift.mcfunction: -------------------------------------------------------------------------------- 1 | execute if score %param1%0 reg matches 0..0 run scoreboard players set %%temp1_lshr_inner reg 1 2 | execute if score %param1%0 reg matches 1..1 run scoreboard players set %%temp1_lshr_inner reg 2 3 | execute if score %param1%0 reg matches 2..2 run scoreboard players set %%temp1_lshr_inner reg 4 4 | execute if score %param1%0 reg matches 3..3 run scoreboard players set %%temp1_lshr_inner reg 8 5 | execute if score %param1%0 reg matches 4..4 run scoreboard players set %%temp1_lshr_inner reg 16 6 | execute if score %param1%0 reg matches 5..5 run scoreboard players set %%temp1_lshr_inner reg 32 7 | execute if score %param1%0 reg matches 6..6 run scoreboard players set %%temp1_lshr_inner reg 64 8 | execute if score %param1%0 reg matches 7..7 run scoreboard players set %%temp1_lshr_inner reg 128 9 | execute if score %param1%0 reg matches 8..8 run scoreboard players set %%temp1_lshr_inner reg 256 10 | execute if score %param1%0 reg matches 9..9 run scoreboard players set %%temp1_lshr_inner reg 512 11 | execute if score %param1%0 reg matches 10..10 run scoreboard players set %%temp1_lshr_inner reg 1024 12 | execute if score %param1%0 reg matches 11..11 run scoreboard players set %%temp1_lshr_inner reg 2048 13 | execute if score %param1%0 reg matches 12..12 run scoreboard players set %%temp1_lshr_inner reg 4096 14 | execute if score %param1%0 reg matches 13..13 run scoreboard players set %%temp1_lshr_inner reg 8192 15 | execute if score %param1%0 reg matches 14..14 run scoreboard players set %%temp1_lshr_inner reg 16384 16 | execute if score %param1%0 reg matches 15..15 run scoreboard players set %%temp1_lshr_inner reg 32768 17 | execute if score %param1%0 reg matches 16..16 run scoreboard players set %%temp1_lshr_inner reg 65536 18 | execute if score %param1%0 reg matches 17..17 run scoreboard players set %%temp1_lshr_inner reg 131072 19 | execute if score %param1%0 reg matches 18..18 run scoreboard players set %%temp1_lshr_inner reg 262144 20 | execute if score %param1%0 reg matches 19..19 run scoreboard players set %%temp1_lshr_inner reg 524288 21 | execute if score %param1%0 reg matches 20..20 run scoreboard players set %%temp1_lshr_inner reg 1048576 22 | execute if score %param1%0 reg matches 21..21 run scoreboard players set %%temp1_lshr_inner reg 2097152 23 | execute if score %param1%0 reg matches 22..22 run scoreboard players set %%temp1_lshr_inner reg 4194304 24 | execute if score %param1%0 reg matches 23..23 run scoreboard players set %%temp1_lshr_inner reg 8388608 25 | execute if score %param1%0 reg matches 24..24 run scoreboard players set %%temp1_lshr_inner reg 16777216 26 | execute if score %param1%0 reg matches 25..25 run scoreboard players set %%temp1_lshr_inner reg 33554432 27 | execute if score %param1%0 reg matches 26..26 run scoreboard players set %%temp1_lshr_inner reg 67108864 28 | execute if score %param1%0 reg matches 27..27 run scoreboard players set %%temp1_lshr_inner reg 134217728 29 | execute if score %param1%0 reg matches 28..28 run scoreboard players set %%temp1_lshr_inner reg 268435456 30 | execute if score %param1%0 reg matches 29..29 run scoreboard players set %%temp1_lshr_inner reg 536870912 31 | execute if score %param1%0 reg matches 30..30 run scoreboard players set %%temp1_lshr_inner reg 1073741824 32 | execute if score %param1%0 reg matches 31..31 run scoreboard players set %%temp1_lshr_inner reg -2147483648 -------------------------------------------------------------------------------- /src/intrinsic/lshr/inner.mcfunction: -------------------------------------------------------------------------------- 1 | execute if score %param1%0 reg matches 2.. run scoreboard players operation %%templshr_sign reg /= %%2 reg 2 | scoreboard players operation %param0%0 reg /= %%2 reg 3 | 4 | scoreboard players remove %param1%0 reg 1 5 | 6 | execute if score %param1%0 reg matches 1.. run function intrinsic:lshr/inner -------------------------------------------------------------------------------- /src/intrinsic/lshr_i64.mcfunction: -------------------------------------------------------------------------------- 1 | 2 | 3 | execute if score %param1%0 reg matches 64.. run scoreboard players set %param0%0 reg 0 4 | execute if score %param1%0 reg matches 64.. run scoreboard players set %param1%0 reg 0 5 | 6 | execute if score %param1%0 reg matches 1..63 run function intrinsic:lshr_i64/shift_once 7 | scoreboard players remove %param1%0 reg 1 8 | 9 | execute if score %param1%0 reg matches 1..63 run function intrinsic:lshr_i64 -------------------------------------------------------------------------------- /src/intrinsic/lshr_i64/shift_once.mcfunction: -------------------------------------------------------------------------------- 1 | 2 | # temp_carry = (param0_hi & 0x1) << 31; 3 | 4 | scoreboard players operation %%temp_carry reg = %param0%1 reg 5 | scoreboard players operation %%temp_carry reg %= %%2 reg 6 | scoreboard players operation %%temp_carry reg *= %%-2147483648 reg 7 | 8 | # ------- Shift top half ------ 9 | 10 | # was_neg = (param0_hi < 0) 11 | execute store success score %%temp0_lshr_inner reg if score %param0%1 reg matches ..-1 12 | # if (was_neg) { param0_1 += 1 << 31 }; 13 | execute if score %%temp0_lshr_inner reg matches 1..1 run scoreboard players operation %param0%1 reg += %%-2147483648 reg 14 | 15 | scoreboard players operation %param0%1 reg /= %%2 reg 16 | 17 | # if (was_neg) { param0_1 += 1 << 30 } 18 | execute if score %%temp0_lshr_inner reg matches 1..1 run scoreboard players add %param0%1 reg 1073741824 19 | 20 | # ------- Shift bottom half ------ 21 | 22 | # was_neg = (param0_lo < 0) 23 | execute store success score %%temp0_lshr_inner reg if score %param0%0 reg matches ..-1 24 | # if (was_neg) { param0_lo += 1 << 31 }; 25 | execute if score %%temp0_lshr_inner reg matches 1..1 run scoreboard players operation %param0%0 reg += %%-2147483648 reg 26 | 27 | scoreboard players operation %param0%0 reg /= %%2 reg 28 | 29 | # if (was_neg) { param0_lo += 1 << 30 } 30 | execute if score %%temp0_lshr_inner reg matches 1..1 run scoreboard players add %param0%0 reg 1073741824 31 | 32 | # ----- Carry ----- 33 | 34 | scoreboard players operation %param0%0 reg += %%temp_carry reg -------------------------------------------------------------------------------- /src/intrinsic/lshr_unopt.mcfunction: -------------------------------------------------------------------------------- 1 | # Code (from stack overflow post that I need to link in a moment) 2 | # // logical shift right (unsigned) 3 | # if (shift > 15) { 4 | # a = 0; // more than 15, becomes zero 5 | # } else if (shift > 0) { 6 | # if (a < 0) { 7 | # // deal with the sign bit (15) 8 | # a += -32768; 9 | # a /= powtab[shift]; 10 | # a += powtab[15 - shift]; 11 | # } else { 12 | # a /= powtab[shift]; 13 | # } 14 | # } 15 | # 16 | # Pseudo-datapack code (for 32-bit integers) 17 | # 18 | # lshr: 19 | # if (shift > 31) { a = 0 } 20 | # if (shift > 0) { call inner } 21 | # 22 | # inner: 23 | # pow = powtab[shift] # but how to calculate this?? 24 | # cond = a < 0 25 | # if (cond) { a += i32::MIN } 26 | # a /= pow 27 | # if (cond) { a += powtab[15 - shift] } 28 | 29 | # %param0%0 : a (mutated, also the return value) 30 | # %param1%0 : shift (clobbered) 31 | 32 | execute store success score %%templshr_sign reg if score %param0%0 reg matches ..-1 33 | execute if score %%templshr_sign reg matches 1..1 run scoreboard players remove %param0%0 reg 2147483647 34 | execute if score %%templshr_sign reg matches 1..1 run scoreboard players remove %param0%0 reg 1 35 | scoreboard players operation %%templshr_sign reg *= %%1073741824 reg 36 | 37 | execute if score %param1%0 reg matches 0..0 run scoreboard players operation %%templshr_sign reg += %%templshr_sign reg 38 | 39 | execute if score %param1%0 reg matches 1.. run function intrinsic:lshr/inner 40 | 41 | scoreboard players operation %param0%0 reg += %%templshr_sign reg -------------------------------------------------------------------------------- /src/intrinsic/memset.mcfunction: -------------------------------------------------------------------------------- 1 | # i8* dest == %param0%0 2 | # i8 value == %param1%0 3 | # i32 len == %param2%0 4 | 5 | # %return%0 is set to the return value 6 | 7 | # tellraw @a [{"score": {"name": "%param0%0", "objective": "reg" }}] 8 | # tellraw @a [{"score": {"name": "%param1%0", "objective": "reg" }}] 9 | # tellraw @a [{"score": {"name": "%param2%0", "objective": "reg" }}] 10 | 11 | # !INTERPRETER: ASSERT if score %param1%0 reg matches 0..255 12 | # !INTERPRETER: ASSERT if score %param2%0 reg matches 0.. 13 | 14 | scoreboard players operation %return%0 reg = %param0%0 reg 15 | 16 | scoreboard players operation %mst_byte_offset reg = %param0%0 reg 17 | scoreboard players operation %mst_byte_offset reg %= %%4 reg 18 | 19 | scoreboard players operation %mst_length reg = %param2%0 reg 20 | 21 | scoreboard players set %mst_bytes_left reg 4 22 | scoreboard players operation %mst_bytes_left reg -= %mst_byte_offset reg 23 | scoreboard players operation %mst_bytes_left reg %= %%4 reg 24 | 25 | scoreboard players operation %ptr reg = %param0%0 reg 26 | function intrinsic:setptr 27 | 28 | # Handle special cases for a memset that occurs within a single word 29 | execute as 44453000-0-0-0-1 at @s if score %mst_bytes_left reg matches 2 if score %mst_length reg matches 1 run function intrinsic:memset/mid_hi_byte 30 | execute as 44453000-0-0-0-1 at @s if score %mst_bytes_left reg matches 3 if score %mst_length reg matches 1 run function intrinsic:memset/mid_lo_byte 31 | execute as 44453000-0-0-0-1 at @s if score %mst_bytes_left reg matches 3 if score %mst_length reg matches 2 run function intrinsic:memset/mid_2_byte 32 | 33 | # Otherwise, do a normal memset 34 | execute as 44453000-0-0-0-1 if score %mst_bytes_left reg <= %mst_length reg run function intrinsic:memset/normal -------------------------------------------------------------------------------- /src/intrinsic/memset/body_words.mcfunction: -------------------------------------------------------------------------------- 1 | # 2 | # i32 x = %mst_x 3 | # i32 y = %mst_y 4 | # i32 z = %mst_z 5 | # 6 | # i32 value = %mst_value_word 7 | # i32 bytes = %mst_length 8 | 9 | # C pseudocode: 10 | # 11 | # i32 x, y, z; 12 | # i32 value; 13 | # i32 bytes; 14 | # 15 | # setptr(x, y, z); 16 | # 17 | # void memset_body_words() { 18 | # assert(bytes >= 4) 19 | # do { 20 | # *memoryptr = value; 21 | # 22 | # ++z; 23 | # if (z == PAGE_SPAN_Z) { 24 | # ++y; 25 | # } 26 | # if (y == PAGE_SPAN_Y) { 27 | # ++x; 28 | # } 29 | # 30 | # 31 | # if (y == PAGE_SPAN_Y) { 32 | # setptr_x(x); 33 | # y = 0; 34 | # } 35 | # 36 | # if (z == PAGE_SPAN_Z) { 37 | # setptr_y(y); 38 | # z = 0; 39 | # } 40 | # 41 | # setptr_z(z); 42 | # 43 | # bytes -= 4; 44 | # } while (bytes >= 4); 45 | # } 46 | 47 | execute at @s store result block ~ ~ ~ RecordItem.tag.Memory int 1 run scoreboard players get %mst_value_word reg 48 | 49 | scoreboard players add %mst_z reg 1 50 | execute if score %mst_z reg = %%PAGE_SPAN_Z reg run scoreboard players add %mst_y reg 1 51 | execute if score %mst_y reg = %%PAGE_SPAN_Y reg run scoreboard players add %mst_x reg 1 52 | 53 | execute if score %mst_y reg = %%PAGE_SPAN_Y reg store result entity @s Pos[0] double 1 run scoreboard players get %mst_x reg 54 | execute if score %mst_y reg = %%PAGE_SPAN_Y reg run scoreboard players set %mst_y reg 0 55 | 56 | execute if score %mst_z reg = %%PAGE_SPAN_Z reg store result entity @s Pos[1] double 1 run scoreboard players get %mst_y reg 57 | execute if score %mst_z reg = %%PAGE_SPAN_Z reg run scoreboard players set %mst_z reg 0 58 | 59 | execute store result entity @s Pos[2] double 1 run scoreboard players get %mst_z reg 60 | 61 | scoreboard players remove %mst_length reg 4 62 | scoreboard players add %param0%0 reg 4 63 | 64 | execute if score %mst_length reg matches 4.. run function intrinsic:memset/body_words -------------------------------------------------------------------------------- /src/intrinsic/memset/head_1_byte.mcfunction: -------------------------------------------------------------------------------- 1 | execute store result score %mst_temp_word reg run data get block ~ ~ ~ RecordItem.tag.Memory 1 2 | 3 | scoreboard players operation %mst_temp_byte reg = %param1%0 reg 4 | scoreboard players operation %mst_temp_byte reg *= %%16777216 reg 5 | 6 | scoreboard players operation %mst_temp_word reg %= %%16777216 reg 7 | scoreboard players operation %mst_temp_word reg += %mst_temp_byte reg 8 | 9 | execute store result block ~ ~ ~ RecordItem.tag.Memory int 1 run scoreboard players get %mst_temp_word reg 10 | 11 | scoreboard players add %param0%0 reg 1 12 | scoreboard players remove %mst_length reg 1 -------------------------------------------------------------------------------- /src/intrinsic/memset/head_2_byte.mcfunction: -------------------------------------------------------------------------------- 1 | execute store result score %mst_temp_word reg run data get block ~ ~ ~ RecordItem.tag.Memory 1 2 | 3 | scoreboard players operation %mst_temp_byte reg = %param1%0 reg 4 | scoreboard players operation %mst_temp_byte reg *= %%65536 reg 5 | 6 | scoreboard players operation %mst_temp_word reg %= %%65536 reg 7 | scoreboard players operation %mst_temp_word reg += %mst_temp_byte reg 8 | scoreboard players operation %mst_temp_byte reg *= %%256 reg 9 | scoreboard players operation %mst_temp_word reg += %mst_temp_byte reg 10 | 11 | execute store result block ~ ~ ~ RecordItem.tag.Memory int 1 run scoreboard players get %mst_temp_word reg 12 | 13 | scoreboard players add %param0%0 reg 2 14 | scoreboard players remove %mst_length reg 2 -------------------------------------------------------------------------------- /src/intrinsic/memset/head_3_byte.mcfunction: -------------------------------------------------------------------------------- 1 | execute store result score %mst_temp_word reg run data get block ~ ~ ~ RecordItem.tag.Memory 1 2 | 3 | scoreboard players operation %mst_temp_byte reg = %param1%0 reg 4 | scoreboard players operation %mst_temp_byte reg *= %%256 reg 5 | 6 | scoreboard players operation %mst_temp_word reg %= %%256 reg 7 | scoreboard players operation %mst_temp_word reg += %mst_temp_byte reg 8 | scoreboard players operation %mst_temp_byte reg *= %%256 reg 9 | scoreboard players operation %mst_temp_word reg += %mst_temp_byte reg 10 | scoreboard players operation %mst_temp_byte reg *= %%256 reg 11 | scoreboard players operation %mst_temp_word reg += %mst_temp_byte reg 12 | 13 | execute store result block ~ ~ ~ RecordItem.tag.Memory int 1 run scoreboard players get %mst_temp_word reg 14 | 15 | scoreboard players add %param0%0 reg 3 16 | scoreboard players remove %mst_length reg 3 -------------------------------------------------------------------------------- /src/intrinsic/memset/mid_2_byte.mcfunction: -------------------------------------------------------------------------------- 1 | # 00_NN_NN_00 2 | 3 | execute store result score %mst_temp_word reg run data get block ~ ~ ~ RecordItem.tag.Memory 1 4 | 5 | # mst_low_half = mst_temp_word & 0x00_FF_FF_00 6 | scoreboard players operation %mst_low_half reg = %mst_temp_word reg 7 | scoreboard players operation %mst_low_half reg %= %%16777216 reg 8 | scoreboard players operation %mst_low_byte reg = %mst_temp_word reg 9 | scoreboard players operation %mst_low_byte reg %= %%256 reg 10 | scoreboard players operation %mst_low_half reg -= %mst_low_byte reg 11 | 12 | scoreboard players operation %mst_temp_word reg -= %mst_low_half reg 13 | 14 | scoreboard players operation %mst_temp_value reg = %param1%0 reg 15 | scoreboard players operation %mst_temp_value reg *= %%256 reg 16 | scoreboard players operation %mst_temp_word reg += %mst_temp_value reg 17 | scoreboard players operation %mst_temp_value reg *= %%256 reg 18 | scoreboard players operation %mst_temp_word reg += %mst_temp_value reg 19 | 20 | execute store result block ~ ~ ~ RecordItem.tag.Memory int 1 run scoreboard players get %mst_temp_word reg -------------------------------------------------------------------------------- /src/intrinsic/memset/mid_hi_byte.mcfunction: -------------------------------------------------------------------------------- 1 | # 00_NN_00_00 2 | 3 | execute store result score %mst_temp_word reg run data get block ~ ~ ~ RecordItem.tag.Memory 1 4 | 5 | # mst_low_half = mst_temp_word & 0x00_FF_00_00 6 | scoreboard players operation %mst_low_half reg = %mst_temp_word reg 7 | scoreboard players operation %mst_low_half reg %= %%16777216 reg 8 | scoreboard players operation %mst_low_byte reg = %mst_temp_word reg 9 | scoreboard players operation %mst_low_byte reg %= %%65536 reg 10 | scoreboard players operation %mst_low_half reg -= %mst_low_byte reg 11 | 12 | scoreboard players operation %mst_temp_word reg -= %mst_low_half reg 13 | 14 | scoreboard players operation %mst_temp_value reg = %param1%0 reg 15 | scoreboard players operation %mst_temp_value reg *= %%65536 reg 16 | 17 | scoreboard players operation %mst_temp_word reg += %mst_temp_value reg 18 | 19 | execute store result block ~ ~ ~ RecordItem.tag.Memory int 1 run scoreboard players get %mst_temp_word reg -------------------------------------------------------------------------------- /src/intrinsic/memset/mid_lo_byte.mcfunction: -------------------------------------------------------------------------------- 1 | # 00_00_NN_00 2 | 3 | execute store result score %mst_temp_word reg run data get block ~ ~ ~ RecordItem.tag.Memory 1 4 | 5 | # mst_low_half = mst_temp_word & 0x00_00_FF_00 6 | scoreboard players operation %mst_low_half reg = %mst_temp_word reg 7 | scoreboard players operation %mst_low_half reg %= %%65536 reg 8 | scoreboard players operation %mst_low_byte reg = %mst_temp_word reg 9 | scoreboard players operation %mst_low_byte reg %= %%256 reg 10 | scoreboard players operation %mst_low_half reg -= %mst_low_byte reg 11 | 12 | scoreboard players operation %mst_temp_word reg -= %mst_low_half reg 13 | 14 | scoreboard players operation %mst_temp_value reg = %param1%0 reg 15 | scoreboard players operation %mst_temp_value reg *= %%256 reg 16 | 17 | scoreboard players operation %mst_temp_word reg += %mst_temp_value reg 18 | 19 | execute store result block ~ ~ ~ RecordItem.tag.Memory int 1 run scoreboard players get %mst_temp_word reg -------------------------------------------------------------------------------- /src/intrinsic/memset/normal.mcfunction: -------------------------------------------------------------------------------- 1 | #execute if score %mst_bytes_left reg > %mst_length reg run say TODO: Only a very short memset 2 | 3 | execute at @s if score %mst_bytes_left reg matches 1 run function intrinsic:memset/head_1_byte 4 | execute at @s if score %mst_bytes_left reg matches 2 run function intrinsic:memset/head_2_byte 5 | execute at @s if score %mst_bytes_left reg matches 3 run function intrinsic:memset/head_3_byte 6 | 7 | scoreboard players operation %mst_value_word reg = %param1%0 reg 8 | scoreboard players operation %mst_value_word reg *= %%256 reg 9 | scoreboard players operation %mst_value_word reg += %param1%0 reg 10 | scoreboard players operation %mst_temp reg = %mst_value_word reg 11 | scoreboard players operation %mst_value_word reg *= %%65536 reg 12 | scoreboard players operation %mst_value_word reg += %mst_temp reg 13 | 14 | scoreboard players operation %ptr reg = %param0%0 reg 15 | function intrinsic:setptr 16 | scoreboard players operation %mst_x reg = %%ptr reg 17 | scoreboard players operation %mst_y reg = %y reg 18 | scoreboard players operation %mst_z reg = %z reg 19 | execute if score %mst_length reg matches 4.. run function intrinsic:memset/body_words 20 | 21 | # The pointer is already set properly by body_words, 22 | # and %param0%0 is already updated. 23 | 24 | execute at @s if score %mst_length reg matches 1 run function intrinsic:memset/tail_1_byte 25 | execute at @s if score %mst_length reg matches 2 run function intrinsic:memset/tail_2_byte 26 | execute at @s if score %mst_length reg matches 3 run function intrinsic:memset/tail_3_byte -------------------------------------------------------------------------------- /src/intrinsic/memset/tail_1_byte.mcfunction: -------------------------------------------------------------------------------- 1 | execute store result score %mst_temp_word reg run data get block ~ ~ ~ RecordItem.tag.Memory 1 2 | 3 | scoreboard players operation %mst_low_bits reg = %mst_temp_word reg 4 | scoreboard players operation %mst_low_bits reg %= %%256 reg 5 | 6 | scoreboard players operation %mst_temp_word reg -= %mst_low_bits reg 7 | 8 | scoreboard players operation %mst_temp_word reg += %param1%0 reg 9 | 10 | execute store result block ~ ~ ~ RecordItem.tag.Memory int 1 run scoreboard players get %mst_temp_word reg -------------------------------------------------------------------------------- /src/intrinsic/memset/tail_2_byte.mcfunction: -------------------------------------------------------------------------------- 1 | execute store result score %mst_temp_word reg run data get block ~ ~ ~ RecordItem.tag.Memory 1 2 | 3 | scoreboard players operation %mst_low_bits reg = %mst_temp_word reg 4 | scoreboard players operation %mst_low_bits reg %= %%65536 reg 5 | 6 | scoreboard players operation %mst_temp_word reg -= %mst_low_bits reg 7 | 8 | scoreboard players operation %mst_value_word reg %= %%65536 reg 9 | scoreboard players operation %mst_temp_word reg += %mst_value_word reg 10 | 11 | execute store result block ~ ~ ~ RecordItem.tag.Memory int 1 run scoreboard players get %mst_temp_word reg -------------------------------------------------------------------------------- /src/intrinsic/memset/tail_3_byte.mcfunction: -------------------------------------------------------------------------------- 1 | execute store result score %mst_temp_word reg run data get block ~ ~ ~ RecordItem.tag.Memory 1 2 | 3 | scoreboard players operation %mst_low_bits reg = %mst_temp_word reg 4 | scoreboard players operation %mst_low_bits reg %= %%16777216 reg 5 | 6 | scoreboard players operation %mst_temp_word reg -= %mst_low_bits reg 7 | 8 | scoreboard players operation %mst_value_word reg %= %%16777216 reg 9 | scoreboard players operation %mst_temp_word reg += %mst_value_word reg 10 | 11 | execute store result block ~ ~ ~ RecordItem.tag.Memory int 1 run scoreboard players get %mst_temp_word reg -------------------------------------------------------------------------------- /src/intrinsic/mul_32_to_64.mcfunction: -------------------------------------------------------------------------------- 1 | # Arguments: 2 | # %param0%0 - Left operand 3 | # %param1%0 - Right operand 4 | # %return%0 - Low word of product 5 | # %return%1 - High word of product 6 | 7 | scoreboard players operation %%tempmul_p0_save reg = %param0%0 reg 8 | scoreboard players operation %%tempmul_p1_save reg = %param1%0 reg 9 | 10 | # %0%0 = p0 11 | scoreboard players operation %%tempmul_%0%0 reg = %param0%0 reg 12 | # %1%0 = p1 13 | scoreboard players operation %%tempmul_%1%0 reg = %param1%0 reg 14 | 15 | # %5%0 = (p0 & 0xFFFF) 16 | # p0_lo = %5%0 17 | scoreboard players operation %%tempmul_%5%0 reg = %%tempmul_%0%0 reg 18 | scoreboard players operation %%tempmul_%5%0 reg %= %%65536 reg 19 | 20 | # %6%0 = (p1 & 0xFFFF) 21 | # p1_lo = %6%0 22 | scoreboard players operation %%tempmul_%6%0 reg = %%tempmul_%1%0 reg 23 | scoreboard players operation %%tempmul_%6%0 reg %= %%65536 reg 24 | 25 | # %7%0 = (%5%0 * %6%0) 26 | # lo_product = %7%0 27 | scoreboard players operation %%tempmul_%7%0 reg = %%tempmul_%6%0 reg 28 | scoreboard players operation %%tempmul_%7%0 reg *= %%tempmul_%5%0 reg 29 | 30 | # %8%0 = (%7%0 & 0xFFFF) 31 | # lo_product_lo = lo_product & 0xFFFF; 32 | scoreboard players operation %%tempmul_%8%0 reg = %%tempmul_%7%0 reg 33 | scoreboard players operation %%tempmul_%8%0 reg %= %%65536 reg 34 | 35 | # %9%0 = (lo_product >> 16) & 0xFFFF; 36 | # lo_product_hi = %9%0 37 | scoreboard players set %%tempmul_%temp3 reg 16 38 | scoreboard players operation %param0%0 reg = %%tempmul_%7%0 reg 39 | scoreboard players operation %param1%0 reg = %%tempmul_%temp3 reg 40 | function intrinsic:lshr 41 | scoreboard players operation %%tempmul_%9%0 reg = %param0%0 reg 42 | 43 | # %10%0 = (p0 >> 16) 44 | # p0_hi = %10%0 45 | scoreboard players set %%tempmul_%temp4 reg 16 46 | scoreboard players operation %param0%0 reg = %%tempmul_%0%0 reg 47 | scoreboard players operation %param1%0 reg = %%tempmul_%temp4 reg 48 | function intrinsic:lshr 49 | scoreboard players operation %%tempmul_%10%0 reg = %param0%0 reg 50 | 51 | # %11%0 = p1_lo * p0_hi 52 | # mid_product_1 = %11%0 53 | # %12%0 = lo_product_hi + mid_product_1 54 | scoreboard players operation %%tempmul_%11%0 reg = %%tempmul_%6%0 reg 55 | scoreboard players operation %%tempmul_%11%0 reg *= %%tempmul_%10%0 reg 56 | scoreboard players operation %%tempmul_%12%0 reg = %%tempmul_%9%0 reg 57 | scoreboard players operation %%tempmul_%12%0 reg += %%tempmul_%11%0 reg 58 | 59 | # %13%0 = (lo_product_hi + mid_product_1) & 0xFFFF 60 | scoreboard players operation %%tempmul_%13%0 reg = %%tempmul_%12%0 reg 61 | scoreboard players operation %%tempmul_%13%0 reg %= %%65536 reg 62 | 63 | scoreboard players set %%tempmul_%temp6 reg 16 64 | scoreboard players operation %param0%0 reg = %%tempmul_%12%0 reg 65 | scoreboard players operation %param1%0 reg = %%tempmul_%temp6 reg 66 | function intrinsic:lshr 67 | scoreboard players operation %%tempmul_%14%0 reg = %param0%0 reg 68 | scoreboard players set %%tempmul_%temp7 reg 16 69 | scoreboard players operation %param0%0 reg = %%tempmul_%1%0 reg 70 | scoreboard players operation %param1%0 reg = %%tempmul_%temp7 reg 71 | function intrinsic:lshr 72 | scoreboard players operation %%tempmul_%15%0 reg = %param0%0 reg 73 | scoreboard players operation %%tempmul_%16%0 reg = %%tempmul_%15%0 reg 74 | scoreboard players operation %%tempmul_%16%0 reg *= %%tempmul_%5%0 reg 75 | scoreboard players operation %%tempmul_%17%0 reg = %%tempmul_%13%0 reg 76 | scoreboard players operation %%tempmul_%17%0 reg += %%tempmul_%16%0 reg 77 | scoreboard players set %%tempmul_%temp8 reg 16 78 | scoreboard players operation %param0%0 reg = %%tempmul_%17%0 reg 79 | scoreboard players operation %param1%0 reg = %%tempmul_%temp8 reg 80 | function intrinsic:lshr 81 | scoreboard players operation %%tempmul_%18%0 reg = %param0%0 reg 82 | 83 | scoreboard players operation %%tempmul_%19%0 reg = %%tempmul_%15%0 reg 84 | scoreboard players operation %%tempmul_%19%0 reg *= %%tempmul_%10%0 reg 85 | scoreboard players operation %%tempmul_%20%0 reg = %%tempmul_%14%0 reg 86 | scoreboard players operation %%tempmul_%20%0 reg += %%tempmul_%19%0 reg 87 | scoreboard players operation %%tempmul_%21%0 reg = %%tempmul_%17%0 reg 88 | scoreboard players operation %%tempmul_%21%0 reg *= %%65536 reg 89 | scoreboard players operation %%tempmul_%22%0 reg = %%tempmul_%20%0 reg 90 | scoreboard players operation %%tempmul_%22%0 reg += %%tempmul_%18%0 reg 91 | 92 | 93 | scoreboard players operation %param0%0 reg = %%tempmul_%21%0 reg 94 | scoreboard players operation %param1%0 reg = %%tempmul_%8%0 reg 95 | function intrinsic:or 96 | scoreboard players operation %%tempmul_%23%0 reg = %return%0 reg 97 | 98 | scoreboard players operation %return%0 reg = %%tempmul_%23%0 reg 99 | scoreboard players operation %return%1 reg = %%tempmul_%22%0 reg 100 | 101 | scoreboard players operation %param0%0 reg = %%tempmul_p0_save reg 102 | scoreboard players operation %param1%0 reg = %%tempmul_p1_save reg -------------------------------------------------------------------------------- /src/intrinsic/or.mcfunction: -------------------------------------------------------------------------------- 1 | scoreboard players operation %tempp1lo reg = %param0%0 reg 2 | scoreboard players operation %tempp1lo reg %= %%65536 reg 3 | 4 | execute unless score %param1%0 reg matches 0..65535 run scoreboard players set %temp1lo reg 1 5 | 6 | execute if score %tempp1lo reg matches 0 run scoreboard players operation %return%0 reg = %param0%0 reg 7 | execute if score %tempp1lo reg matches 0 run scoreboard players operation %return%0 reg += %param1%0 reg 8 | 9 | execute unless score %tempp1lo reg matches 0 run function intrinsic:or_normal -------------------------------------------------------------------------------- /src/intrinsic/or_inner.mcfunction: -------------------------------------------------------------------------------- 1 | # return <<= 1 2 | scoreboard players operation %return%0 reg += %return%0 reg 3 | 4 | # if param0 < 0 { c += 1 } 5 | execute if score %param0%0 reg matches ..-1 run scoreboard players add %return%0 reg 1 6 | 7 | # else if param0 < 0 { c += 1 } 8 | execute if score %param1%0 reg matches ..-1 if score %param0%0 reg matches 0.. run scoreboard players add %return%0 reg 1 9 | 10 | scoreboard players operation %param0%0 reg += %param0%0 reg 11 | scoreboard players operation %param1%0 reg += %param1%0 reg -------------------------------------------------------------------------------- /src/intrinsic/or_normal.mcfunction: -------------------------------------------------------------------------------- 1 | scoreboard players set %return%0 reg 0 2 | 3 | # First half: 4 | # 1-4 5 | function intrinsic:or_inner 6 | function intrinsic:or_inner 7 | function intrinsic:or_inner 8 | function intrinsic:or_inner 9 | 10 | # 5-8 11 | function intrinsic:or_inner 12 | function intrinsic:or_inner 13 | function intrinsic:or_inner 14 | function intrinsic:or_inner 15 | 16 | # 9-12 17 | function intrinsic:or_inner 18 | function intrinsic:or_inner 19 | function intrinsic:or_inner 20 | function intrinsic:or_inner 21 | 22 | # 13-16 23 | function intrinsic:or_inner 24 | function intrinsic:or_inner 25 | function intrinsic:or_inner 26 | function intrinsic:or_inner 27 | 28 | # Second half: 29 | # 1-4 30 | function intrinsic:or_inner 31 | function intrinsic:or_inner 32 | function intrinsic:or_inner 33 | function intrinsic:or_inner 34 | 35 | # 5-8 36 | function intrinsic:or_inner 37 | function intrinsic:or_inner 38 | function intrinsic:or_inner 39 | function intrinsic:or_inner 40 | 41 | # 9-12 42 | function intrinsic:or_inner 43 | function intrinsic:or_inner 44 | function intrinsic:or_inner 45 | function intrinsic:or_inner 46 | 47 | # 13-16 48 | function intrinsic:or_inner 49 | function intrinsic:or_inner 50 | function intrinsic:or_inner 51 | function intrinsic:or_inner -------------------------------------------------------------------------------- /src/intrinsic/pop_and_branch.mcfunction: -------------------------------------------------------------------------------- 1 | # tellraw @a [{"text": "%stackptr at start of pop_and_branch is "}, {"score": {"name": "%stackptr", "objective": "rust" } }] 2 | scoreboard players remove %stackptr rust 4 3 | scoreboard players operation %ptr rust = %stackptr rust 4 | function intrinsic:setptr 5 | execute at @e[tag=ptr] store result score %%temp0_pab rust run data get block ~ ~ ~ RecordItem.tag.Memory 1 6 | 7 | scoreboard players operation %%tempz_pab rust = %%temp0_pab rust 8 | scoreboard players operation %%tempz_pab rust %= %%ROW_SIZE rust 9 | 10 | scoreboard players operation %%tempx_pab rust = %%temp0_pab rust 11 | scoreboard players operation %%tempx_pab rust /= %%ROW_SIZE rust 12 | scoreboard players operation %%tempx_pab rust *= %%-1 rust 13 | 14 | execute as @e[tag=ptr] store result entity @s Pos[0] double 1 run scoreboard players get %%tempx_pab rust 15 | execute as @e[tag=ptr] store result entity @s Pos[2] double 1 run scoreboard players get %%tempz_pab rust 16 | execute as @e[tag=ptr] at @s run tp @s ~-2 1 ~ 17 | execute if score %%temp0_pab rust matches 0.. run execute at @e[tag=ptr] run setblock ~ ~ ~ minecraft:redstone_block -------------------------------------------------------------------------------- /src/intrinsic/popcnt.mcfunction: -------------------------------------------------------------------------------- 1 | # %param0%0 - input 2 | # %return%0 - output 3 | 4 | scoreboard players set %return%0 reg 0 5 | 6 | # --- First half --- 7 | 8 | # 0 - 3 9 | execute if score %param0%0 reg matches ..-1 run scoreboard players add %return%0 reg 1 10 | scoreboard players operation %param0%0 reg += %param0%0 reg 11 | execute if score %param0%0 reg matches ..-1 run scoreboard players add %return%0 reg 1 12 | scoreboard players operation %param0%0 reg += %param0%0 reg 13 | execute if score %param0%0 reg matches ..-1 run scoreboard players add %return%0 reg 1 14 | scoreboard players operation %param0%0 reg += %param0%0 reg 15 | execute if score %param0%0 reg matches ..-1 run scoreboard players add %return%0 reg 1 16 | scoreboard players operation %param0%0 reg += %param0%0 reg 17 | 18 | # 4 - 7 19 | execute if score %param0%0 reg matches ..-1 run scoreboard players add %return%0 reg 1 20 | scoreboard players operation %param0%0 reg += %param0%0 reg 21 | execute if score %param0%0 reg matches ..-1 run scoreboard players add %return%0 reg 1 22 | scoreboard players operation %param0%0 reg += %param0%0 reg 23 | execute if score %param0%0 reg matches ..-1 run scoreboard players add %return%0 reg 1 24 | scoreboard players operation %param0%0 reg += %param0%0 reg 25 | execute if score %param0%0 reg matches ..-1 run scoreboard players add %return%0 reg 1 26 | scoreboard players operation %param0%0 reg += %param0%0 reg 27 | 28 | # 8 - 11 29 | execute if score %param0%0 reg matches ..-1 run scoreboard players add %return%0 reg 1 30 | scoreboard players operation %param0%0 reg += %param0%0 reg 31 | execute if score %param0%0 reg matches ..-1 run scoreboard players add %return%0 reg 1 32 | scoreboard players operation %param0%0 reg += %param0%0 reg 33 | execute if score %param0%0 reg matches ..-1 run scoreboard players add %return%0 reg 1 34 | scoreboard players operation %param0%0 reg += %param0%0 reg 35 | execute if score %param0%0 reg matches ..-1 run scoreboard players add %return%0 reg 1 36 | scoreboard players operation %param0%0 reg += %param0%0 reg 37 | 38 | # 12 - 15 39 | execute if score %param0%0 reg matches ..-1 run scoreboard players add %return%0 reg 1 40 | scoreboard players operation %param0%0 reg += %param0%0 reg 41 | execute if score %param0%0 reg matches ..-1 run scoreboard players add %return%0 reg 1 42 | scoreboard players operation %param0%0 reg += %param0%0 reg 43 | execute if score %param0%0 reg matches ..-1 run scoreboard players add %return%0 reg 1 44 | scoreboard players operation %param0%0 reg += %param0%0 reg 45 | execute if score %param0%0 reg matches ..-1 run scoreboard players add %return%0 reg 1 46 | scoreboard players operation %param0%0 reg += %param0%0 reg 47 | 48 | # --- Second half --- 49 | 50 | # 0 - 3 51 | execute if score %param0%0 reg matches ..-1 run scoreboard players add %return%0 reg 1 52 | scoreboard players operation %param0%0 reg += %param0%0 reg 53 | execute if score %param0%0 reg matches ..-1 run scoreboard players add %return%0 reg 1 54 | scoreboard players operation %param0%0 reg += %param0%0 reg 55 | execute if score %param0%0 reg matches ..-1 run scoreboard players add %return%0 reg 1 56 | scoreboard players operation %param0%0 reg += %param0%0 reg 57 | execute if score %param0%0 reg matches ..-1 run scoreboard players add %return%0 reg 1 58 | scoreboard players operation %param0%0 reg += %param0%0 reg 59 | 60 | # 4 - 7 61 | execute if score %param0%0 reg matches ..-1 run scoreboard players add %return%0 reg 1 62 | scoreboard players operation %param0%0 reg += %param0%0 reg 63 | execute if score %param0%0 reg matches ..-1 run scoreboard players add %return%0 reg 1 64 | scoreboard players operation %param0%0 reg += %param0%0 reg 65 | execute if score %param0%0 reg matches ..-1 run scoreboard players add %return%0 reg 1 66 | scoreboard players operation %param0%0 reg += %param0%0 reg 67 | execute if score %param0%0 reg matches ..-1 run scoreboard players add %return%0 reg 1 68 | scoreboard players operation %param0%0 reg += %param0%0 reg 69 | 70 | # 8 - 11 71 | execute if score %param0%0 reg matches ..-1 run scoreboard players add %return%0 reg 1 72 | scoreboard players operation %param0%0 reg += %param0%0 reg 73 | execute if score %param0%0 reg matches ..-1 run scoreboard players add %return%0 reg 1 74 | scoreboard players operation %param0%0 reg += %param0%0 reg 75 | execute if score %param0%0 reg matches ..-1 run scoreboard players add %return%0 reg 1 76 | scoreboard players operation %param0%0 reg += %param0%0 reg 77 | execute if score %param0%0 reg matches ..-1 run scoreboard players add %return%0 reg 1 78 | scoreboard players operation %param0%0 reg += %param0%0 reg 79 | 80 | # 12 - 15 81 | execute if score %param0%0 reg matches ..-1 run scoreboard players add %return%0 reg 1 82 | scoreboard players operation %param0%0 reg += %param0%0 reg 83 | execute if score %param0%0 reg matches ..-1 run scoreboard players add %return%0 reg 1 84 | scoreboard players operation %param0%0 reg += %param0%0 reg 85 | execute if score %param0%0 reg matches ..-1 run scoreboard players add %return%0 reg 1 86 | scoreboard players operation %param0%0 reg += %param0%0 reg 87 | execute if score %param0%0 reg matches ..-1 run scoreboard players add %return%0 reg 1 88 | scoreboard players operation %param0%0 reg += %param0%0 reg 89 | -------------------------------------------------------------------------------- /src/intrinsic/put_char.mcfunction: -------------------------------------------------------------------------------- 1 | 2 | execute if score %param0%0 reg matches 10 run function intrinsic:put_char/flush 3 | 4 | # carriage return (13) is ignored 5 | 6 | execute if score %param0%0 reg matches ..9 run data modify storage wasm:stdout buffer append value ["�"] 7 | execute if score %param0%0 reg matches 11..12 run data modify storage wasm:stdout buffer append value ["�"] 8 | execute if score %param0%0 reg matches 14..31 run data modify storage wasm:stdout buffer append value ["�"] 9 | execute if score %param0%0 reg matches 128.. run data modify storage wasm:stdout buffer append value ["�"] 10 | 11 | execute if score %param0%0 reg matches 32 run data modify storage wasm:stdout buffer append value [" "] 12 | execute if score %param0%0 reg matches 33 run data modify storage wasm:stdout buffer append value ["!"] 13 | # Can't use double quotes in interpreted NBT, so we replace it with two single quotes. 14 | execute if score %param0%0 reg matches 34 run data modify storage wasm:stdout buffer append value ["''"] 15 | execute if score %param0%0 reg matches 35 run data modify storage wasm:stdout buffer append value ["#"] 16 | execute if score %param0%0 reg matches 36 run data modify storage wasm:stdout buffer append value ["$"] 17 | execute if score %param0%0 reg matches 37 run data modify storage wasm:stdout buffer append value ["%"] 18 | execute if score %param0%0 reg matches 38 run data modify storage wasm:stdout buffer append value ["&"] 19 | execute if score %param0%0 reg matches 39 run data modify storage wasm:stdout buffer append value ["'"] 20 | execute if score %param0%0 reg matches 40 run data modify storage wasm:stdout buffer append value ["("] 21 | execute if score %param0%0 reg matches 41 run data modify storage wasm:stdout buffer append value [")"] 22 | execute if score %param0%0 reg matches 42 run data modify storage wasm:stdout buffer append value ["*"] 23 | execute if score %param0%0 reg matches 43 run data modify storage wasm:stdout buffer append value ["+"] 24 | execute if score %param0%0 reg matches 44 run data modify storage wasm:stdout buffer append value [","] 25 | execute if score %param0%0 reg matches 45 run data modify storage wasm:stdout buffer append value ["-"] 26 | execute if score %param0%0 reg matches 46 run data modify storage wasm:stdout buffer append value ["."] 27 | execute if score %param0%0 reg matches 47 run data modify storage wasm:stdout buffer append value ["/"] 28 | execute if score %param0%0 reg matches 48 run data modify storage wasm:stdout buffer append value ["0"] 29 | execute if score %param0%0 reg matches 49 run data modify storage wasm:stdout buffer append value ["1"] 30 | execute if score %param0%0 reg matches 50 run data modify storage wasm:stdout buffer append value ["2"] 31 | execute if score %param0%0 reg matches 51 run data modify storage wasm:stdout buffer append value ["3"] 32 | execute if score %param0%0 reg matches 52 run data modify storage wasm:stdout buffer append value ["4"] 33 | execute if score %param0%0 reg matches 53 run data modify storage wasm:stdout buffer append value ["5"] 34 | execute if score %param0%0 reg matches 54 run data modify storage wasm:stdout buffer append value ["6"] 35 | execute if score %param0%0 reg matches 55 run data modify storage wasm:stdout buffer append value ["7"] 36 | execute if score %param0%0 reg matches 56 run data modify storage wasm:stdout buffer append value ["8"] 37 | execute if score %param0%0 reg matches 57 run data modify storage wasm:stdout buffer append value ["9"] 38 | execute if score %param0%0 reg matches 58 run data modify storage wasm:stdout buffer append value [":"] 39 | execute if score %param0%0 reg matches 59 run data modify storage wasm:stdout buffer append value [";"] 40 | execute if score %param0%0 reg matches 60 run data modify storage wasm:stdout buffer append value ["<"] 41 | execute if score %param0%0 reg matches 61 run data modify storage wasm:stdout buffer append value ["="] 42 | execute if score %param0%0 reg matches 62 run data modify storage wasm:stdout buffer append value [">"] 43 | execute if score %param0%0 reg matches 63 run data modify storage wasm:stdout buffer append value ["?"] 44 | execute if score %param0%0 reg matches 64 run data modify storage wasm:stdout buffer append value ["@"] 45 | execute if score %param0%0 reg matches 65 run data modify storage wasm:stdout buffer append value ["A"] 46 | execute if score %param0%0 reg matches 66 run data modify storage wasm:stdout buffer append value ["B"] 47 | execute if score %param0%0 reg matches 67 run data modify storage wasm:stdout buffer append value ["C"] 48 | execute if score %param0%0 reg matches 68 run data modify storage wasm:stdout buffer append value ["D"] 49 | execute if score %param0%0 reg matches 69 run data modify storage wasm:stdout buffer append value ["E"] 50 | execute if score %param0%0 reg matches 70 run data modify storage wasm:stdout buffer append value ["F"] 51 | execute if score %param0%0 reg matches 71 run data modify storage wasm:stdout buffer append value ["G"] 52 | execute if score %param0%0 reg matches 72 run data modify storage wasm:stdout buffer append value ["H"] 53 | execute if score %param0%0 reg matches 73 run data modify storage wasm:stdout buffer append value ["I"] 54 | execute if score %param0%0 reg matches 74 run data modify storage wasm:stdout buffer append value ["J"] 55 | execute if score %param0%0 reg matches 75 run data modify storage wasm:stdout buffer append value ["K"] 56 | execute if score %param0%0 reg matches 76 run data modify storage wasm:stdout buffer append value ["L"] 57 | execute if score %param0%0 reg matches 77 run data modify storage wasm:stdout buffer append value ["M"] 58 | execute if score %param0%0 reg matches 78 run data modify storage wasm:stdout buffer append value ["N"] 59 | execute if score %param0%0 reg matches 79 run data modify storage wasm:stdout buffer append value ["O"] 60 | execute if score %param0%0 reg matches 80 run data modify storage wasm:stdout buffer append value ["P"] 61 | execute if score %param0%0 reg matches 81 run data modify storage wasm:stdout buffer append value ["Q"] 62 | execute if score %param0%0 reg matches 82 run data modify storage wasm:stdout buffer append value ["R"] 63 | execute if score %param0%0 reg matches 83 run data modify storage wasm:stdout buffer append value ["S"] 64 | execute if score %param0%0 reg matches 84 run data modify storage wasm:stdout buffer append value ["T"] 65 | execute if score %param0%0 reg matches 85 run data modify storage wasm:stdout buffer append value ["U"] 66 | execute if score %param0%0 reg matches 86 run data modify storage wasm:stdout buffer append value ["V"] 67 | execute if score %param0%0 reg matches 87 run data modify storage wasm:stdout buffer append value ["W"] 68 | execute if score %param0%0 reg matches 88 run data modify storage wasm:stdout buffer append value ["X"] 69 | execute if score %param0%0 reg matches 89 run data modify storage wasm:stdout buffer append value ["Y"] 70 | execute if score %param0%0 reg matches 90 run data modify storage wasm:stdout buffer append value ["Z"] 71 | execute if score %param0%0 reg matches 91 run data modify storage wasm:stdout buffer append value ["["] 72 | execute if score %param0%0 reg matches 92 run data modify storage wasm:stdout buffer append value ["\\"] 73 | execute if score %param0%0 reg matches 93 run data modify storage wasm:stdout buffer append value ["]"] 74 | execute if score %param0%0 reg matches 94 run data modify storage wasm:stdout buffer append value ["^"] 75 | execute if score %param0%0 reg matches 95 run data modify storage wasm:stdout buffer append value ["_"] 76 | execute if score %param0%0 reg matches 96 run data modify storage wasm:stdout buffer append value ["`"] 77 | execute if score %param0%0 reg matches 97 run data modify storage wasm:stdout buffer append value ["a"] 78 | execute if score %param0%0 reg matches 98 run data modify storage wasm:stdout buffer append value ["b"] 79 | execute if score %param0%0 reg matches 99 run data modify storage wasm:stdout buffer append value ["c"] 80 | execute if score %param0%0 reg matches 100 run data modify storage wasm:stdout buffer append value ["d"] 81 | execute if score %param0%0 reg matches 101 run data modify storage wasm:stdout buffer append value ["e"] 82 | execute if score %param0%0 reg matches 102 run data modify storage wasm:stdout buffer append value ["f"] 83 | execute if score %param0%0 reg matches 103 run data modify storage wasm:stdout buffer append value ["g"] 84 | execute if score %param0%0 reg matches 104 run data modify storage wasm:stdout buffer append value ["h"] 85 | execute if score %param0%0 reg matches 105 run data modify storage wasm:stdout buffer append value ["i"] 86 | execute if score %param0%0 reg matches 106 run data modify storage wasm:stdout buffer append value ["j"] 87 | execute if score %param0%0 reg matches 107 run data modify storage wasm:stdout buffer append value ["k"] 88 | execute if score %param0%0 reg matches 108 run data modify storage wasm:stdout buffer append value ["l"] 89 | execute if score %param0%0 reg matches 109 run data modify storage wasm:stdout buffer append value ["m"] 90 | execute if score %param0%0 reg matches 110 run data modify storage wasm:stdout buffer append value ["n"] 91 | execute if score %param0%0 reg matches 111 run data modify storage wasm:stdout buffer append value ["o"] 92 | execute if score %param0%0 reg matches 112 run data modify storage wasm:stdout buffer append value ["p"] 93 | execute if score %param0%0 reg matches 113 run data modify storage wasm:stdout buffer append value ["q"] 94 | execute if score %param0%0 reg matches 114 run data modify storage wasm:stdout buffer append value ["r"] 95 | execute if score %param0%0 reg matches 115 run data modify storage wasm:stdout buffer append value ["s"] 96 | execute if score %param0%0 reg matches 116 run data modify storage wasm:stdout buffer append value ["t"] 97 | execute if score %param0%0 reg matches 117 run data modify storage wasm:stdout buffer append value ["u"] 98 | execute if score %param0%0 reg matches 118 run data modify storage wasm:stdout buffer append value ["v"] 99 | execute if score %param0%0 reg matches 119 run data modify storage wasm:stdout buffer append value ["w"] 100 | execute if score %param0%0 reg matches 120 run data modify storage wasm:stdout buffer append value ["x"] 101 | execute if score %param0%0 reg matches 121 run data modify storage wasm:stdout buffer append value ["y"] 102 | execute if score %param0%0 reg matches 122 run data modify storage wasm:stdout buffer append value ["z"] 103 | execute if score %param0%0 reg matches 123 run data modify storage wasm:stdout buffer append value ["{"] 104 | execute if score %param0%0 reg matches 124 run data modify storage wasm:stdout buffer append value ["|"] 105 | execute if score %param0%0 reg matches 125 run data modify storage wasm:stdout buffer append value ["}"] 106 | execute if score %param0%0 reg matches 126 run data modify storage wasm:stdout buffer append value ["~"] 107 | -------------------------------------------------------------------------------- /src/intrinsic/put_char/flush.mcfunction: -------------------------------------------------------------------------------- 1 | tellraw @a {"nbt":"buffer", "storage":"wasm:stdout","interpret":true} 2 | data modify storage wasm:stdout buffer set value [] -------------------------------------------------------------------------------- /src/intrinsic/rotl.mcfunction: -------------------------------------------------------------------------------- 1 | # 2 | # %param0%0 - lhs 3 | # %param1%0 - rhs 4 | 5 | # pseudocode: 6 | # rotl(p0, p1) { 7 | # carry = p0 & (1 << 31) 8 | # if p1 > 0 { 9 | # p0 *= 2 10 | # p0 += carry; 11 | # rotl(p0, p1 - 1); 12 | # } 13 | # } 14 | 15 | execute store success score %%temprotl_carry reg if score %param0%0 reg matches ..-1 16 | 17 | execute if score %param1%0 reg matches 1.. run scoreboard players operation %param0%0 reg += %param0%0 reg 18 | execute if score %param1%0 reg matches 1.. run scoreboard players operation %param0%0 reg += %%temprotl_carry reg 19 | scoreboard players remove %param1%0 reg 1 20 | execute if score %param1%0 reg matches 1.. run function intrinsic:rotl 21 | -------------------------------------------------------------------------------- /src/intrinsic/rotl_64.mcfunction: -------------------------------------------------------------------------------- 1 | # Arguments: 2 | # %param0%0 3 | # %param0%1 - The value to be shifted (also the output) 4 | 5 | # %param1%0 - The amount to shift by 6 | 7 | execute if score %param1%0 reg matches 1.. run function intrinsic:rotl_64_once 8 | 9 | scoreboard players remove %param1%0 reg 1 10 | execute if score %param1%0 reg matches 1.. run function intrinsic:rotl_64 11 | -------------------------------------------------------------------------------- /src/intrinsic/rotl_64_once.mcfunction: -------------------------------------------------------------------------------- 1 | # Arguments: 2 | # %param0%0 3 | # %param0%1 - The value to be shifted (also the output) 4 | 5 | scoreboard players operation %%temprotl64_param1_save reg = %param1%0 reg 6 | 7 | # carry = (lhs & (1 << 63)) != 0 8 | execute store success score %%temprotl64_carry reg if score %param0%1 reg matches ..-1 9 | scoreboard players set %param1%0 reg 1 10 | function intrinsic:shl_64 11 | scoreboard players operation %param0%0 reg += %%temprotl64_carry reg 12 | 13 | scoreboard players operation %param1%0 reg = %%temprotl64_param1_save reg -------------------------------------------------------------------------------- /src/intrinsic/rotr.mcfunction: -------------------------------------------------------------------------------- 1 | # 2 | # %param0%0 - lhs 3 | # %param1%0 - rhs 4 | 5 | # pseudocode: 6 | # rotl(p0, p1) { 7 | # carry = p0 & (1 << 31) 8 | # if p1 > 0 { 9 | # p0 *= 2 10 | # p0 += carry; 11 | # rotl(p0, p1 - 1); 12 | # } 13 | # } 14 | 15 | execute if score %param1%0 reg matches 1.. run function intrinsic:rotr/rotr_inner 16 | execute if score %param1%0 reg matches 1.. run function intrinsic:rotr 17 | -------------------------------------------------------------------------------- /src/intrinsic/rotr/rotr_inner.mcfunction: -------------------------------------------------------------------------------- 1 | # temp_carry = (param0_hi & 0x1) << 31; 2 | 3 | scoreboard players operation %%temp_carry reg = %param0%0 reg 4 | scoreboard players operation %%temp_carry reg %= %%2 reg 5 | scoreboard players operation %%temp_carry reg *= %%-2147483648 reg 6 | 7 | # ==== Logical shift right ==== 8 | 9 | # was_neg = (param0_hi < 0) 10 | execute store success score %%temp0_rotr_inner reg if score %param0%0 reg matches ..-1 11 | # if (was_neg) { param0_1 += 1 << 31 }; 12 | execute if score %%temp0_rotr_inner reg matches 1..1 run scoreboard players operation %param0%0 reg += %%-2147483648 reg 13 | 14 | scoreboard players operation %param0%0 reg /= %%2 reg 15 | 16 | # if (was_neg) { param0_1 += 1 << 30 } 17 | execute if score %%temp0_rotr_inner reg matches 1..1 run scoreboard players add %param0%0 reg 1073741824 18 | 19 | scoreboard players operation %param0%0 reg += %%temp_carry reg 20 | 21 | scoreboard players remove %param1%0 reg 1 -------------------------------------------------------------------------------- /src/intrinsic/rotr_64.mcfunction: -------------------------------------------------------------------------------- 1 | # Arguments 2 | # 3 | # %param0%0 4 | # %param0%1 - The value to be shifted 5 | # 6 | # %param1%0 - The amount to shift by 7 | 8 | execute if score %param1%0 reg matches 1.. run function intrinsic:rotr_64_once 9 | 10 | scoreboard players remove %param1%0 reg 1 11 | execute if score %param1%0 reg matches 1.. run function intrinsic:rotr_64 -------------------------------------------------------------------------------- /src/intrinsic/rotr_64_once.mcfunction: -------------------------------------------------------------------------------- 1 | 2 | scoreboard players operation %%temprotr64_carry reg = %param0%0 reg 3 | scoreboard players operation %%temprotr64_carry reg %= %%2 reg 4 | scoreboard players operation %%temprotr64_carry reg *= %%-2147483648 reg 5 | 6 | function intrinsic:lshr_i64/shift_once 7 | 8 | scoreboard players operation %param0%1 reg += %%temprotr64_carry reg -------------------------------------------------------------------------------- /src/intrinsic/setptr.mcfunction: -------------------------------------------------------------------------------- 1 | scoreboard players operation %%ptr reg = %ptr reg 2 | scoreboard players operation %%ptr reg /= %%4 reg 3 | scoreboard players operation %z reg = %%ptr reg 4 | scoreboard players operation %z reg %= %%PAGE_SPAN_Z reg 5 | scoreboard players operation %%ptr reg /= %%PAGE_SPAN_Z reg 6 | scoreboard players operation %y reg = %%ptr reg 7 | scoreboard players operation %y reg %= %%PAGE_SPAN_Y reg 8 | scoreboard players operation %%ptr reg /= %%PAGE_SPAN_Y reg 9 | execute store result storage wasm:scratch Pos[0] double 1 run scoreboard players get %%ptr reg 10 | execute store result storage wasm:scratch Pos[1] double 1 run scoreboard players get %y reg 11 | execute store result storage wasm:scratch Pos[2] double 1 run scoreboard players get %z reg 12 | data modify entity 44453000-0-0-0-1 Pos set from storage wasm:scratch Pos -------------------------------------------------------------------------------- /src/intrinsic/shift_from_ptr.mcfunction: -------------------------------------------------------------------------------- 1 | scoreboard players operation %%ptr rust = %ptr rust 2 | scoreboard players operation %%ptr rust %= %%4 rust 3 | 4 | # let %param0%1 = %param0%0 < 0 5 | execute store success score %param0%1 rust if score %param0%0 rust matches ..-1 6 | 7 | execute if score %param0%1 rust matches 1..1 run scoreboard players operation %param0%0 rust *= %%-1 rust 8 | execute if score %param0%1 rust matches 1..1 run scoreboard players operation %param0%0 rust += %%-1 rust 9 | 10 | execute if score %%ptr rust matches 1.. run function intrinsic:shift_from_ptr_inner 11 | 12 | execute if score %param0%1 rust matches 1..1 run scoreboard players operation %param0%0 rust *= %%-1 rust 13 | execute if score %param0%1 rust matches 1..1 run scoreboard players operation %param0%0 rust += %%-1 rust 14 | 15 | scoreboard players operation %%ptr rust = %ptr rust 16 | scoreboard players operation %%ptr rust %= %%4 rust 17 | 18 | execute if score %param0%1 rust matches 1..1 if score %%ptr rust matches 1..1 run scoreboard players add %param0%0 rust 16777216 19 | execute if score %param0%1 rust matches 1..1 if score %%ptr rust matches 2..2 run scoreboard players add %param0%0 rust 65536 20 | execute if score %param0%1 rust matches 1..1 if score %%ptr rust matches 3..3 run scoreboard players add %param0%0 rust 256 -------------------------------------------------------------------------------- /src/intrinsic/shift_from_ptr_inner.mcfunction: -------------------------------------------------------------------------------- 1 | scoreboard players operation %param0%0 rust /= %%256 rust 2 | scoreboard players remove %%ptr rust 1 3 | execute if score %%ptr rust matches 1.. run function intrinsic:shift_from_ptr_inner -------------------------------------------------------------------------------- /src/intrinsic/shl.mcfunction: -------------------------------------------------------------------------------- 1 | # Arguments: 2 | # %param0%0 - The value to be shifted (also the output) 3 | # %param1%0 - The amount to shift by, is clobbered 4 | 5 | execute if score %param1%0 reg matches 16.. run scoreboard players operation %param0%0 reg *= %%65536 reg 6 | execute if score %param1%0 reg matches 16.. run scoreboard players remove %param1%0 reg 16 7 | 8 | execute if score %param1%0 reg matches 8.. run scoreboard players operation %param0%0 reg *= %%256 reg 9 | execute if score %param1%0 reg matches 8.. run scoreboard players remove %param1%0 reg 8 10 | 11 | execute if score %param1%0 reg matches 4.. run scoreboard players operation %param0%0 reg *= %%16 reg 12 | execute if score %param1%0 reg matches 4.. run scoreboard players remove %param1%0 reg 4 13 | 14 | execute if score %param1%0 reg matches 2.. run scoreboard players operation %param0%0 reg *= %%4 reg 15 | execute if score %param1%0 reg matches 2.. run scoreboard players remove %param1%0 reg 2 16 | 17 | execute if score %param1%0 reg matches 1.. run scoreboard players operation %param0%0 reg *= %%2 reg 18 | 19 | # execute if score %param1%0 reg matches 1.. run scoreboard players operation %param0%0 reg *= %%2 reg 20 | # scoreboard players remove %param1%0 reg 1 21 | # execute if score %param1%0 reg matches 0.. run function intrinsic:shl -------------------------------------------------------------------------------- /src/intrinsic/shl_64.mcfunction: -------------------------------------------------------------------------------- 1 | # Arguments: 2 | # %param0%0 3 | # %param0%1 - The value to be shifted (also the output) 4 | 5 | # %param1%0 - The amount to shift by, is clobbered 6 | 7 | # temp_carry = ((param0_lo) & (1 << 31)) != 0 8 | execute if score %param1%0 reg matches 1.. run execute store success score %temp_carry reg if score %param0%0 reg matches ..-1 9 | # param0_lo <<= 1 10 | execute if score %param1%0 reg matches 1.. run scoreboard players operation %param0%0 reg *= %%2 reg 11 | # param0_hi <<= 1 12 | execute if score %param1%0 reg matches 1.. run scoreboard players operation %param0%1 reg *= %%2 reg 13 | # param0_lo += carry 14 | execute if score %param1%0 reg matches 1.. run scoreboard players operation %param0%1 reg += %temp_carry reg 15 | 16 | scoreboard players remove %param1%0 reg 1 17 | execute if score %param1%0 reg matches 0.. run function intrinsic:shl_64 -------------------------------------------------------------------------------- /src/intrinsic/store_byte.mcfunction: -------------------------------------------------------------------------------- 1 | # This assumes the memory pointer is already at the correct location 2 | # arguments: 3 | # %ptr - The location to write to 4 | # %param2%0 - The byte to write 5 | 6 | scoreboard players operation %tempsave_store_byte reg = %param2%0 reg 7 | scoreboard players operation %param2%0 reg %= %%256 reg 8 | 9 | execute at 44453000-0-0-0-1 store result score %param0%0 reg run data get block ~ ~ ~ RecordItem.tag.Memory 1 10 | 11 | scoreboard players operation %%temp0_store_byte reg = %ptr reg 12 | scoreboard players operation %%temp0_store_byte reg %= %%4 reg 13 | 14 | scoreboard players operation %return%0 reg = %param0%0 reg 15 | # 0xFFFF_FF00 16 | execute if score %%temp0_store_byte reg matches 0 run scoreboard players operation %param0%0 reg %= %%256 reg 17 | execute if score %%temp0_store_byte reg matches 0 run scoreboard players operation %return%0 reg -= %param0%0 reg 18 | # 0xFFFF_00FF 19 | execute if score %%temp0_store_byte reg matches 1 run scoreboard players operation %param0%0 reg %= %%65536 reg 20 | execute if score %%temp0_store_byte reg matches 1 run scoreboard players operation %param0%0 reg /= %%256 reg 21 | execute if score %%temp0_store_byte reg matches 1 run scoreboard players operation %param0%0 reg *= %%256 reg 22 | execute if score %%temp0_store_byte reg matches 1 run scoreboard players operation %return%0 reg -= %param0%0 reg 23 | # 0xFF00_FFFF 24 | execute if score %%temp0_store_byte reg matches 2 run scoreboard players operation %param0%0 reg %= %%16777216 reg 25 | execute if score %%temp0_store_byte reg matches 2 run scoreboard players operation %param0%0 reg /= %%65536 reg 26 | execute if score %%temp0_store_byte reg matches 2 run scoreboard players operation %param0%0 reg *= %%65536 reg 27 | execute if score %%temp0_store_byte reg matches 2 run scoreboard players operation %return%0 reg -= %param0%0 reg 28 | # 0x00FF_FFFF 29 | execute if score %%temp0_store_byte reg matches 3 run scoreboard players operation %return%0 reg %= %%16777216 reg 30 | 31 | # %param2%0 *= 1 << 0 32 | #execute if score %%temp0_store_byte reg matches 0..0 run scoreboard players operation %param2%0 reg *= %%1 reg 33 | # %param2%0 *= 1 << 8 34 | execute if score %%temp0_store_byte reg matches 1 run scoreboard players operation %param2%0 reg *= %%256 reg 35 | # %param2%0 *= 1 << 16 36 | execute if score %%temp0_store_byte reg matches 2 run scoreboard players operation %param2%0 reg *= %%65536 reg 37 | # %param2%0 *= 1 << 24 38 | execute if score %%temp0_store_byte reg matches 3 run scoreboard players operation %param2%0 reg *= %%16777216 reg 39 | 40 | scoreboard players operation %return%0 reg += %param2%0 reg 41 | 42 | execute at 44453000-0-0-0-1 store result block ~ ~ ~ RecordItem.tag.Memory int 1 run scoreboard players get %return%0 reg 43 | 44 | scoreboard players operation %param2%0 reg = %tempsave_store_byte reg -------------------------------------------------------------------------------- /src/intrinsic/store_halfword.mcfunction: -------------------------------------------------------------------------------- 1 | # arguments: 2 | # %ptr - The location to write to 3 | # %param2%0 - The halfword to write 4 | 5 | scoreboard players operation %tempsave_store_word reg = %param2%0 reg 6 | scoreboard players operation %param2%0 reg %= %%65536 reg 7 | 8 | scoreboard players operation %%temp0_store_byte reg = %ptr reg 9 | scoreboard players operation %%temp0_store_byte reg %= %%2 reg 10 | execute unless score %%temp0_store_byte reg matches 0 run function intrinsic:store_halfword_unaligned 11 | execute if score %%temp0_store_byte reg matches 0 run function intrinsic:store_halfword_aligned -------------------------------------------------------------------------------- /src/intrinsic/store_halfword_aligned.mcfunction: -------------------------------------------------------------------------------- 1 | function intrinsic:setptr 2 | 3 | execute at 44453000-0-0-0-1 store result score %param0%0 reg run data get block ~ ~ ~ RecordItem.tag.Memory 1 4 | 5 | scoreboard players operation %%temp0_store_byte reg = %ptr reg 6 | scoreboard players operation %%temp0_store_byte reg %= %%4 reg 7 | 8 | scoreboard players operation %return%0 reg = %param0%0 reg 9 | # %return%0 = %param0%0 & 0xFFFF_0000 10 | execute if score %%temp0_store_byte reg matches 0..0 run scoreboard players operation %param0%0 reg %= %%65536 reg 11 | execute if score %%temp0_store_byte reg matches 0..0 run scoreboard players operation %return%0 reg -= %param0%0 reg 12 | # %return%0 = %param0%0 & 0x0000_FFFF 13 | execute if score %%temp0_store_byte reg matches 2..2 run scoreboard players operation %return%0 reg %= %%65536 reg 14 | 15 | # %param2%0 *= 1 << 0 16 | execute if score %%temp0_store_byte reg matches 0..0 run scoreboard players operation %param2%0 reg *= %%1 reg 17 | # %param2%0 *= 1 << 16 18 | execute if score %%temp0_store_byte reg matches 2..2 run scoreboard players operation %param2%0 reg *= %%65536 reg 19 | 20 | scoreboard players operation %return%0 reg += %param2%0 reg 21 | 22 | execute at 44453000-0-0-0-1 store result block ~ ~ ~ RecordItem.tag.Memory int 1 run scoreboard players get %return%0 reg 23 | 24 | scoreboard players operation %param2%0 reg = %tempsave_store_word reg -------------------------------------------------------------------------------- /src/intrinsic/store_halfword_unaligned.mcfunction: -------------------------------------------------------------------------------- 1 | # Arguments 2 | # %ptr - Address to store at 3 | # %param0%0 - Word to be stored 4 | 5 | scoreboard players operation %tempsave_swu reg = %param0%0 reg 6 | scoreboard players operation %param0%0 reg %= %%65536 reg 7 | 8 | scoreboard players operation %%temp0_swu reg = %param0%0 reg 9 | 10 | # FIXME: This may not actually work like an `and` 11 | scoreboard players operation %param2%0 reg = %%temp0_swu reg 12 | scoreboard players operation %param2%0 reg %= %%256 reg 13 | function intrinsic:setptr 14 | function intrinsic:store_byte 15 | scoreboard players add %ptr reg 1 16 | 17 | scoreboard players operation %param0%0 reg = %%temp0_swu reg 18 | scoreboard players set %param1%0 reg 8 19 | function intrinsic:lshr 20 | scoreboard players operation %param2%0 reg = %param0%0 reg 21 | scoreboard players operation %param2%0 reg %= %%256 reg 22 | function intrinsic:setptr 23 | function intrinsic:store_byte 24 | 25 | scoreboard players operation %param0%0 reg = %tempsave_swu reg -------------------------------------------------------------------------------- /src/intrinsic/store_word.mcfunction: -------------------------------------------------------------------------------- 1 | scoreboard players operation %%align reg = %ptr reg 2 | scoreboard players operation %%align reg %= %%4 reg 3 | execute if score %%align reg matches 0 run execute at 44453000-0-0-0-1 store result block ~ ~ ~ RecordItem.tag.Memory int 1 run scoreboard players get %param0%0 reg 4 | execute if score %%align reg matches 1 run function intrinsic:store_word_unaligned 5 | execute if score %%align reg matches 2 run function intrinsic:store_word_halfaligned 6 | execute if score %%align reg matches 3 run function intrinsic:store_word_unaligned -------------------------------------------------------------------------------- /src/intrinsic/store_word_halfaligned.mcfunction: -------------------------------------------------------------------------------- 1 | 2 | scoreboard players operation %swha_savedptr reg = %ptr reg 3 | 4 | scoreboard players set %tempsave_store_word reg 0 5 | 6 | scoreboard players operation %param2%0 reg = %param0%0 reg 7 | scoreboard players operation %param2%0 reg %= %%65536 reg 8 | function intrinsic:store_halfword_aligned 9 | 10 | scoreboard players add %ptr reg 2 11 | scoreboard players operation %param2%0 reg = %param0%0 reg 12 | scoreboard players operation %param2%0 reg /= %%65536 reg 13 | scoreboard players operation %param2%0 reg %= %%65536 reg 14 | function intrinsic:store_halfword_aligned 15 | 16 | scoreboard players operation %ptr reg = %swha_savedptr reg -------------------------------------------------------------------------------- /src/intrinsic/store_word_unaligned.mcfunction: -------------------------------------------------------------------------------- 1 | # Arguments 2 | # %ptr - Address to store at 3 | # %param0%0 - Word to be stored 4 | 5 | # tellraw @a [{"text":"storing unaligned word "},{"score": {"name": "%%align", "objective": "reg"}}] 6 | 7 | scoreboard players operation %%temp0_swu reg = %param0%0 reg 8 | 9 | # FIXME: This may not actually work like an `and` 10 | scoreboard players operation %param2%0 reg = %%temp0_swu reg 11 | scoreboard players operation %param2%0 reg %= %%256 reg 12 | function intrinsic:setptr 13 | function intrinsic:store_byte 14 | scoreboard players add %ptr reg 1 15 | 16 | scoreboard players operation %param0%0 reg = %%temp0_swu reg 17 | scoreboard players set %param1%0 reg 8 18 | function intrinsic:lshr 19 | scoreboard players operation %param2%0 reg = %param0%0 reg 20 | scoreboard players operation %param2%0 reg %= %%256 reg 21 | function intrinsic:setptr 22 | function intrinsic:store_byte 23 | scoreboard players add %ptr reg 1 24 | 25 | scoreboard players operation %param0%0 reg = %%temp0_swu reg 26 | scoreboard players set %param1%0 reg 16 27 | function intrinsic:lshr 28 | scoreboard players operation %param2%0 reg = %param0%0 reg 29 | scoreboard players operation %param2%0 reg %= %%256 reg 30 | function intrinsic:setptr 31 | function intrinsic:store_byte 32 | scoreboard players add %ptr reg 1 33 | 34 | scoreboard players operation %param0%0 reg = %%temp0_swu reg 35 | scoreboard players set %param1%0 reg 24 36 | function intrinsic:lshr 37 | scoreboard players operation %param2%0 reg = %param0%0 reg 38 | scoreboard players operation %param2%0 reg %= %%256 reg 39 | function intrinsic:setptr 40 | function intrinsic:store_byte -------------------------------------------------------------------------------- /src/intrinsic/xor.mcfunction: -------------------------------------------------------------------------------- 1 | # first argument - %param0%0 reg 2 | # second argument - %param1%0 reg 3 | # return value - %return%0 reg 4 | 5 | execute if score %param1%0 reg matches 0 run scoreboard players operation %return%0 reg = %param0%0 reg 6 | 7 | execute if score %param1%0 reg matches -1 run scoreboard players operation %return%0 reg = %param0%0 reg 8 | execute if score %param1%0 reg matches -1 run scoreboard players operation %return%0 reg *= %%-1 reg 9 | execute if score %param1%0 reg matches -1 run scoreboard players remove %return%0 reg 1 10 | 11 | execute unless score %param1%0 reg matches -1..0 run function intrinsic:xor_normal -------------------------------------------------------------------------------- /src/intrinsic/xor_inner.mcfunction: -------------------------------------------------------------------------------- 1 | # return <<= 1 2 | scoreboard players operation %return%0 reg += %return%0 reg 3 | 4 | # if param0 < 0 && param1 >= 0 { c += 1 } 5 | execute if score %param0%0 reg matches ..-1 if score %param1%0 reg matches 0.. run scoreboard players add %return%0 reg 1 6 | 7 | # if param0 >= 0 && param1 < 0 { c += 1 } 8 | execute if score %param0%0 reg matches 0.. if score %param1%0 reg matches ..-1 run scoreboard players add %return%0 reg 1 9 | 10 | scoreboard players operation %param0%0 reg += %param0%0 reg 11 | scoreboard players operation %param1%0 reg += %param1%0 reg -------------------------------------------------------------------------------- /src/intrinsic/xor_normal.mcfunction: -------------------------------------------------------------------------------- 1 | # first argument - %param0%0 reg 2 | # second argument - %param1%0 reg 3 | # return value - %return%0 reg 4 | 5 | scoreboard players set %return%0 reg 0 6 | 7 | # First half: 8 | # 1-4 9 | function intrinsic:xor_inner 10 | function intrinsic:xor_inner 11 | function intrinsic:xor_inner 12 | function intrinsic:xor_inner 13 | 14 | # 5-8 15 | function intrinsic:xor_inner 16 | function intrinsic:xor_inner 17 | function intrinsic:xor_inner 18 | function intrinsic:xor_inner 19 | 20 | # 9-12 21 | function intrinsic:xor_inner 22 | function intrinsic:xor_inner 23 | function intrinsic:xor_inner 24 | function intrinsic:xor_inner 25 | 26 | # 13-16 27 | function intrinsic:xor_inner 28 | function intrinsic:xor_inner 29 | function intrinsic:xor_inner 30 | function intrinsic:xor_inner 31 | 32 | # Second half: 33 | # 1-4 34 | function intrinsic:xor_inner 35 | function intrinsic:xor_inner 36 | function intrinsic:xor_inner 37 | function intrinsic:xor_inner 38 | 39 | # 5-8 40 | function intrinsic:xor_inner 41 | function intrinsic:xor_inner 42 | function intrinsic:xor_inner 43 | function intrinsic:xor_inner 44 | 45 | # 9-12 46 | function intrinsic:xor_inner 47 | function intrinsic:xor_inner 48 | function intrinsic:xor_inner 49 | function intrinsic:xor_inner 50 | 51 | # 13-16 52 | function intrinsic:xor_inner 53 | function intrinsic:xor_inner 54 | function intrinsic:xor_inner 55 | function intrinsic:xor_inner -------------------------------------------------------------------------------- /src/lir/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod interp; 2 | 3 | use std::{ops::RangeInclusive, fmt, collections::{HashSet, HashMap}}; 4 | 5 | use datapack_common::functions::command_components::{ScoreHolder, Objective}; 6 | use wasmparser::ValType; 7 | 8 | use crate::ssa::{BlockId, Memory, interp::TypedValue, Table, lir_emitter::RegisterWithInfo}; 9 | 10 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] 11 | pub enum Half { 12 | Hi, 13 | Lo, 14 | } 15 | 16 | impl fmt::Display for Half { 17 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 18 | match self { 19 | Half::Lo => write!(f, "lo"), 20 | Half::Hi => write!(f, "hi"), 21 | } 22 | } 23 | } 24 | 25 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] 26 | pub enum DoubleRegister { 27 | /// function id, register id 28 | Work(u32, u32), 29 | Temp(u32), 30 | Return(u32), 31 | Param(u32), 32 | Const(i64), 33 | Global(u32), 34 | CondTaken, 35 | SleepNeeded, 36 | } 37 | 38 | impl DoubleRegister { 39 | pub fn lo(self) -> Register { 40 | Register { double: self, half: Half::Lo } 41 | } 42 | 43 | pub fn hi(self) -> Register { 44 | Register { double: self, half: Half::Hi } 45 | } 46 | 47 | pub fn split_lo_hi(self) -> (Register, Register) { 48 | (self.lo(), self.hi()) 49 | } 50 | 51 | pub fn return_reg(id: u32) -> DoubleRegister { 52 | DoubleRegister::Return(id) 53 | } 54 | 55 | pub fn temp(id: u32) -> DoubleRegister { 56 | DoubleRegister::Temp(id) 57 | } 58 | 59 | pub fn param(id: u32) -> DoubleRegister { 60 | DoubleRegister::Param(id) 61 | } 62 | 63 | pub fn global(id: u32) -> DoubleRegister { 64 | DoubleRegister::Global(id) 65 | } 66 | 67 | pub fn const_val(val: i64) -> DoubleRegister { 68 | DoubleRegister::Const(val) 69 | } 70 | } 71 | 72 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] 73 | pub struct Register { 74 | double: DoubleRegister, 75 | half: Half, 76 | } 77 | 78 | impl Register { 79 | pub fn work_lo(func: u32, id: u32) -> Register { 80 | DoubleRegister::Work(func, id).lo() 81 | } 82 | 83 | pub fn work_hi(func: u32, id: u32) -> Register { 84 | DoubleRegister::Work(func, id).hi() 85 | } 86 | 87 | pub fn return_lo(id: u32) -> Register { 88 | DoubleRegister::return_reg(id).lo() 89 | } 90 | 91 | pub fn return_hi(id: u32) -> Register { 92 | DoubleRegister::return_reg(id).hi() 93 | } 94 | 95 | pub fn temp_lo(id: u32) -> Register { 96 | DoubleRegister::temp(id).lo() 97 | } 98 | 99 | pub fn temp_hi(id: u32) -> Register { 100 | DoubleRegister::temp(id).hi() 101 | } 102 | 103 | pub fn param_lo(id: u32) -> Register { 104 | DoubleRegister::param(id).lo() 105 | } 106 | 107 | pub fn param_hi(id: u32) -> Register { 108 | DoubleRegister::param(id).hi() 109 | } 110 | 111 | pub fn global_lo(id: u32) -> Register { 112 | DoubleRegister::global(id).lo() 113 | } 114 | 115 | pub fn global_hi(id: u32) -> Register { 116 | DoubleRegister::global(id).hi() 117 | } 118 | 119 | pub fn cond_taken() -> Register { 120 | DoubleRegister::CondTaken.lo() 121 | } 122 | 123 | pub fn sleep_needed() -> Register { 124 | DoubleRegister::SleepNeeded.lo() 125 | } 126 | 127 | pub fn const_val(v: i32) -> Register { 128 | DoubleRegister::Const(v as i64).lo() 129 | } 130 | 131 | pub fn get_const(self) -> Option { 132 | if let DoubleRegister::Const(c) = self.double { 133 | match self.half { 134 | Half::Hi => Some((c >> 32) as i32), 135 | Half::Lo => Some(c as i32), 136 | } 137 | } else { 138 | None 139 | } 140 | } 141 | 142 | pub fn scoreboard_pair(self) -> (ScoreHolder, Objective) { 143 | let s = self.to_string(); 144 | let (holder, obj) = s.split_once(' ').unwrap(); 145 | let holder = ScoreHolder::new(holder.to_string()).unwrap(); 146 | let obj = Objective::new(obj.to_string()).unwrap(); 147 | (holder, obj) 148 | } 149 | } 150 | 151 | const OBJECTIVE_NAME: &str = "reg"; 152 | 153 | impl fmt::Display for Register { 154 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 155 | let half = self.half; 156 | match self.double { 157 | DoubleRegister::Work(func, reg) => write!(f, "%work%{func}%{reg}%{half}")?, 158 | DoubleRegister::Temp(reg) => write!(f, "%temp%{reg}%{half}")?, 159 | DoubleRegister::Return(reg) => write!(f, "%return%{reg}%{half}")?, 160 | DoubleRegister::Param(reg) => write!(f, "%param%{reg}%{half}")?, 161 | DoubleRegister::Global(reg) => write!(f, "%global%{reg}%{half}")?, 162 | DoubleRegister::Const(val) if half == Half::Hi => write!(f, "%const%{}", (val >> 32) as i32)?, 163 | DoubleRegister::Const(val) => write!(f,"%const%{}", val as i32)?, 164 | DoubleRegister::CondTaken => write!(f, "%condtaken")?, 165 | DoubleRegister::SleepNeeded => write!(f, "%sleepneeded")?, 166 | } 167 | 168 | write!(f, " {OBJECTIVE_NAME}") 169 | } 170 | } 171 | 172 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] 173 | pub enum DynRegister { 174 | Double(DoubleRegister), 175 | Single(Register), 176 | } 177 | 178 | impl From for DynRegister { 179 | fn from(value: DoubleRegister) -> Self { 180 | DynRegister::Double(value) 181 | } 182 | } 183 | 184 | impl From for DynRegister { 185 | fn from(value: Register) -> Self { 186 | DynRegister::Single(value) 187 | 188 | } 189 | } 190 | 191 | /// by default: 192 | /// 193 | /// work%0%lo 194 | 195 | #[derive(Clone, Debug)] 196 | pub enum Condition { 197 | Matches(Register, RangeInclusive), 198 | NotMatches(Register, RangeInclusive), 199 | } 200 | 201 | impl Condition { 202 | pub fn eq_zero(reg: Register) -> Self { 203 | Condition::Matches(reg, 0..=0) 204 | } 205 | 206 | pub fn eq_const(reg: Register, val: i32) -> Self { 207 | Condition::Matches(reg, val..=val) 208 | } 209 | 210 | pub fn neq_zero(reg: Register) -> Self { 211 | Condition::NotMatches(reg, 0..=0) 212 | } 213 | } 214 | 215 | #[derive(Debug, Clone)] 216 | pub enum LirInstr { 217 | Assign(Register, Register), 218 | Set(Register, i32), 219 | 220 | Add(Register, Register), 221 | Sub(Register, Register), 222 | Mul(Register, Register), 223 | DivS(Register, Register, Register), 224 | DivU(Register, Register, Register), 225 | RemS(Register, Register, Register), 226 | RemU(Register, Register, Register), 227 | 228 | MulTo64(DoubleRegister, Register, Register), 229 | 230 | Add64(DoubleRegister, DoubleRegister, DoubleRegister), 231 | Sub64(DoubleRegister, DoubleRegister, DoubleRegister), 232 | DivS64(DoubleRegister, DoubleRegister, DoubleRegister), 233 | DivU64(DoubleRegister, DoubleRegister, DoubleRegister), 234 | RemS64(DoubleRegister, DoubleRegister, DoubleRegister), 235 | RemU64(DoubleRegister, DoubleRegister, DoubleRegister), 236 | 237 | Shl(Register, Register, Register), 238 | ShrS(Register, Register, Register), 239 | ShrU(Register, Register, Register), 240 | Rotl(Register, Register, Register), 241 | Rotr(Register, Register, Register), 242 | 243 | Shl64(DoubleRegister, DoubleRegister, DoubleRegister), 244 | ShrS64(DoubleRegister, DoubleRegister, DoubleRegister), 245 | ShrU64(DoubleRegister, DoubleRegister, DoubleRegister), 246 | Rotl64(DoubleRegister, DoubleRegister, DoubleRegister), 247 | Rotr64(DoubleRegister, DoubleRegister, DoubleRegister), 248 | 249 | Xor(Register, RegisterWithInfo, RegisterWithInfo), 250 | And(Register, RegisterWithInfo, RegisterWithInfo), 251 | Or(Register, RegisterWithInfo, RegisterWithInfo), 252 | 253 | PopcntAdd(Register, Register), 254 | Ctz(Register, Register), 255 | Clz(Register, Register), 256 | 257 | Ctz64(DoubleRegister, DoubleRegister), 258 | Clz64(DoubleRegister, DoubleRegister), 259 | 260 | Eqz(Register, Register), 261 | Eqz64(Register, DoubleRegister), 262 | 263 | GtS(Register, Register, Register), 264 | GtU(Register, Register, Register), 265 | GeS(Register, Register, Register), 266 | GeU(Register, Register, Register), 267 | LtS(Register, Register, Register), 268 | LtU(Register, Register, Register), 269 | LeS(Register, Register, Register), 270 | LeU(Register, Register, Register), 271 | Eq(Register, Register, Register), 272 | Ne(Register, Register, Register), 273 | 274 | GtS64(Register, DoubleRegister, DoubleRegister), 275 | GtU64(Register, DoubleRegister, DoubleRegister), 276 | GeS64(Register, DoubleRegister, DoubleRegister), 277 | GeU64(Register, DoubleRegister, DoubleRegister), 278 | LtS64(Register, DoubleRegister, DoubleRegister), 279 | LtU64(Register, DoubleRegister, DoubleRegister), 280 | LeS64(Register, DoubleRegister, DoubleRegister), 281 | LeU64(Register, DoubleRegister, DoubleRegister), 282 | Eq64(Register, DoubleRegister, DoubleRegister), 283 | Ne64(Register, DoubleRegister, DoubleRegister), 284 | 285 | 286 | /// arg, bits 287 | Trunc(Register, u32), 288 | 289 | SignExtend8(Register), 290 | SignExtend16(Register), 291 | SignExtend32(DoubleRegister), 292 | 293 | LocalSet(u32, Half, Register), 294 | LocalGet(Register, u32, Half), 295 | 296 | GlobalSet(u32, Half, Register), 297 | GlobalGet(Register, u32, Half), 298 | 299 | // src, addr 300 | Store32(Register, RegisterWithInfo), 301 | Store16(Register, RegisterWithInfo), 302 | Store8(Register, RegisterWithInfo), 303 | 304 | // dst, addr 305 | Load64(DoubleRegister, RegisterWithInfo), 306 | Load32(Register, RegisterWithInfo), 307 | Load16(Register, RegisterWithInfo), 308 | Load8(Register, RegisterWithInfo), 309 | 310 | /// arg, old width (assumes high bits are zero) 311 | SignExtend(Register, u32), 312 | 313 | Select { dst: Register, true_reg: Register, false_reg: Register, cond: Register }, 314 | 315 | Call { func: u32 }, 316 | CallIndirect { table: Vec>, table_entry: Register }, 317 | 318 | Push(Vec), 319 | Pop(Vec), 320 | 321 | IfCond { cond: Condition, instr: Box }, 322 | 323 | PushLocalFrame(Vec), 324 | PopLocalFrame(Vec), 325 | 326 | Memset { dest: Register, value: Register, length: Register, result: Register }, 327 | 328 | TurtleSetX(Register), 329 | TurtleSetY(Register), 330 | TurtleSetZ(Register), 331 | TurtleSetBlock(Register), 332 | TurtleFillBlock { block: Register, x_span: Register, y_span: Register, z_span: Register }, 333 | TurtleCopyRegion { x_span: Register, y_span: Register, z_span: Register }, 334 | TurtlePasteRegionMasked { x_span: Register, y_span: Register, z_span: Register }, 335 | TurtleGetBlock(Register), 336 | TurtleCopy, 337 | TurtlePaste, 338 | PrintInt(Register), 339 | PutChar(Register), 340 | 341 | PushReturnAddr(BlockId), 342 | PopReturnAddr, 343 | } 344 | 345 | impl LirInstr { 346 | pub fn if_cond(self, cond: Condition) -> LirInstr { 347 | LirInstr::IfCond { cond, instr: Box::new(self) } 348 | } 349 | 350 | pub fn unless_cond_taken(self) -> LirInstr { 351 | self.if_cond(Condition::eq_zero(Register::cond_taken())) 352 | } 353 | } 354 | 355 | #[derive(Debug, Clone, Copy)] 356 | pub struct LirJumpTarget { 357 | /// The location in the code to jump to 358 | pub label: BlockId, 359 | /// If true, jumping to this target may be scheduled instead of direct 360 | pub cmd_check: bool, 361 | } 362 | 363 | #[derive(Debug)] 364 | pub enum LirTerminator { 365 | ScheduleJump(BlockId, u32), 366 | Jump(LirJumpTarget), 367 | JumpIf { true_label: LirJumpTarget, false_label: LirJumpTarget, cond: Register }, 368 | JumpTable { arms: Vec>, default: Option, cond: Register }, 369 | Return, 370 | ReturnToSaved, 371 | } 372 | 373 | pub struct LirBasicBlock { 374 | pub body: Vec, 375 | pub term: LirTerminator, 376 | } 377 | 378 | pub struct LirFunction { 379 | pub code: Vec<(BlockId, LirBasicBlock)>, 380 | pub returns: Box<[ValType]>, 381 | } 382 | 383 | impl LirFunction { 384 | pub fn func_id(&self) -> usize { 385 | self.code[0].0.func 386 | } 387 | } 388 | 389 | pub struct LirProgram { 390 | pub globals: Vec, 391 | pub memory: Vec, 392 | pub tables: Vec, 393 | pub code: Vec, 394 | pub constants: HashSet, 395 | pub exports: HashMap, 396 | } 397 | 398 | impl LirProgram { 399 | pub fn all_block_ids(&self) -> impl Iterator + '_ { 400 | self.code.iter().flat_map(|func| { 401 | func.code.iter().map(|(id, _)| *id) 402 | }) 403 | } 404 | 405 | pub fn get_block_index(&self, id: BlockId) -> usize { 406 | self.all_block_ids().enumerate().find(|(_, i)| *i == id).unwrap().0 407 | } 408 | } -------------------------------------------------------------------------------- /src/runner.rs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SuperTails/wasmcraft2/5431ca420e25cc8ec0fe880da4825d083f6937b6/src/runner.rs -------------------------------------------------------------------------------- /src/set.rs: -------------------------------------------------------------------------------- 1 | use bit_set::BitSet; 2 | use std::{fmt, marker::PhantomData}; 3 | 4 | #[derive(Debug, Default, PartialEq, Eq, Clone)] 5 | pub struct Set { 6 | data: BitSet, 7 | marker: PhantomData, 8 | } 9 | 10 | impl Set { 11 | pub fn new() -> Self { 12 | Set { 13 | data: BitSet::new(), 14 | marker: PhantomData, 15 | } 16 | } 17 | } 18 | 19 | impl Set { 20 | pub fn remove(&mut self, elem: T) { 21 | self.data.remove(elem.to_usize()); 22 | } 23 | 24 | /// Equivalent to calling `remove` on all elements in the iterator. 25 | pub fn remove_from(&mut self, iter: I) 26 | where 27 | I: IntoIterator, 28 | { 29 | for elem in iter { 30 | self.remove(elem); 31 | } 32 | } 33 | 34 | /// Makes this set the difference with the specified other set in-place. 35 | pub fn difference_with(&mut self, other: &Self) { 36 | self.data.difference_with(&other.data); 37 | } 38 | 39 | pub fn insert(&mut self, elem: T) { 40 | self.data.insert(elem.to_usize()); 41 | } 42 | 43 | /// Unions in-place with the specified other set. 44 | pub fn union_with(&mut self, other: &Self) { 45 | self.data.union_with(&other.data); 46 | } 47 | 48 | pub fn contains(&self, elem: T) -> bool { 49 | self.data.contains(elem.to_usize()) 50 | } 51 | 52 | pub fn iter(&self) -> SetIter { 53 | SetIter(self.data.iter(), PhantomData) 54 | } 55 | 56 | pub fn len(&self) -> usize { 57 | self.data.len() 58 | } 59 | } 60 | 61 | impl Extend for Set { 62 | fn extend(&mut self, iter: I) 63 | where 64 | I: IntoIterator, 65 | { 66 | for elem in iter { 67 | self.insert(elem); 68 | } 69 | } 70 | } 71 | 72 | pub struct SetIter<'a, T>(bit_set::Iter<'a, u32>, PhantomData); 73 | 74 | impl Iterator for SetIter<'_, T> { 75 | type Item = T; 76 | 77 | fn next(&mut self) -> Option { 78 | self.0.next().map(T::from_usize) 79 | } 80 | } 81 | 82 | impl<'a, T: Enumerable> IntoIterator for &'a Set { 83 | type Item = T; 84 | 85 | type IntoIter = SetIter<'a, T>; 86 | 87 | fn into_iter(self) -> Self::IntoIter { 88 | self.iter() 89 | } 90 | } 91 | 92 | impl FromIterator for Set { 93 | fn from_iter>(iter: I) -> Self { 94 | let mut result = Set::new(); 95 | for elem in iter { 96 | result.insert(elem); 97 | } 98 | result 99 | } 100 | } 101 | 102 | /* 103 | struct Cluster { 104 | start: usize, 105 | data: BitSet, 106 | } 107 | 108 | pub struct ClusteredSet { 109 | data: Vec, 110 | _marker: PhantomData, 111 | } 112 | 113 | impl CompactSet { 114 | pub fn new() -> Self { 115 | CompactSet { 116 | start: 0, 117 | len: 0, 118 | data: Vec::new(), 119 | _marker: PhantomData, 120 | } 121 | } 122 | 123 | pub fn is_empty(&self) -> bool { 124 | self.len() == 0 125 | } 126 | 127 | pub fn len(&self) -> usize { 128 | self.len 129 | } 130 | } 131 | 132 | */ 133 | 134 | pub trait Enumerable { 135 | fn to_usize(self) -> usize; 136 | 137 | fn from_usize(index: usize) -> Self; 138 | } 139 | 140 | impl Enumerable for usize { 141 | fn to_usize(self) -> usize { 142 | self 143 | } 144 | 145 | fn from_usize(index: usize) -> Self { 146 | index 147 | } 148 | } 149 | 150 | /*#[derive(Default, Debug, PartialEq, Eq, Clone)] 151 | pub struct PairMap(Vec<(K, V)>); 152 | 153 | impl PairMap { 154 | pub fn new() -> Self { 155 | Default::default() 156 | } 157 | 158 | pub fn insert() 159 | }*/ 160 | 161 | /*impl disjoint_sets::ElementType for Temp { 162 | fn from_usize(n: usize) -> Option { 163 | u32::try_from(n).ok().map(Temp) 164 | } 165 | 166 | fn to_usize(self) -> usize { 167 | self.0 as usize 168 | } 169 | }*/ 170 | 171 | #[derive(PartialEq, Eq, Clone)] 172 | pub struct DenseMap { 173 | values: Vec>, 174 | marker: PhantomData, 175 | } 176 | 177 | impl DenseMap { 178 | pub fn new() -> Self { 179 | DenseMap { 180 | values: Vec::new(), 181 | marker: PhantomData, 182 | } 183 | } 184 | 185 | pub fn with_capacity(capacity: usize) -> Self { 186 | DenseMap { 187 | values: Vec::with_capacity(capacity), 188 | marker: PhantomData, 189 | } 190 | } 191 | } 192 | 193 | impl Default for DenseMap { 194 | fn default() -> Self { 195 | DenseMap::new() 196 | } 197 | } 198 | 199 | impl DenseMap { 200 | pub fn insert(&mut self, k: K, v: V) -> Option { 201 | let k = k.to_usize(); 202 | if k >= self.values.len() { 203 | self.values.resize_with(k + 1, || None); 204 | } 205 | 206 | std::mem::replace(&mut self.values[k], Some(v)) 207 | } 208 | 209 | pub fn get(&self, k: K) -> Option<&V> { 210 | let k = k.to_usize(); 211 | self.values.get(k).and_then(|v| v.as_ref()) 212 | } 213 | 214 | pub fn get_mut(&mut self, k: K) -> Option<&mut V> { 215 | let k = k.to_usize(); 216 | self.values.get_mut(k).and_then(|v| v.as_mut()) 217 | } 218 | 219 | pub fn contains(&self, k: K) -> bool { 220 | self.get(k).is_some() 221 | } 222 | 223 | pub fn iter(&self) -> DenseMapIter { 224 | DenseMapIter(self.values.iter().enumerate(), PhantomData) 225 | } 226 | 227 | #[allow(clippy::needless_lifetimes)] // false positive 228 | pub fn keys<'a>(&'a self) -> impl Iterator + 'a { 229 | self.iter().map(|(k, _)| k) 230 | } 231 | } 232 | 233 | pub struct DenseMapIter<'a, K, V>( 234 | std::iter::Enumerate>>, 235 | PhantomData, 236 | ); 237 | 238 | impl<'a, K: Enumerable, V> Iterator for DenseMapIter<'a, K, V> { 239 | type Item = (K, &'a V); 240 | 241 | fn next(&mut self) -> Option { 242 | loop { 243 | let pair = self.0.next()?; 244 | if let (key, Some(value)) = pair { 245 | let key = K::from_usize(key); 246 | return Some((key, value)); 247 | } 248 | } 249 | } 250 | } 251 | 252 | impl fmt::Debug for DenseMap { 253 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 254 | f.debug_map().entries(self.iter()).finish() 255 | } 256 | } 257 | 258 | /// Represents all key-value pairs as literal pairs in a Vec. 259 | /// Good for a map with only a small handful of entries and quickly-comparable keys. 260 | #[derive(Clone)] 261 | pub struct PairMap { 262 | values: Vec<(K, V)>, 263 | } 264 | 265 | impl PairMap { 266 | pub fn new() -> Self { 267 | PairMap { 268 | values: Vec::new(), 269 | } 270 | } 271 | 272 | pub fn with_capacity(capacity: usize) -> Self { 273 | PairMap { 274 | values: Vec::with_capacity(capacity), 275 | } 276 | } 277 | 278 | pub fn len(&self) -> usize { 279 | self.values.len() 280 | } 281 | 282 | pub fn is_empty(&self) -> bool { 283 | self.values.is_empty() 284 | } 285 | } 286 | 287 | impl Default for PairMap { 288 | fn default() -> Self { 289 | PairMap::new() 290 | } 291 | } 292 | 293 | impl PairMap { 294 | pub fn insert(&mut self, k: K, v: V) -> Option { 295 | let prev = self.values.iter_mut().find(|(key, _)| key == &k); 296 | if let Some((_, prev_value)) = prev { 297 | Some(std::mem::replace(prev_value, v)) 298 | } else { 299 | self.values.push((k, v)); 300 | None 301 | } 302 | } 303 | 304 | pub fn remove(&mut self, k: &K) -> Option { 305 | let prev = self.values.iter_mut().enumerate().find(|(_, (key, _))| key == k); 306 | if let Some((prev, _)) = prev { 307 | Some(self.values.swap_remove(prev).1) 308 | } else { 309 | None 310 | } 311 | } 312 | 313 | pub fn get(&self, k: &K) -> Option<&V> { 314 | self.values.iter().find(|(key, _)| key == k).map(|(_, v)| v) 315 | } 316 | 317 | pub fn get_mut(&mut self, k: &K) -> Option<&mut V> { 318 | self.values.iter_mut().find(|(key, _)| key == k).map(|(_, v)| v) 319 | } 320 | 321 | pub fn contains_key(&self, k: &K) -> bool { 322 | self.get(k).is_some() 323 | } 324 | 325 | pub fn iter(&self) -> impl Iterator { 326 | self.values.iter() 327 | } 328 | 329 | pub fn iter_mut(&mut self) -> impl Iterator { 330 | self.values.iter_mut() 331 | } 332 | 333 | #[allow(clippy::needless_lifetimes)] // false positive 334 | pub fn keys<'a>(&'a self) -> impl Iterator + 'a { 335 | self.iter().map(|(k, _)| k) 336 | } 337 | } 338 | 339 | impl IntoIterator for PairMap { 340 | type Item = (K, V); 341 | 342 | type IntoIter = std::vec::IntoIter<(K, V)>; 343 | 344 | fn into_iter(self) -> Self::IntoIter { 345 | self.values.into_iter() 346 | } 347 | } 348 | 349 | impl fmt::Debug for PairMap { 350 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 351 | let i = self.iter().map(|(k, v)| (k, v)); 352 | f.debug_map().entries(i).finish() 353 | } 354 | } 355 | -------------------------------------------------------------------------------- /src/ssa/call_graph.rs: -------------------------------------------------------------------------------- 1 | use std::collections::{HashMap, HashSet}; 2 | 3 | use crate::ssa::SsaTerminator; 4 | 5 | use super::{SsaProgram, SsaInstr, SsaFunction, TypedSsaVar, lir_emitter::get_compatible_functions, liveness::DomTree}; 6 | 7 | #[derive(Debug, Clone, Copy)] 8 | struct TableInfo { 9 | is_only_single_tick: bool, 10 | is_only_multi_tick: bool, 11 | } 12 | 13 | pub struct CallGraph { 14 | // Map from a function ID to the functions that it can call directly. 15 | direct_calls: HashMap>, 16 | 17 | // The keys are table IDs 18 | table_info: HashMap, 19 | 20 | // The keys are function IDs 21 | is_single_tick: HashMap, 22 | } 23 | 24 | impl CallGraph { 25 | pub fn new(program: &SsaProgram) -> Self { 26 | println!("doing direct calls!"); 27 | 28 | let direct_calls = get_direct_calls(program); 29 | 30 | println!("did direct calls, doing single tick!"); 31 | 32 | let is_single_tick = get_single_tick_funcs(program); 33 | 34 | println!("did single tick, doing tables!"); 35 | 36 | // FIXME: This becomes invalid once table-modifying instructions are added 37 | let mut table_info = HashMap::new(); 38 | for (table_idx, table) in program.tables.iter().enumerate() { 39 | let mut is_only_single_tick = true; 40 | let mut is_only_multi_tick = true; 41 | for elem in table.elements.iter().copied().flatten() { 42 | let target_is_single_tick = *is_single_tick.get(&(elem as u32)).unwrap(); 43 | if target_is_single_tick { 44 | is_only_multi_tick = false; 45 | } else { 46 | is_only_single_tick = false; 47 | } 48 | } 49 | 50 | table_info.insert(table_idx as u32, TableInfo { is_only_single_tick, is_only_multi_tick }); 51 | } 52 | 53 | CallGraph { direct_calls, table_info, is_single_tick } 54 | } 55 | 56 | pub fn may_call(&self, caller: u32, callee: u32) -> bool { 57 | let callees = self.direct_calls.get(&caller).unwrap(); 58 | callees.contains(&callee) 59 | } 60 | 61 | pub fn table_may_call(&self, _caller_table: u32, _callee: u32) -> bool { 62 | // TODO: 63 | true 64 | } 65 | 66 | pub fn is_single_tick(&self, func: u32) -> bool { 67 | *self.is_single_tick.get(&func).unwrap() 68 | } 69 | 70 | // true if all jump targets in a table are single tick 71 | pub fn table_is_only_single_tick(&self, table: u32) -> bool { 72 | self.table_info.get(&table).unwrap().is_only_single_tick 73 | } 74 | 75 | // true if all jump targets in a table are multi tick 76 | pub fn table_is_only_multi_tick(&self, table: u32) -> bool { 77 | self.table_info.get(&table).unwrap().is_only_multi_tick 78 | } 79 | } 80 | 81 | fn get_direct_calls(program: &SsaProgram) -> HashMap> { 82 | let mut direct_calls = HashMap::new(); 83 | 84 | for func in program.code.iter() { 85 | let caller_id = func.func_id(); 86 | 87 | let mut callee_ids = HashSet::new(); 88 | 89 | callee_ids.extend(iter_all_calls(program, func)); 90 | 91 | assert!(!direct_calls.contains_key(&caller_id)); 92 | direct_calls.insert(caller_id, callee_ids); 93 | } 94 | 95 | direct_calls 96 | 97 | } 98 | 99 | // Returns an iterator over the function IDs that this function can call using call instructions 100 | fn iter_direct_calls(func: &SsaFunction) -> impl Iterator + '_ { 101 | func.iter().flat_map(|(_, block)| { 102 | block.body.iter().filter_map(|instr| { 103 | if let SsaInstr::Call { function_index, .. } = instr { 104 | Some(*function_index) 105 | } else { 106 | None 107 | } 108 | }) 109 | }) 110 | } 111 | 112 | // Returns an iterator over the table IDs, params, and returns that this function uses in call_indirect instructions 113 | fn iter_indirect_tables(func: &SsaFunction) -> impl Iterator + '_ { 114 | func.iter().flat_map(|(_, block)| { 115 | block.body.iter().filter_map(|instr| { 116 | if let SsaInstr::CallIndirect { table_index, params, returns, .. } = instr { 117 | Some((*table_index, ¶ms[..], &returns[..])) 118 | } else { 119 | None 120 | } 121 | }) 122 | }) 123 | } 124 | 125 | // Returns an iterator over the function IDs that this function can call through call_indirect instructions 126 | fn iter_indirect_calls<'a>(program: &'a SsaProgram, func: &'a SsaFunction) -> impl Iterator + 'a { 127 | iter_indirect_tables(func).flat_map(|(table_idx, params, returns)| { 128 | let table = &program.tables[table_idx as usize]; 129 | let funcs = get_compatible_functions(program, table, params, returns); 130 | funcs.flatten().map(|e| e as u32) 131 | }) 132 | } 133 | 134 | fn iter_all_calls<'a>(program: &'a SsaProgram, func: &'a SsaFunction) -> impl Iterator + 'a { 135 | iter_direct_calls(func).chain(iter_indirect_calls(program, func)) 136 | } 137 | 138 | fn contains_scheduled_jump(func: &SsaFunction) -> bool { 139 | func.iter().any(|(_, block)| { 140 | matches!(block.term, SsaTerminator::ScheduleJump { .. } ) 141 | }) 142 | } 143 | 144 | fn contains_back_edge(func: &SsaFunction) -> bool { 145 | let dom_tree = DomTree::analyze(func); 146 | 147 | for (block_id, block) in func.iter() { 148 | for succ_node in block.term.successors() { 149 | if dom_tree.dominates(succ_node, block_id) { 150 | return true 151 | } 152 | } 153 | } 154 | 155 | false 156 | } 157 | 158 | fn get_single_tick_funcs(program: &SsaProgram) -> HashMap { 159 | // A function is single tick iff: 160 | // - It contains no scheduled jumps, 161 | // - it contains no back edges, and 162 | // - all of its callees are single tick 163 | 164 | let mut is_single_tick = HashMap::new(); 165 | 166 | for func in program.code.iter() { 167 | let is_st = !contains_scheduled_jump(func) && !contains_back_edge(func); 168 | is_single_tick.insert(func.func_id(), is_st); 169 | } 170 | 171 | let mut changed = true; 172 | while changed { 173 | changed = false; 174 | 175 | for func in program.code.iter() { 176 | if is_single_tick.get(&func.func_id()) == Some(&false) { 177 | continue; 178 | } 179 | 180 | let mut callees = iter_all_calls(program, func); 181 | let callees_single_tick = callees.all(|c| is_single_tick.get(&c).copied().unwrap_or(true)); 182 | 183 | if !callees_single_tick { 184 | is_single_tick.insert(func.func_id(), false); 185 | changed = true; 186 | } 187 | } 188 | } 189 | 190 | is_single_tick 191 | } -------------------------------------------------------------------------------- /src/ssa/copy_prop.rs: -------------------------------------------------------------------------------- 1 | 2 | // Notation from https://inst.eecs.berkeley.edu/~cs164/sp11/lectures/lecture37-2x2.pdf 3 | enum CopyValue { 4 | /// No value has reached here yet 5 | Bottom, 6 | Definite(TypedSsaVar), 7 | Top, 8 | } -------------------------------------------------------------------------------- /src/ssa/dce.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashSet; 2 | 3 | use super::{SsaProgram, liveness::{FullLivenessInfo}, BlockId, SsaBasicBlock, SsaTerminator}; 4 | 5 | pub fn do_dead_code_elim(_program: &mut SsaProgram) { 6 | println!("TODO: Dead code elimination") 7 | } 8 | 9 | /*pub fn do_dead_code_elim(program: &mut SsaProgram) { 10 | for func in program.code.iter_mut() { 11 | let mut reachable_blocks = HashSet::new(); 12 | 13 | let mut to_visit = vec![func.entry_point_id()]; 14 | while let Some(node) = to_visit.pop() { 15 | if reachable_blocks.contains(&node) { 16 | continue; 17 | } 18 | reachable_blocks.insert(node); 19 | 20 | to_visit.extend(func.get(node).term.successors()); 21 | } 22 | 23 | for idx in func.code.iter_all_keys() { 24 | if !reachable_blocks.contains(&idx) { 25 | func.code.remove(idx); 26 | } 27 | } 28 | } 29 | 30 | for func in program.code.iter_mut() { 31 | let live_info = FullLivenessInfo::analyze(func); 32 | 33 | for (block_id, block) in func.iter_mut() { 34 | let changes = get_dce_changes(block_id, block, &live_info); 35 | 36 | apply_changes(block, &changes); 37 | } 38 | } 39 | 40 | for func in program.code.iter_mut() { 41 | // Safe to precalculate it once because DCE doesn't change overall control-flow. 42 | let pred_info = super::liveness::PredInfo::new(func); 43 | 44 | let mut changed = true; 45 | while changed { 46 | changed = false; 47 | 48 | let live_info = FullLivenessInfo::analyze(func); 49 | 50 | let ids = func.iter().map(|(i, _)| i).collect::>(); 51 | 52 | for block_id in ids { 53 | let block_ids = pred_info.get_predecessors(block_id).iter().copied().collect::>(); 54 | 55 | let block = func.get_mut(block_id); 56 | let indices_to_remove = get_param_dce_indices(block_id, block, &live_info); 57 | 58 | if !indices_to_remove.is_empty() { 59 | changed = true; 60 | } 61 | 62 | remove_from_params(block, &indices_to_remove); 63 | 64 | for pred_id in block_ids { 65 | let block = func.get_mut(pred_id); 66 | remove_from_target(pred_id, block_id, block, &indices_to_remove); 67 | } 68 | } 69 | } 70 | 71 | println!("Did DCE on {}", func.func_id()); 72 | } 73 | } 74 | 75 | fn remove_from_target(_block_id: BlockId, child: BlockId, block: &mut SsaBasicBlock, mut indices: &[usize]) { 76 | let mut offset = 0; 77 | let mut i = 0; 78 | while let Some(index) = indices.first() { 79 | if index - offset == i { 80 | indices = &indices[1..]; 81 | offset += 1; 82 | 83 | match &mut block.term { 84 | SsaTerminator::Unreachable => {}, 85 | SsaTerminator::ScheduleJump(t, _) | 86 | SsaTerminator::Jump(t) => { 87 | t.params.remove(i); 88 | assert_eq!(t.label, child); 89 | } 90 | SsaTerminator::BranchIf { cond: _, true_target, false_target } => { 91 | if true_target.label == child { 92 | true_target.params.remove(i); 93 | } 94 | if false_target.label == child { 95 | false_target.params.remove(i); 96 | } 97 | assert!(true_target.label == child || false_target.label == child); 98 | } 99 | SsaTerminator::BranchTable { cond: _, default, arms } => { 100 | assert!(default.label == child || arms.iter().any(|a| a.label == child)); 101 | if default.label == child { 102 | default.params.remove(i); 103 | } 104 | for arm in arms { 105 | if arm.label == child { 106 | arm.params.remove(i); 107 | } 108 | } 109 | } 110 | SsaTerminator::Return(_) => panic!(), 111 | } 112 | } else { 113 | i += 1; 114 | } 115 | } 116 | } 117 | 118 | fn remove_from_params(block: &mut SsaBasicBlock, mut indices: &[usize]) { 119 | let mut offset = 0; 120 | let mut i = 0; 121 | while let Some(index) = indices.first() { 122 | if index - offset == i { 123 | indices = &indices[1..]; 124 | block.params.remove(i); 125 | offset += 1; 126 | } else { 127 | i += 1; 128 | } 129 | } 130 | } 131 | 132 | fn get_param_dce_indices(block_id: BlockId, block: &SsaBasicBlock, live_info: &FullLivenessInfo) -> Vec { 133 | block.params.iter().enumerate().filter(|(_i, p)| { 134 | !live_info.0.get(block_id).unwrap().live_in[0].contains(p) 135 | }).map(|(i, _)| i).collect() 136 | } 137 | 138 | fn get_dce_changes(block_id: BlockId, block: &SsaBasicBlock, live_info: &FullLivenessInfo) -> Vec { 139 | block.body.iter().enumerate().filter_map(|(idx, instr)| { 140 | let live_out = live_info.live_out_body(block_id, idx); 141 | let defs = instr.defs(); 142 | let all_defs_unused = defs.iter().all(|def| !live_out.contains(def)); 143 | if all_defs_unused && !instr.has_side_effects() { 144 | Some(idx) 145 | } else { 146 | None 147 | } 148 | }).collect() 149 | } 150 | 151 | fn apply_changes(block: &mut SsaBasicBlock, mut changes: &[usize]) { 152 | let mut offset = 0; 153 | 154 | let mut i = 0; 155 | while let Some(change) = changes.first() { 156 | if *change - offset == i { 157 | changes = &changes[1..]; 158 | block.body.remove(i); 159 | offset += 1; 160 | } else { 161 | i += 1; 162 | } 163 | } 164 | 165 | assert!(changes.is_empty()); 166 | } 167 | */ -------------------------------------------------------------------------------- /src/ssa/opt/dead_block_removal.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashSet; 2 | 3 | use crate::{ssa::{SsaFunction}, block_id_map::LocalBlockMap}; 4 | 5 | /// Removes blocks that are never jumped to. 6 | pub fn dead_block_removal(func: &mut SsaFunction) { 7 | let mut visited_blocks = HashSet::new(); 8 | let mut queue = vec![func.entry_point_id()]; 9 | 10 | //println!("Determining visited blocks"); 11 | 12 | while let Some(block) = queue.pop() { 13 | if visited_blocks.contains(&block.block) { 14 | continue; 15 | } 16 | 17 | visited_blocks.insert(block.block); 18 | 19 | let block = &func.get(block); 20 | 21 | for successor in block.term.successors() { 22 | queue.push(successor); 23 | } 24 | } 25 | 26 | let id = func.func_id(); 27 | 28 | let code = std::mem::replace(&mut func.code, LocalBlockMap::new(id as usize)); 29 | 30 | func.code = code 31 | .into_iter() 32 | .filter(|(idx, _)| visited_blocks.contains(&idx.block)) 33 | .collect(); 34 | 35 | for (_, block) in func.iter_mut() { 36 | let to_remove = block.phi_node.sources() 37 | .filter(|(s, _)| !visited_blocks.contains(s)) 38 | .map(|(s, _)| s) 39 | .collect::>(); 40 | 41 | for r in to_remove { 42 | block.phi_node.remove_source(r).unwrap(); 43 | } 44 | } 45 | 46 | //println!("Removing redundant phi nodes"); 47 | 48 | // Remove any phi nodes that have only a single predecessor now 49 | //remove_redundant_phi_nodes(program); 50 | 51 | //println!("Finished dead block removal"); 52 | } 53 | -------------------------------------------------------------------------------- /src/ssa/opt/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod dead_block_removal; 2 | pub mod coalesce; 3 | -------------------------------------------------------------------------------- /src/ssa/reg_alloc.rs: -------------------------------------------------------------------------------- 1 | use std::collections::{HashSet, HashMap}; 2 | 3 | use crate::{lir::{Register, DoubleRegister, DynRegister}, ssa::{liveness::print_live_ranges, opt::coalesce::{Coloring, coalesce, IFGraph}}, graph::Graph, set::DenseMap}; 4 | 5 | use super::{SsaFunction, SsaVar, liveness::{NoopLivenessInfo, FullLivenessInfo}, TypedSsaVar, BlockId}; 6 | 7 | pub trait RegAlloc { 8 | //fn analyze(ssa_func: &SsaFunction) -> Self; 9 | 10 | fn get(&self, val: SsaVar) -> Register; 11 | 12 | fn get_double(&self, val: SsaVar) -> DoubleRegister; 13 | 14 | fn get_const(&mut self, val: i32) -> Register; 15 | 16 | fn get_double_const(&mut self, val: i64) -> DoubleRegister; 17 | 18 | fn get_temp(&mut self) -> Register; 19 | 20 | fn get_temp_double(&mut self) -> DoubleRegister; 21 | 22 | fn get_typed(&self, var: TypedSsaVar) -> DynRegister { 23 | match var.ty() { 24 | wasmparser::ValType::I32 => self.get(var.into_untyped()).into(), 25 | wasmparser::ValType::I64 => self.get_double(var.into_untyped()).into(), 26 | wasmparser::ValType::F32 => todo!(), 27 | wasmparser::ValType::F64 => todo!(), 28 | _ => unreachable!(), 29 | } 30 | } 31 | 32 | fn const_pool(&self) -> &HashSet; 33 | } 34 | 35 | pub struct NoopRegAlloc { 36 | pub const_pool: HashSet, 37 | func: u32, 38 | temp: u32, 39 | } 40 | 41 | impl NoopRegAlloc { 42 | pub fn analyze(func: &SsaFunction) -> Self { 43 | NoopRegAlloc { const_pool: HashSet::new(), func: func.iter().next().unwrap().0.func as u32, temp: 1000 } 44 | } 45 | } 46 | 47 | impl RegAlloc for NoopRegAlloc { 48 | fn get(&self, val: SsaVar) -> Register { 49 | Register::work_lo(self.func, val.0) 50 | } 51 | 52 | fn get_double(&self, val: SsaVar) -> DoubleRegister { 53 | DoubleRegister::Work(self.func, val.0) 54 | } 55 | 56 | fn get_const(&mut self, val: i32) -> Register { 57 | self.const_pool.insert(val); 58 | Register::const_val(val) 59 | } 60 | 61 | fn get_double_const(&mut self, val: i64) -> DoubleRegister { 62 | self.const_pool.insert(val as i32); 63 | self.const_pool.insert((val >> 32) as i32); 64 | DoubleRegister::const_val(val) 65 | } 66 | 67 | fn get_temp(&mut self) -> Register { 68 | let reg = Register::temp_lo(self.temp); 69 | self.temp += 1; 70 | reg 71 | } 72 | 73 | fn get_temp_double(&mut self) -> DoubleRegister { 74 | let reg = DoubleRegister::temp(self.temp); 75 | self.temp += 1; 76 | reg 77 | } 78 | 79 | fn const_pool(&self) -> &HashSet { 80 | &self.const_pool 81 | } 82 | } 83 | 84 | pub struct FullRegAlloc { 85 | pub const_pool: HashSet, 86 | pub map: DenseMap, 87 | func: u32, 88 | temp: u32, 89 | } 90 | 91 | mod register_set { 92 | use std::collections::{HashMap, BTreeSet}; 93 | 94 | use crate::ssa::{liveness::LiveRange}; 95 | 96 | use super::*; 97 | 98 | #[derive(Debug)] 99 | pub struct InterfGraph(HashMap>); 100 | 101 | impl InterfGraph { 102 | pub fn interferes(&self, lhs: &MergedRegister, rhs: &MergedRegister) -> bool { 103 | for lhs_reg in lhs.members.iter() { 104 | if let Some(interf_regs) = self.0.get(lhs_reg) { 105 | for rhs_reg in interf_regs.iter() { 106 | if rhs.members.contains(rhs_reg) { 107 | return true; 108 | } 109 | } 110 | } 111 | } 112 | 113 | false 114 | } 115 | } 116 | 117 | #[derive(Debug)] 118 | pub struct MergedRegister { 119 | pub members: BTreeSet, 120 | } 121 | 122 | #[derive(Debug)] 123 | pub struct RegisterSet(Vec); 124 | 125 | impl RegisterSet { 126 | pub fn new(func: &SsaFunction) -> Self { 127 | let all_vars = NoopLivenessInfo::analyze(func).vars; 128 | 129 | let live_info = FullLivenessInfo::analyze(func); 130 | 131 | //crate::ssa::liveness::print_liveness_info(&live_info, func); 132 | 133 | println!("Need to create {} registers in set", all_vars.len()); 134 | 135 | let regs = all_vars.into_iter().map(|var| { 136 | let members = Some(var).into_iter().collect(); 137 | MergedRegister { members } 138 | }).collect(); 139 | 140 | RegisterSet(regs) 141 | } 142 | 143 | pub fn len(&self) -> usize { 144 | self.0.len() 145 | } 146 | 147 | pub fn to_map(&self) -> HashMap { 148 | let mut result = HashMap::new(); 149 | for merged_reg in self.0.iter() { 150 | let rep = merged_reg.members.iter().next().unwrap(); 151 | for reg in merged_reg.members.iter().copied() { 152 | result.insert(reg.into_untyped(), rep.0); 153 | } 154 | } 155 | result 156 | } 157 | 158 | pub fn get(&self, elem: TypedSsaVar) -> &MergedRegister { 159 | self.0.iter().find(|reg| reg.members.contains(&elem)).unwrap() 160 | } 161 | 162 | pub fn merge(&mut self, var1: TypedSsaVar, var2: TypedSsaVar) { 163 | let r1 = self.get_idx(var1); 164 | let mut merged1 = self.0.remove(r1); 165 | 166 | let r2 = self.get_idx(var2); 167 | let merged2 = self.0.remove(r2); 168 | 169 | merged1.members.extend(merged2.members); 170 | 171 | self.0.push(merged1); 172 | } 173 | 174 | pub fn get_idx(&self, var1: TypedSsaVar) -> usize { 175 | self.0.iter().enumerate().find(|(_, reg)| reg.members.contains(&var1)).unwrap().0 176 | } 177 | 178 | pub fn get_idx2(&self, var1: SsaVar) -> usize { 179 | self.0.iter().enumerate().find(|(_, reg)| reg.members.iter().any(|r| r.0 == var1.0)).unwrap().0 180 | } 181 | 182 | pub fn get_rep(&self, var1: SsaVar) -> TypedSsaVar { 183 | let idx = self.get_idx2(var1); 184 | *self.0[idx].members.iter().next().unwrap() 185 | } 186 | } 187 | } 188 | 189 | use register_set::*; 190 | use wasmparser::ValType; 191 | 192 | fn try_merge(sets: &mut RegisterSet, dst: &TypedSsaVar, src: &TypedSsaVar, interf_graph: &Graph) { 193 | if sets.get_idx(*dst) == sets.get_idx(*src) { 194 | return; 195 | } 196 | 197 | let dst_set = sets.get(*dst); 198 | let src_set = sets.get(*src); 199 | 200 | for m1 in &dst_set.members { 201 | for m2 in &src_set.members { 202 | if interf_graph.interferes(m1.into_untyped(), m2.into_untyped()) { 203 | return; 204 | } 205 | } 206 | } 207 | 208 | sets.merge(*dst, *src); 209 | } 210 | 211 | impl FullRegAlloc { 212 | pub fn analyze(func: &SsaFunction, liveness: &FullLivenessInfo) -> Self { 213 | println!("Starting regalloc for {}", func.func_id()); 214 | 215 | let func_id = func.func_id(); 216 | 217 | let interf_graph = Graph::new(func, liveness); 218 | 219 | let mut next_temp = 0; 220 | 221 | let mut v_to_r = |v: TypedSsaVar| -> DynRegister { 222 | if next_temp <= v.0 { 223 | next_temp = v.0 + 1; 224 | } 225 | 226 | match v.1 { 227 | ValType::I32 => Register::work_lo(func_id, v.0).into(), 228 | ValType::I64 => DoubleRegister::Work(func_id, v.0).into(), 229 | _ => todo!(), 230 | } 231 | }; 232 | 233 | let mut colors = DenseMap::new(); 234 | for (_, block) in func.iter() { 235 | for arm in block.phi_node.arms() { 236 | let r = v_to_r(arm.dest()); 237 | colors.insert(arm.dest().into_untyped(), r); 238 | } 239 | for instr in block.body.iter() { 240 | for def in instr.defs() { 241 | let r = v_to_r(def); 242 | colors.insert(def.into_untyped(), r); 243 | } 244 | } 245 | } 246 | 247 | let mut coloring = Coloring { 248 | colors, 249 | precolors: HashMap::new(), 250 | func_id, 251 | next_temp, 252 | }; 253 | 254 | let graph = IFGraph(interf_graph); 255 | 256 | coalesce(func, &graph, &mut coloring); 257 | 258 | /*for (block_id, block) in func.iter() { 259 | for (instr_idx, instr) in block.body.iter().enumerate() { 260 | for (dst, src) in instr.coalescable_vars() { 261 | let uses = instr.uses(); 262 | let defs = instr.defs(); 263 | assert!(uses.contains(src)); 264 | assert!(defs.contains(dst)); 265 | 266 | try_merge(&mut sets, dst, src, &interf_graph); 267 | } 268 | } 269 | 270 | /*for (dst, src) in func.coalescable_term_vars(block_id) { 271 | try_merge(&mut sets, &dst, &src, &interf_graph); 272 | }*/ 273 | } 274 | 275 | println!("Coalesced into {} registers", sets.len());*/ 276 | 277 | 278 | FullRegAlloc { const_pool: HashSet::new(), map: coloring.colors, func: func.func_id(), temp: 1000 } 279 | } 280 | } 281 | 282 | impl RegAlloc for FullRegAlloc { 283 | fn get(&self, val: SsaVar) -> Register { 284 | let rep = *self.map.get(val).unwrap(); 285 | let DynRegister::Single(rep) = rep else { panic!() }; 286 | rep 287 | } 288 | 289 | fn get_double(&self, val: SsaVar) -> DoubleRegister { 290 | let rep = *self.map.get(val).unwrap(); 291 | let DynRegister::Double(rep) = rep else { panic!() }; 292 | rep 293 | } 294 | 295 | fn get_const(&mut self, val: i32) -> Register { 296 | self.const_pool.insert(val); 297 | Register::const_val(val) 298 | } 299 | 300 | fn get_double_const(&mut self, val: i64) -> DoubleRegister { 301 | self.const_pool.insert(val as i32); 302 | self.const_pool.insert((val >> 32) as i32); 303 | DoubleRegister::const_val(val) 304 | } 305 | 306 | fn get_temp(&mut self) -> Register { 307 | let reg = Register::temp_lo(self.temp); 308 | self.temp += 1; 309 | reg 310 | } 311 | 312 | fn get_temp_double(&mut self) -> DoubleRegister { 313 | let reg = DoubleRegister::temp(self.temp); 314 | self.temp += 1; 315 | reg 316 | } 317 | 318 | fn const_pool(&self) -> &HashSet { 319 | &self.const_pool 320 | } 321 | } 322 | 323 | #[cfg(test)] 324 | mod test { 325 | use wasmparser::ValType; 326 | 327 | use crate::ssa::{SsaBasicBlock, TypedSsaVar, SsaInstr, SsaTerminator, JumpTarget, BlockId, SsaFunction, liveness::FullLivenessInfo}; 328 | 329 | use super::{FullRegAlloc, RegAlloc}; 330 | 331 | /*#[test] 332 | fn reg_across_jump() { 333 | let r0 = TypedSsaVar(0, ValType::I32); 334 | let r1 = TypedSsaVar(1, ValType::I32); 335 | 336 | let b0 = SsaBasicBlock { 337 | // TODO: ??? 338 | params: Vec::new(), 339 | body: vec![ 340 | SsaInstr::I32Set(r0, 123), 341 | ], 342 | term: SsaTerminator::Jump(JumpTarget { 343 | label: BlockId { func: 0, block: 1 }, 344 | params: vec![r0], 345 | }) 346 | }; 347 | }*/ 348 | 349 | /*#[test] 350 | #[ignore] 351 | fn coalesce_jump_param() { 352 | let r0 = TypedSsaVar(0, ValType::I32); 353 | let r1 = TypedSsaVar(1, ValType::I32); 354 | 355 | let b0 = SsaBasicBlock { 356 | params: Vec::new(), 357 | body: vec![ 358 | SsaInstr::I32Set(r0, 123) 359 | ], 360 | term: SsaTerminator::Jump(JumpTarget { 361 | label: BlockId { func: 0, block: 1}, 362 | params: vec![r0] 363 | }) 364 | }; 365 | 366 | let b1 = SsaBasicBlock { 367 | params: vec![r1], 368 | body: Vec::new(), 369 | term: SsaTerminator::Return(vec![r1]), 370 | }; 371 | 372 | let func = SsaFunction::new( 373 | vec![ 374 | (BlockId { func: 0, block: 1 }, b1), 375 | (BlockId { func: 0, block: 0 }, b0), 376 | ], 377 | Box::new([]), 378 | Box::new([ValType::I32]), 379 | ); 380 | 381 | let lv = FullLivenessInfo::analyze(&func); 382 | 383 | let reg_alloc = FullRegAlloc::analyze(&func, &lv); 384 | 385 | println!("{:?}", reg_alloc.map); 386 | panic!(); 387 | 388 | /*let liveness = FullLivenessInfo::analyze(&func); 389 | 390 | let r0_range = liveness.live_range(r0); 391 | let r1_range = liveness.live_range(r1); 392 | 393 | print_liveness_info(&liveness, &func); 394 | println!(); 395 | 396 | println!("{:?}\n", r0_range); 397 | println!("{:?}\n", r1_range); 398 | println!("{:?}\n", overlap); 399 | panic!();*/ 400 | }*/ 401 | } -------------------------------------------------------------------------------- /src/wasm_file.rs: -------------------------------------------------------------------------------- 1 | use wasmparser::{Data, Element, Export, FuncType, Global, Import, MemoryType, Operator, Parser, Payload, TableType, BlockType, ExternalKind, GlobalType, ConstExpr, ValType, Type, TypeRef, TableInit}; 2 | 3 | use crate::ssa::interp::TypedValue; 4 | 5 | #[derive(Debug, Default)] 6 | pub struct DataList<'a> { 7 | pub data: Vec>, 8 | } 9 | 10 | impl<'a> DataList<'a> { 11 | pub fn new() -> Self { 12 | Self::default() 13 | } 14 | 15 | pub fn add_data(&mut self, d: Data<'a>) { 16 | self.data.push(d); 17 | } 18 | } 19 | 20 | #[derive(Debug, Default)] 21 | pub struct TableList { 22 | pub tables: Vec, 23 | } 24 | 25 | impl TableList { 26 | pub fn new() -> Self { 27 | Default::default() 28 | } 29 | 30 | pub fn add_table(&mut self, ty: TableType) { 31 | self.tables.push(ty); 32 | } 33 | } 34 | 35 | #[derive(Default)] 36 | pub struct ElementList<'a> { 37 | pub elements: Vec>, 38 | } 39 | 40 | impl<'a> ElementList<'a> { 41 | pub fn new() -> Self { 42 | Default::default() 43 | } 44 | 45 | pub fn add_element(&mut self, elem: Element<'a>) { 46 | self.elements.push(elem); 47 | } 48 | } 49 | 50 | #[derive(Debug, Default)] 51 | pub struct GlobalList<'a> { 52 | pub globals: Vec>, 53 | } 54 | 55 | impl<'a> GlobalList<'a> { 56 | pub fn new() -> Self { 57 | GlobalList::default() 58 | } 59 | 60 | pub fn add_global(&mut self, global: Global<'a>) { 61 | self.globals.push(global); 62 | } 63 | } 64 | 65 | #[derive(Debug, Clone, Copy)] 66 | pub struct FuncImport<'a> { 67 | pub module: &'a str, 68 | pub field: &'a str, 69 | pub ty: u32, 70 | } 71 | 72 | #[derive(Debug, Clone, Copy)] 73 | struct GlobalImport<'a> { 74 | pub module: &'a str, 75 | pub field: &'a str, 76 | pub content_type: ValType, 77 | pub mutable: bool, 78 | } 79 | 80 | #[derive(Debug, Default)] 81 | pub struct ImportList<'a> { 82 | func_imports: Vec>, 83 | global_imports: Vec>, 84 | } 85 | 86 | impl<'a> ImportList<'a> { 87 | pub fn new() -> Self { 88 | ImportList::default() 89 | } 90 | 91 | pub fn add_import(&mut self, i: Import<'a>) { 92 | match i.ty { 93 | TypeRef::Func(ty) => { 94 | self.func_imports.push(FuncImport { module: i.module, field: i.name, ty }) 95 | } 96 | TypeRef::Global(GlobalType { content_type, mutable }) => { 97 | self.global_imports.push(GlobalImport { module: i.module, field: i.name, content_type, mutable }) 98 | } 99 | _ => todo!("{:?}", i), 100 | } 101 | } 102 | } 103 | 104 | #[derive(Debug, Default)] 105 | pub struct ExportList<'a> { 106 | pub exports: Vec>, 107 | } 108 | 109 | impl<'a> ExportList<'a> { 110 | pub fn new() -> Self { 111 | Self::default() 112 | } 113 | 114 | pub fn add_export(&mut self, export: Export<'a>) { 115 | self.exports.push(export); 116 | } 117 | 118 | pub fn find_func(&self, name: &str) -> Option { 119 | for export in self.exports.iter() { 120 | if export.name == name { 121 | assert!(matches!(export.kind, ExternalKind::Func)); 122 | return Some(export.index as usize); 123 | } 124 | } 125 | 126 | None 127 | } 128 | } 129 | 130 | #[derive(Debug, Default)] 131 | pub struct MemoryList { 132 | pub memory: Vec, 133 | } 134 | 135 | impl MemoryList { 136 | pub fn new() -> Self { 137 | Default::default() 138 | } 139 | 140 | pub fn add_memory(&mut self, memory: MemoryType) { 141 | self.memory.push(memory); 142 | } 143 | } 144 | 145 | #[derive(Debug)] 146 | pub struct FunctionBody<'a> { 147 | pub operators: Vec>, 148 | pub locals: Vec<(u32, ValType)>, 149 | } 150 | 151 | #[derive(Debug, Default)] 152 | pub struct TypeList { 153 | pub types: Vec, 154 | } 155 | 156 | impl TypeList { 157 | pub fn new() -> Self { 158 | TypeList::default() 159 | } 160 | 161 | pub fn add_type(&mut self, ty: Type) { 162 | self.types.push(ty); 163 | } 164 | 165 | pub fn func_type(&self, idx: u32) -> &FuncType { 166 | let Type::Func(f) = &self.types[idx as usize]; 167 | 168 | f 169 | } 170 | 171 | pub fn start_types(&self, ty: BlockType) -> Box<[ValType]> { 172 | match ty { 173 | BlockType::Empty => Box::new([]), 174 | BlockType::Type(_) => Vec::new().into_boxed_slice(), 175 | BlockType::FuncType(i) => { 176 | let ty = &self.func_type(i); 177 | ty.params().to_owned().into_boxed_slice() 178 | } 179 | } 180 | } 181 | 182 | pub fn end_types(&self, ty: BlockType) -> Box<[ValType]> { 183 | match ty { 184 | BlockType::Empty => Box::new([]), 185 | BlockType::Type(t) => vec![t].into_boxed_slice(), 186 | BlockType::FuncType(i) => { 187 | let ty = &self.func_type(i); 188 | ty.results().to_owned().into_boxed_slice() 189 | } 190 | } 191 | } 192 | } 193 | 194 | 195 | #[derive(Debug, Clone, Default)] 196 | pub struct FunctionList { 197 | pub functions: Vec, 198 | } 199 | 200 | impl FunctionList { 201 | pub fn new() -> Self { 202 | FunctionList::default() 203 | } 204 | 205 | pub fn add_function(&mut self, function: u32) { 206 | self.functions.push(function) 207 | } 208 | } 209 | 210 | 211 | pub struct WasmFile<'a> { 212 | pub types: TypeList, 213 | pub globals: GlobalList<'a>, 214 | pub memory: MemoryList, 215 | pub exports: ExportList<'a>, 216 | pub imports: ImportList<'a>, 217 | pub data: DataList<'a>, 218 | pub tables: TableList, 219 | pub elements: ElementList<'a>, 220 | /// Includes imported functions 221 | pub functions: FunctionList, 222 | pub bodies: Vec>, 223 | } 224 | 225 | impl<'a> WasmFile<'a> { 226 | pub fn func_body(&self, func_idx: usize) -> &FunctionBody<'a> { 227 | let import_count = self.imports.func_imports.len(); 228 | if !self.func_is_defined(func_idx) { 229 | panic!("tried to get the body of an imported function ({})", func_idx) 230 | } else { 231 | &self.bodies[func_idx - import_count] 232 | } 233 | } 234 | 235 | pub fn func_is_defined(&self, func_idx: usize) -> bool { 236 | let import_count = self.imports.func_imports.len(); 237 | assert!(func_idx <= import_count + self.bodies.len()); 238 | func_idx >= import_count 239 | } 240 | 241 | pub fn defined_funcs(&self) -> impl Iterator { 242 | let import_count = self.imports.func_imports.len(); 243 | import_count..import_count + self.bodies.len() 244 | } 245 | 246 | pub fn func_type_idx(&self, func_idx: usize) -> u32 { 247 | self.functions.functions[func_idx] 248 | } 249 | 250 | pub fn func_type(&self, func_idx: usize) -> &FuncType { 251 | let type_idx = self.func_type_idx(func_idx); 252 | self.types.func_type(type_idx) 253 | } 254 | 255 | pub fn func_locals(&self, func_idx: usize) -> Vec { 256 | let mut result = Vec::new(); 257 | 258 | let func_ty = self.func_type(func_idx); 259 | result.extend(func_ty.params().iter().copied()); 260 | 261 | let body = self.func_body(func_idx); 262 | for &(count, local_ty) in body.locals.iter() { 263 | for _ in 0..count { 264 | result.push(local_ty); 265 | } 266 | } 267 | 268 | result 269 | } 270 | 271 | pub fn func_import(&self, func_idx: usize) -> FuncImport { 272 | self.imports.func_imports[func_idx] 273 | } 274 | 275 | pub fn find_func(&self, name: &str) -> Option { 276 | self.exports.find_func(name) 277 | } 278 | 279 | pub fn global(&self, index: u32) -> Global { 280 | if !self.imports.global_imports.is_empty() { 281 | todo!() 282 | } 283 | 284 | self.globals.globals[index as usize] 285 | } 286 | 287 | pub fn globals(&self) -> &[Global] { 288 | if !self.imports.global_imports.is_empty() { 289 | todo!() 290 | } 291 | 292 | &self.globals.globals 293 | } 294 | } 295 | 296 | impl<'a> From<&'a [u8]> for WasmFile<'a> { 297 | fn from(file: &'a [u8]) -> WasmFile<'a> { 298 | let mut types = TypeList::new(); 299 | let mut globals = GlobalList::new(); 300 | let mut memory = MemoryList::new(); 301 | let mut exports = ExportList::new(); 302 | let mut imports = ImportList::new(); 303 | let mut tables = TableList::new(); 304 | let mut data = DataList::new(); 305 | let mut elements = ElementList::new(); 306 | 307 | let mut func_reader = None; 308 | 309 | let mut codes = Vec::new(); 310 | 311 | for payload in Parser::new(0).parse_all(file) { 312 | let payload = payload.unwrap(); 313 | match payload { 314 | Payload::Version { .. } => {} 315 | Payload::ImportSection(i) => { 316 | for import in i { 317 | let import = import.unwrap(); 318 | imports.add_import(import); 319 | } 320 | } 321 | Payload::DataSection(d) => { 322 | for d in d { 323 | let d = d.unwrap(); 324 | data.add_data(d); 325 | } 326 | } 327 | Payload::ExportSection(e) => { 328 | for export in e { 329 | let export = export.unwrap(); 330 | exports.add_export(export); 331 | } 332 | } 333 | Payload::TypeSection(t) => { 334 | for ty in t { 335 | let ty = ty.unwrap(); 336 | 337 | types.add_type(ty); 338 | } 339 | } 340 | Payload::GlobalSection(g) => { 341 | for global in g { 342 | let global = global.unwrap(); 343 | 344 | globals.add_global(global); 345 | } 346 | } 347 | Payload::MemorySection(m) => { 348 | for mem in m { 349 | let mem = mem.unwrap(); 350 | 351 | memory.add_memory(mem); 352 | } 353 | } 354 | Payload::FunctionSection(f) => { 355 | assert!(func_reader.is_none()); 356 | func_reader = Some(f); 357 | } 358 | Payload::CodeSectionStart { .. } => {} 359 | Payload::CodeSectionEntry(e) => { 360 | let operators = e.get_operators_reader().unwrap() 361 | .into_iter() 362 | .map(|o| o.unwrap()) 363 | .collect::>(); 364 | let locals = e.get_locals_reader().unwrap() 365 | .into_iter() 366 | .map(|l| l.unwrap()) 367 | .collect::>(); 368 | 369 | codes.push(FunctionBody { operators, locals }); 370 | } 371 | Payload::TableSection(t) => { 372 | for table in t { 373 | let table = table.unwrap(); 374 | tables.add_table(table.ty); 375 | if !matches!(table.init, TableInit::RefNull) { 376 | todo!("{:?}", table.init); 377 | } 378 | } 379 | } 380 | Payload::ElementSection(e) => { 381 | for elem in e { 382 | let elem = elem.unwrap(); 383 | elements.add_element(elem); 384 | } 385 | } 386 | Payload::End(_) => {} 387 | _other => { 388 | println!("TODO: Unknown section {:?}", _other); 389 | } 390 | } 391 | } 392 | 393 | let mut functions = FunctionList::new(); 394 | 395 | for f in imports.func_imports.iter() { 396 | functions.add_function(f.ty); 397 | } 398 | 399 | if let Some(func_reader) = func_reader { 400 | for func in func_reader { 401 | let func = func.unwrap(); 402 | 403 | functions.add_function(func); 404 | } 405 | } 406 | 407 | println!("{:?}", exports); 408 | 409 | WasmFile { functions, memory, globals, exports, imports, types, tables, data, elements, bodies: codes } 410 | } 411 | } 412 | 413 | pub fn eval_const_expr(init_expr: &ConstExpr) -> Vec { 414 | let ops = init_expr.get_operators_reader().into_iter().map(|o| o.unwrap()).collect::>(); 415 | 416 | match &ops[..] { 417 | &[Operator::I32Const { value }, Operator::End] => { 418 | vec![TypedValue::I32(value)] 419 | } 420 | ops => todo!("{:?}", ops) 421 | } 422 | } 423 | 424 | pub fn eval_const_expr_single(init_expr: &ConstExpr) -> TypedValue { 425 | let result = eval_const_expr(init_expr); 426 | assert_eq!(result.len(), 1); 427 | result.into_iter().next().unwrap() 428 | } 429 | 430 | -------------------------------------------------------------------------------- /tests/server_test.rs: -------------------------------------------------------------------------------- 1 | #[path="sexpr.rs"] 2 | mod sexpr; 3 | 4 | use rcon::{Connection, AsyncStdStream}; 5 | use wasmcraft::{ssa::{interp::TypedValue, lir_emitter, BlockId}, lir::Register, wasm_file::WasmFile, validator::wasm_to_ssa, pack_emitter::{self, get_mc_id}, CompileContext}; 6 | use wasmparser::ValType; 7 | 8 | type Server = Connection; 9 | 10 | async fn connect_to_server() -> Server { 11 | let address = "localhost:25575"; 12 | >::builder() 13 | .enable_minecraft_quirks(true) 14 | .connect(address, "test") 15 | .await.unwrap() 16 | 17 | //let resp = conn.cmd("scoreboard players set %param%0%lo reg 123").await.unwrap(); 18 | //println!("resp: {}", resp); 19 | } 20 | 21 | async fn scoreboard_set_reg(server: &mut Server, reg: Register, val: i32) { 22 | let pair = reg.to_string(); 23 | let (holder, obj) = pair.split_once(' ').unwrap(); 24 | scoreboard_set(server, holder, obj, val).await; 25 | } 26 | 27 | async fn scoreboard_set(server: &mut Server, holder: &str, obj: &str, val: i32) { 28 | let cmd = format!("scoreboard players set {holder} {obj} {val}"); 29 | let resp = server.cmd(&cmd).await.unwrap(); 30 | let ex = format!("Set [{obj}] for {holder} to {val}"); 31 | assert_eq!(resp, ex); 32 | } 33 | 34 | async fn scoreboard_get_reg(server: &mut Server, reg: Register) -> i32 { 35 | let pair = reg.to_string(); 36 | let (holder, obj) = pair.split_once(' ').unwrap(); 37 | scoreboard_get(server, holder, obj).await 38 | } 39 | 40 | async fn scoreboard_get(server: &mut Server, holder: &str, obj: &str) -> i32 { 41 | let cmd = format!("scoreboard players get {holder} {obj}"); 42 | let resp = server.cmd(&cmd).await.unwrap(); 43 | 44 | let ex_prefix = format!("{holder} has "); 45 | let ex_suffix = format!(" [{obj}]"); 46 | 47 | let resp = resp.strip_prefix(&ex_prefix).unwrap().strip_suffix(&ex_suffix).unwrap(); 48 | resp.parse().unwrap() 49 | } 50 | 51 | async fn set_params(server: &mut Server, func_params: &[TypedValue]) { 52 | for (param_idx, param) in func_params.iter().enumerate() { 53 | match *param { 54 | TypedValue::I32(v) => { 55 | let reg = Register::param_lo(param_idx as u32); 56 | scoreboard_set_reg(server, reg, v).await; 57 | } 58 | TypedValue::I64(v) => { 59 | let v_lo = v as i32; 60 | let v_hi = (v >> 32) as i32; 61 | 62 | let param_pair_lo = Register::param_lo(param_idx as u32); 63 | scoreboard_set_reg(server, param_pair_lo, v_lo).await; 64 | 65 | let param_pair_hi = Register::param_hi(param_idx as u32); 66 | scoreboard_set_reg(server, param_pair_hi, v_hi).await; 67 | } 68 | } 69 | } 70 | } 71 | 72 | async fn get_returns(server: &mut Server, return_tys: &[ValType]) -> Vec { 73 | let mut result = Vec::new(); 74 | 75 | for (idx, ty) in return_tys.iter().enumerate() { 76 | match ty { 77 | ValType::I32 => { 78 | let param_pair = Register::return_lo(idx as u32); 79 | let val = scoreboard_get_reg(server, param_pair).await; 80 | 81 | result.push(TypedValue::I32(val)); 82 | } 83 | ValType::I64 => { 84 | let param_pair_lo = Register::return_lo(idx as u32); 85 | let v_lo = scoreboard_get_reg(server, param_pair_lo).await; 86 | 87 | let param_pair_hi = Register::return_hi(idx as u32); 88 | let v_hi = scoreboard_get_reg(server, param_pair_hi).await; 89 | 90 | let v = (v_lo as u32 as i64) | ((v_hi as i64) << 32); 91 | result.push(TypedValue::I64(v)); 92 | } 93 | _ => todo!(), 94 | } 95 | } 96 | 97 | result 98 | } 99 | 100 | pub fn load_state(wasm_data: &[u8]) -> TestState { 101 | async_std::task::block_on(load_state_async(wasm_data)) 102 | } 103 | 104 | async fn load_state_async(wasm_data: &[u8]) -> TestState { 105 | let ctx = CompileContext::new_from_opt(1); 106 | 107 | let wasm_file = ctx.compute_wasm_file(wasm_data); 108 | let program = ctx.compute_ssa(&wasm_file); 109 | let program = ctx.compute_lir(program); 110 | let program = ctx.compute_datapack(&program); 111 | 112 | pack_emitter::persist_program(std::path::Path::new("./tests/server_test/world/datapacks/out"), &program); 113 | 114 | let mut server = connect_to_server().await; 115 | 116 | let resp = server.cmd("reload").await.unwrap(); 117 | println!("{:?}", resp); 118 | let resp = server.cmd("gamerule maxCommandChainLength 10000000").await.unwrap(); 119 | println!("{:?}", resp); 120 | let resp = server.cmd("function wasmrunner:init").await.unwrap(); 121 | println!("{:?}", resp); 122 | 123 | TestState { server, wasm_file } 124 | 125 | } 126 | 127 | use sexpr::SExpr; 128 | 129 | pub struct TestState<'a> { 130 | wasm_file: WasmFile<'a>, 131 | server: Server, 132 | } 133 | 134 | async fn eval(expr: &SExpr, test_state: Option<&mut TestState<'_>>) -> Vec { 135 | match expr { 136 | SExpr::Node { name, params } if name == "invoke" => { 137 | let test_state = test_state.unwrap(); 138 | 139 | let func_name = ¶ms[0]; 140 | let func_params2 = ¶ms[1..]; 141 | 142 | let func_name = if let SExpr::String(s) = func_name { 143 | &**s 144 | } else { 145 | panic!() 146 | }; 147 | 148 | let mut func_params = Vec::new(); 149 | for p in func_params2.iter() { 150 | func_params.push(eval_single(p, Some(test_state)).await); 151 | } 152 | 153 | set_params(&mut test_state.server, &func_params).await; 154 | 155 | let func_idx = test_state.wasm_file.find_func(func_name).unwrap(); 156 | let return_tys = &test_state.wasm_file.func_type(func_idx).results(); 157 | let func_name = get_mc_id(BlockId { func: func_idx, block: 0 }); 158 | 159 | println!("Calling func {func_name} with params {:?}", func_params); 160 | 161 | let resp = test_state.server.cmd(&format!("function {func_name}")).await.unwrap(); 162 | println!("TODO: {resp}"); 163 | 164 | get_returns(&mut test_state.server, return_tys).await 165 | } 166 | SExpr::Node { name, params } if name == "i32.const" => { 167 | if let [SExpr::Int(i)] = ¶ms[..] { 168 | vec![TypedValue::I32((*i) as i32)] 169 | } else { 170 | panic!() 171 | } 172 | } 173 | SExpr::Node { name, params } if name == "i64.const" => { 174 | if let [SExpr::Int(i)] = ¶ms[..] { 175 | vec![TypedValue::I64(*i)] 176 | } else { 177 | panic!() 178 | } 179 | } 180 | _ => todo!("{:?}", expr) 181 | } 182 | } 183 | 184 | /*async fn eval_single<'a>(expr: &'a SExpr, test_state: Option<&'a mut TestState<'a>>) -> TypedValue { 185 | let values = eval(expr, test_state).await; 186 | 187 | assert_eq!(values.len(), 1); 188 | 189 | values.into_iter().next().unwrap() 190 | }*/ 191 | 192 | async fn eval_single(expr: &SExpr, _test_state: Option<&mut TestState<'_>>) -> TypedValue { 193 | match expr { 194 | SExpr::Node { name, params } if name == "i32.const" => { 195 | if let [SExpr::Int(i)] = ¶ms[..] { 196 | TypedValue::I32((*i) as i32) 197 | } else { 198 | panic!() 199 | } 200 | } 201 | SExpr::Node { name, params } if name == "i64.const" => { 202 | if let [SExpr::Int(i)] = ¶ms[..] { 203 | TypedValue::I64(*i) 204 | } else { 205 | panic!() 206 | } 207 | } 208 | _ => todo!("{:?}", expr) 209 | } 210 | } 211 | 212 | impl<'a> TestState<'a> { 213 | async fn eval(&mut self, expr: &SExpr) -> Vec { 214 | eval(expr, Some(self)).await 215 | } 216 | 217 | async fn eval_single(&mut self, expr: &SExpr) -> TypedValue { 218 | eval_single(expr, Some(self)).await 219 | } 220 | 221 | pub fn run_check(&mut self, arg: &str) { 222 | async_std::task::block_on(self.run_check_async(arg)) 223 | } 224 | 225 | async fn run_check_async(&mut self, arg: &str) { 226 | println!("\n{}", arg); 227 | 228 | let arg: SExpr = arg.parse().unwrap(); 229 | 230 | match arg { 231 | SExpr::AssertReturn(expr, expected) => { 232 | let actual = self.eval(&expr).await; 233 | 234 | let mut expected2 = Vec::new(); 235 | for s in expected.into_iter() { 236 | expected2.push(self.eval_single(&s).await); 237 | } 238 | 239 | assert_eq!(actual, expected2); 240 | } 241 | SExpr::AssertTrap(_, _) => { 242 | println!("Ignoring AssertTrap"); 243 | } 244 | SExpr::Node { name, .. } if name == "assert_invalid" => { 245 | println!("Ignoring AssertInvalid"); 246 | } 247 | SExpr::Node { name, .. } if name == "assert_malformed" => { 248 | println!("Ignoring AssertMalformed"); 249 | } 250 | SExpr::Node { name, .. } if name == "assert_exhaustion" => { 251 | println!("Ignoring AssertExhaustion"); 252 | } 253 | _ => todo!("{:?}", arg), 254 | } 255 | } 256 | } -------------------------------------------------------------------------------- /tests/sexpr.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug, Clone, PartialEq, Eq)] 2 | enum SExprToken { 3 | StrLit(String), 4 | IntLit(i64), 5 | Ident(String), 6 | OpenParen, 7 | CloseParen, 8 | } 9 | 10 | struct SExprLexer<'a> { 11 | s: &'a str 12 | } 13 | 14 | impl Iterator for SExprLexer<'_> { 15 | type Item = SExprToken; 16 | 17 | fn next(&mut self) -> Option { 18 | while self.s.chars().next().unwrap().is_whitespace() { 19 | let mut indices = self.s.char_indices(); 20 | indices.next(); 21 | self.s = &self.s[indices.next().unwrap().0..]; 22 | } 23 | 24 | if self.s.is_empty() { 25 | None 26 | } else if self.s.starts_with('(') { 27 | self.s = &self.s[1..]; 28 | Some(SExprToken::OpenParen) 29 | } else if self.s.starts_with(')') { 30 | self.s = &self.s[1..]; 31 | Some(SExprToken::CloseParen) 32 | } else if self.s.starts_with('"') { 33 | self.s = &self.s[1..]; 34 | let mut end_idx = None; 35 | for (idx, c) in self.s.char_indices() { 36 | if c == '\\' { 37 | todo!() 38 | } else if c == '"' { 39 | end_idx = Some(idx); 40 | break 41 | } 42 | } 43 | let end_idx = end_idx.unwrap(); 44 | let name = self.s[..end_idx].to_string(); 45 | self.s = &self.s[end_idx+1..]; 46 | Some(SExprToken::StrLit(name)) 47 | } else if self.s.starts_with('-') || self.s.chars().next().unwrap().is_digit(10) { 48 | let is_neg = if self.s.starts_with('-') { 49 | self.s = &self.s[1..]; 50 | true 51 | } else { 52 | false 53 | }; 54 | 55 | let radix = if self.s.starts_with("0x") || self.s.starts_with("0X") { 56 | self.s = &self.s[2..]; 57 | 16 58 | } else if self.s.starts_with("0b") || self.s.starts_with("0B") { 59 | self.s = &self.s[2..]; 60 | 2 61 | // TODO: ??? 62 | /* 63 | } else if self.s.starts_with('0') { 64 | todo!("octal?") 65 | */ 66 | } else { 67 | 10 68 | }; 69 | 70 | let mut s = String::new(); 71 | 72 | loop { 73 | let c = self.s.chars().next().unwrap(); 74 | if c.is_digit(radix) { 75 | s.push(c); 76 | } else if c == '_' { 77 | // Ignore 78 | } else { 79 | break; 80 | } 81 | 82 | self.s = &self.s[1..]; 83 | }; 84 | 85 | let mut value = u64::from_str_radix(&s, radix).unwrap_or_else(|e| panic!("Failed to parse {} because {}", &s, e)) as i64; 86 | if is_neg { 87 | value = value.wrapping_mul(-1); 88 | } 89 | 90 | Some(SExprToken::IntLit(value)) 91 | } else { 92 | let end_idx = self.s.find(|c: char| c.is_whitespace() || c == '(' || c == ')').unwrap(); 93 | let name = self.s[..end_idx].to_string(); 94 | self.s = &self.s[end_idx..]; 95 | Some(SExprToken::Ident(name)) 96 | } 97 | } 98 | } 99 | 100 | struct SExprParser<'a> { 101 | s: std::iter::Peekable> 102 | } 103 | 104 | impl<'a> SExprParser<'a> { 105 | pub fn new(s: &'a str) -> Self { 106 | SExprParser { s: SExprLexer { s }.peekable() } 107 | } 108 | 109 | pub fn parse(&mut self) -> Result { 110 | if self.s.peek() == Some(&SExprToken::OpenParen) { 111 | let (name, params) = self.parse_parenthesized()?; 112 | if name == "assert_return" { 113 | let mut params = params.into_iter(); 114 | let lhs = params.next().unwrap(); 115 | let rhs = params.collect(); 116 | Ok(SExpr::AssertReturn(Box::new(lhs), rhs)) 117 | } else if name == "assert_trap" { 118 | let mut params = params.into_iter(); 119 | let lhs = params.next().unwrap(); 120 | let rhs = params.next().unwrap(); 121 | assert!(params.next().is_none()); 122 | let rhs = if let SExpr::String(s) = rhs { 123 | s 124 | } else { 125 | panic!() 126 | }; 127 | 128 | Ok(SExpr::AssertTrap(Box::new(lhs), rhs)) 129 | } else { 130 | Ok(SExpr::Node { name, params }) 131 | } 132 | } else { 133 | let tok = self.s.next().unwrap(); 134 | match tok { 135 | SExprToken::IntLit(i) => Ok(SExpr::Int(i)), 136 | SExprToken::StrLit(s) => Ok(SExpr::String(s)), 137 | SExprToken::Ident(i) => Ok(SExpr::Ident(i)), 138 | _ => todo!("{:?}", tok) 139 | } 140 | } 141 | } 142 | 143 | pub fn parse_parenthesized(&mut self) -> Result<(String, Vec), String> { 144 | if self.s.next() != Some(SExprToken::OpenParen) { 145 | return Err("expected opening parenthesis".to_string()) 146 | } 147 | 148 | let name = if let Some(SExprToken::Ident(i)) = self.s.next() { 149 | i 150 | } else { 151 | return Err("expected ident".to_string()) 152 | }; 153 | 154 | let mut params = Vec::new(); 155 | while self.s.peek() != Some(&SExprToken::CloseParen) { 156 | params.push(self.parse()?); 157 | } 158 | 159 | if self.s.next() != Some(SExprToken::CloseParen) { 160 | return Err("expecting closing paren".to_string()); 161 | } 162 | 163 | Ok((name, params)) 164 | } 165 | } 166 | 167 | #[derive(Debug)] 168 | pub enum SExpr { 169 | Node { 170 | name: String, 171 | params: Vec, 172 | }, 173 | AssertReturn(Box, Vec), 174 | AssertTrap(Box, String), 175 | String(String), 176 | Ident(String), 177 | Int(i64), 178 | } 179 | 180 | impl std::str::FromStr for SExpr { 181 | type Err = String; 182 | 183 | fn from_str(s: &str) -> Result { 184 | SExprParser::new(s).parse() 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /tests/test_common.rs: -------------------------------------------------------------------------------- 1 | #[path="sexpr.rs"] 2 | mod sexpr; 3 | 4 | /* 5 | pub mod wasm_suite_prelude { 6 | use wasmcraft::{wasm_file::WasmFile, ssa::{interp::{SsaInterpreter, TypedValue}, lir_emitter}, validator::wasm_to_ssa, lir::interp::LirInterpreter}; 7 | 8 | use super::sexpr::SExpr; 9 | 10 | pub struct TestState<'a> { 11 | wasm_file: WasmFile<'a>, 12 | 13 | interp: SsaInterpreter 14 | } 15 | 16 | pub fn eval(expr: &SExpr, test_state: Option<&mut TestState>) -> Vec { 17 | match expr { 18 | SExpr::Node { name, params } if name == "invoke" => { 19 | let test_state = test_state.unwrap(); 20 | 21 | let func_name = ¶ms[0]; 22 | let func_params = ¶ms[1..]; 23 | 24 | let func_name = if let SExpr::String(s) = func_name { 25 | &**s 26 | } else { 27 | panic!() 28 | }; 29 | 30 | let func_params = func_params.iter().map(|p| eval_single(p, Some(test_state))).collect::>(); 31 | 32 | let func_idx = test_state.wasm_file.find_func(func_name).unwrap(); 33 | 34 | test_state.interp.call(func_idx, func_params); 35 | 36 | test_state.interp.run_until_halted() 37 | } 38 | SExpr::Node { name, params } if name == "i32.const" => { 39 | if let [SExpr::Int(i)] = ¶ms[..] { 40 | vec![TypedValue::I32((*i) as i32)] 41 | } else { 42 | panic!() 43 | } 44 | } 45 | SExpr::Node { name, params } if name == "i64.const" => { 46 | if let [SExpr::Int(i)] = ¶ms[..] { 47 | vec![TypedValue::I64(*i)] 48 | } else { 49 | panic!() 50 | } 51 | } 52 | _ => todo!("{:?}", expr) 53 | } 54 | } 55 | 56 | pub fn eval_single(expr: &SExpr, test_state: Option<&mut TestState>) -> TypedValue { 57 | let values = eval(expr, test_state); 58 | 59 | assert_eq!(values.len(), 1); 60 | 61 | values.into_iter().next().unwrap() 62 | } 63 | 64 | impl<'a> TestState<'a> { 65 | pub fn eval(&mut self, expr: &SExpr) -> Vec { 66 | eval(expr, Some(self)) 67 | } 68 | 69 | pub fn eval_single(&mut self, expr: &SExpr) -> TypedValue { 70 | eval_single(expr, Some(self)) 71 | } 72 | 73 | pub fn run_check(&mut self, arg: &str) { 74 | println!("\n{}", arg); 75 | 76 | let arg: SExpr = arg.parse().unwrap(); 77 | 78 | match arg { 79 | SExpr::AssertReturn(expr, expected) => { 80 | let actual = self.eval(&expr); 81 | 82 | let expected = expected.into_iter().map(|s| self.eval_single(&s)).collect::>(); 83 | 84 | assert_eq!(actual, expected); 85 | } 86 | SExpr::AssertTrap(_, _) => { 87 | println!("Ignoring AssertTrap"); 88 | } 89 | SExpr::Node { name, .. } if name == "assert_invalid" => { 90 | println!("Ignoring AssertInvalid"); 91 | } 92 | SExpr::Node { name, .. } if name == "assert_malformed" => { 93 | println!("Ignoring AssertMalformed"); 94 | } 95 | SExpr::Node { name, .. } if name == "assert_exhaustion" => { 96 | println!("Ignoring AssertExhaustion"); 97 | } 98 | _ => todo!("{:?}", arg), 99 | } 100 | } 101 | } 102 | 103 | pub fn load_state(wasm_data: &[u8]) -> TestState { 104 | let wasm_file = WasmFile::from(wasm_data); 105 | 106 | let program = wasm_to_ssa(&wasm_file); 107 | 108 | let interp = SsaInterpreter::new(program); 109 | 110 | //let program = lir_emitter::convert(program); 111 | 112 | TestState { wasm_file, interp } 113 | } 114 | 115 | } 116 | */ 117 | 118 | /* 119 | pub mod wasm_suite_prelude { 120 | use wasmcraft::{wasm_file::WasmFile, ssa::{interp::{SsaInterpreter, TypedValue}, lir_emitter}, validator::wasm_to_ssa, lir::interp::LirInterpreter}; 121 | 122 | use super::sexpr::SExpr; 123 | 124 | pub struct TestState<'a> { 125 | wasm_file: WasmFile<'a>, 126 | 127 | interp: LirInterpreter 128 | } 129 | 130 | pub fn eval(expr: &SExpr, test_state: Option<&mut TestState>) -> Vec { 131 | match expr { 132 | SExpr::Node { name, params } if name == "invoke" => { 133 | let test_state = test_state.unwrap(); 134 | 135 | let func_name = ¶ms[0]; 136 | let func_params = ¶ms[1..]; 137 | 138 | let func_name = if let SExpr::String(s) = func_name { 139 | &**s 140 | } else { 141 | panic!() 142 | }; 143 | 144 | let func_params = func_params.iter().map(|p| eval_single(p, Some(test_state))).collect::>(); 145 | 146 | let func_idx = test_state.wasm_file.find_func(func_name).unwrap(); 147 | 148 | test_state.interp.call(func_idx, &func_params); 149 | 150 | test_state.interp.run_until_halted() 151 | } 152 | SExpr::Node { name, params } if name == "i32.const" => { 153 | if let [SExpr::Int(i)] = ¶ms[..] { 154 | vec![TypedValue::I32((*i) as i32)] 155 | } else { 156 | panic!() 157 | } 158 | } 159 | SExpr::Node { name, params } if name == "i64.const" => { 160 | if let [SExpr::Int(i)] = ¶ms[..] { 161 | vec![TypedValue::I64(*i)] 162 | } else { 163 | panic!() 164 | } 165 | } 166 | _ => todo!("{:?}", expr) 167 | } 168 | } 169 | 170 | pub fn eval_single(expr: &SExpr, test_state: Option<&mut TestState>) -> TypedValue { 171 | let values = eval(expr, test_state); 172 | 173 | assert_eq!(values.len(), 1); 174 | 175 | values.into_iter().next().unwrap() 176 | } 177 | 178 | impl<'a> TestState<'a> { 179 | pub fn eval(&mut self, expr: &SExpr) -> Vec { 180 | eval(expr, Some(self)) 181 | } 182 | 183 | pub fn eval_single(&mut self, expr: &SExpr) -> TypedValue { 184 | eval_single(expr, Some(self)) 185 | } 186 | 187 | pub fn run_check(&mut self, arg: &str) { 188 | println!("\n{}", arg); 189 | 190 | let arg: SExpr = arg.parse().unwrap(); 191 | 192 | match arg { 193 | SExpr::AssertReturn(expr, expected) => { 194 | let actual = self.eval(&expr); 195 | 196 | let expected = expected.into_iter().map(|s| self.eval_single(&s)).collect::>(); 197 | 198 | assert_eq!(actual, expected); 199 | } 200 | SExpr::AssertTrap(_, _) => { 201 | println!("Ignoring AssertTrap"); 202 | } 203 | SExpr::Node { name, .. } if name == "assert_invalid" => { 204 | println!("Ignoring AssertInvalid"); 205 | } 206 | SExpr::Node { name, .. } if name == "assert_malformed" => { 207 | println!("Ignoring AssertMalformed"); 208 | } 209 | SExpr::Node { name, .. } if name == "assert_exhaustion" => { 210 | println!("Ignoring AssertExhaustion"); 211 | } 212 | _ => todo!("{:?}", arg), 213 | } 214 | } 215 | } 216 | 217 | pub fn load_state(wasm_data: &[u8]) -> TestState { 218 | let wasm_file = WasmFile::from(wasm_data); 219 | 220 | let program = wasm_to_ssa(&wasm_file); 221 | 222 | let program = lir_emitter::convert(program); 223 | 224 | let interp = LirInterpreter::new(program); 225 | 226 | //let program = lir_emitter::convert(program); 227 | 228 | TestState { wasm_file, interp } 229 | } 230 | } 231 | */ 232 | 233 | pub mod wasm_suite_prelude { 234 | use command_parser::CommandParse; 235 | use datapack_common::functions::command_components::{FunctionIdent, ScoreHolder, Objective}; 236 | use datapack_vm::Interpreter; 237 | use wasmcraft::{wasm_file::WasmFile, ssa::{interp::{SsaInterpreter, TypedValue}, lir_emitter, BlockId}, validator::wasm_to_ssa, lir::{interp::LirInterpreter, Register}, pack_emitter::{self, get_mc_id}, CompileContext}; 238 | use wasmparser::ValType; 239 | 240 | use super::sexpr::SExpr; 241 | 242 | pub struct TestState<'a> { 243 | wasm_file: WasmFile<'a>, 244 | 245 | interp: Interpreter, 246 | } 247 | 248 | pub fn set_params(interp: &mut Interpreter, func_params: &[TypedValue]) { 249 | for (param_idx, param) in func_params.iter().enumerate() { 250 | match *param { 251 | TypedValue::I32(v) => { 252 | let (holder, obj) = Register::param_lo(param_idx as u32).scoreboard_pair(); 253 | interp.set_named_score(&holder, &obj, v); 254 | } 255 | TypedValue::I64(v) => { 256 | let v_lo = v as i32; 257 | let v_hi = (v >> 32) as i32; 258 | 259 | let (holder_lo, obj_lo) = Register::param_lo(param_idx as u32).scoreboard_pair(); 260 | interp.set_named_score(&holder_lo, &obj_lo, v_lo); 261 | 262 | let (holder_hi, obj_hi) = Register::param_hi(param_idx as u32).scoreboard_pair(); 263 | interp.set_named_score(&holder_hi, &obj_hi, v_hi); 264 | } 265 | } 266 | } 267 | } 268 | 269 | pub fn get_returns(interp: &mut Interpreter, return_tys: &[ValType]) -> Vec { 270 | return_tys.iter().enumerate().map(|(idx, ty)| { 271 | match ty { 272 | ValType::I32 => { 273 | let (holder, obj) = Register::return_lo(idx as u32).scoreboard_pair(); 274 | let v = interp.get_named_score(&holder, &obj).unwrap(); 275 | TypedValue::I32(v) 276 | } 277 | ValType::I64 => { 278 | let (holder_lo, obj_lo) = Register::return_lo(idx as u32).scoreboard_pair(); 279 | let v_lo = interp.get_named_score(&holder_lo, &obj_lo).unwrap(); 280 | 281 | let (holder_hi, obj_hi) = Register::return_hi(idx as u32).scoreboard_pair(); 282 | let v_hi = interp.get_named_score(&holder_hi, &obj_hi).unwrap(); 283 | 284 | let v = (v_lo as u32 as i64) | ((v_hi as i64) << 32); 285 | TypedValue::I64(v) 286 | } 287 | _ => todo!(), 288 | } 289 | }).collect() 290 | } 291 | 292 | pub fn eval(expr: &SExpr, test_state: Option<&mut TestState>) -> Vec { 293 | match expr { 294 | SExpr::Node { name, params } if name == "invoke" => { 295 | let test_state = test_state.unwrap(); 296 | 297 | let func_name = ¶ms[0]; 298 | let func_params = ¶ms[1..]; 299 | 300 | let func_name = if let SExpr::String(s) = func_name { 301 | &**s 302 | } else { 303 | panic!() 304 | }; 305 | 306 | let func_params = func_params.iter().map(|p| eval_single(p, Some(test_state))).collect::>(); 307 | 308 | set_params(&mut test_state.interp, &func_params); 309 | 310 | let func_idx = test_state.wasm_file.find_func(func_name).unwrap_or_else(|| panic!("couldn't find {:?}", func_name)); 311 | let return_tys = &test_state.wasm_file.func_type(func_idx).results(); 312 | 313 | let mc_func_name = format!("wasmrunner:{func_name}"); 314 | let (_, mc_func_name) = FunctionIdent::parse_from_command(&mc_func_name).unwrap(); 315 | 316 | println!("Calling func {mc_func_name} ({func_idx}) with params {:?}", func_params); 317 | 318 | let interp_idx = test_state.interp.get_func_idx(&mc_func_name); 319 | test_state.interp.set_pos(interp_idx); 320 | 321 | test_state.interp.run_to_end().unwrap(); 322 | 323 | get_returns(&mut test_state.interp, return_tys) 324 | } 325 | SExpr::Node { name, params } if name == "i32.const" => { 326 | if let [SExpr::Int(i)] = ¶ms[..] { 327 | vec![TypedValue::I32((*i) as i32)] 328 | } else { 329 | panic!() 330 | } 331 | } 332 | SExpr::Node { name, params } if name == "i64.const" => { 333 | if let [SExpr::Int(i)] = ¶ms[..] { 334 | vec![TypedValue::I64(*i)] 335 | } else { 336 | panic!() 337 | } 338 | } 339 | _ => todo!("{:?}", expr) 340 | } 341 | } 342 | 343 | pub fn eval_single(expr: &SExpr, test_state: Option<&mut TestState>) -> TypedValue { 344 | let values = eval(expr, test_state); 345 | 346 | assert_eq!(values.len(), 1); 347 | 348 | values.into_iter().next().unwrap() 349 | } 350 | 351 | impl<'a> TestState<'a> { 352 | pub fn eval(&mut self, expr: &SExpr) -> Vec { 353 | eval(expr, Some(self)) 354 | } 355 | 356 | pub fn eval_single(&mut self, expr: &SExpr) -> TypedValue { 357 | eval_single(expr, Some(self)) 358 | } 359 | 360 | pub fn run_check(&mut self, arg: &str) { 361 | println!("\n{}", arg); 362 | 363 | let arg: SExpr = arg.parse().unwrap(); 364 | 365 | match arg { 366 | SExpr::AssertReturn(expr, expected) => { 367 | let actual = self.eval(&expr); 368 | 369 | let expected = expected.into_iter().map(|s| self.eval_single(&s)).collect::>(); 370 | 371 | assert_eq!(actual, expected); 372 | } 373 | SExpr::AssertTrap(_, _) => { 374 | println!("Ignoring AssertTrap"); 375 | } 376 | SExpr::Node { name, .. } if name == "assert_invalid" => { 377 | println!("Ignoring AssertInvalid"); 378 | } 379 | SExpr::Node { name, .. } if name == "assert_malformed" => { 380 | println!("Ignoring AssertMalformed"); 381 | } 382 | SExpr::Node { name, .. } if name == "assert_exhaustion" => { 383 | println!("Ignoring AssertExhaustion"); 384 | } 385 | _ => todo!("{:?}", arg), 386 | } 387 | } 388 | } 389 | 390 | pub fn load_state(wasm_data: &[u8]) -> TestState { 391 | let ctx = CompileContext::new_from_opt(1); 392 | 393 | let wasm_file = ctx.compute_wasm_file(wasm_data); 394 | let program = ctx.compute_ssa(&wasm_file); 395 | let program = ctx.compute_lir(program); 396 | let program = ctx.compute_datapack(&program); 397 | 398 | let mut interp = Interpreter::new(program, 0); 399 | 400 | let (_, func_name) = FunctionIdent::parse_from_command("wasmrunner:init").unwrap(); 401 | 402 | let interp_idx = interp.get_func_idx(&func_name); 403 | interp.set_pos(interp_idx); 404 | 405 | interp.run_to_end().unwrap(); 406 | 407 | TestState { wasm_file, interp } 408 | } 409 | } --------------------------------------------------------------------------------