├── .gitignore ├── .rustfmt.toml ├── Cargo.toml ├── LICENSE ├── README.md └── src ├── bin └── toy.rs ├── frontend.rs ├── jit.rs └── lib.rs /.gitignore: -------------------------------------------------------------------------------- 1 | *.bk 2 | *.swp 3 | *.swo 4 | *.swx 5 | tags 6 | target 7 | Cargo.lock 8 | .*.rustfmt 9 | cranelift.dbg* 10 | rusty-tags.* 11 | -------------------------------------------------------------------------------- /.rustfmt.toml: -------------------------------------------------------------------------------- 1 | # This file tells tools we use rustfmt. We use the default settings. 2 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "cranelift-jit-demo" 3 | version = "0.0.0" 4 | authors = ["The Cranelift Project Developers"] 5 | license = "Apache-2.0 WITH LLVM-exception" 6 | repository = "https://github.com/bytecodealliance/cranelift-jit-demo" 7 | description = "Toy language implemented using cranelift-jit" 8 | edition = "2024" 9 | 10 | [dependencies] 11 | peg = "0.8.1" 12 | cranelift = "0.118.0" 13 | cranelift-module = "0.118.0" 14 | cranelift-jit = "0.118.0" 15 | cranelift-native = "0.118.0" 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 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 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | 204 | 205 | --- LLVM Exceptions to the Apache 2.0 License ---- 206 | 207 | As an exception, if, as a result of your compiling your source code, portions 208 | of this Software are embedded into an Object form of such source code, you 209 | may redistribute such embedded portions in such Object form without complying 210 | with the conditions of Sections 4(a), 4(b) and 4(d) of the License. 211 | 212 | In addition, if you combine or link compiled forms of this Software with 213 | software that is licensed under the GPLv2 ("Combined Software") and if a 214 | court of competent jurisdiction determines that the patent provision (Section 215 | 3), the indemnity provision (Section 9) or other Section of the License 216 | conflicts with the conditions of the GPLv2, you may retroactively and 217 | prospectively choose to deem waived or otherwise exclude such Section(s) of 218 | the License, but only in their entirety and only with respect to the Combined 219 | Software. 220 | 221 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Hello! 2 | 3 | This is a simple demo that JIT-compiles a toy language, using Cranelift. 4 | 5 | It uses the new JIT interface in development 6 | [here](https://github.com/bytecodealliance/wasmtime/tree/main/cranelift/jit). JIT takes care 7 | of managing a symbol table, allocating memory, and performing relocations, offering 8 | a relatively simple API. 9 | 10 | This is inspired in part by Ulysse Carion's 11 | [llvm-rust-getting-started](https://github.com/ucarion/llvm-rust-getting-started) 12 | and JT's [rustyjit](https://github.com/jntrnr/rustyjit). 13 | 14 | A quick introduction to Cranelift: Cranelift is a compiler backend. It's 15 | light-weight, supports `no_std` mode, doesn't use floating-point itself, 16 | and it makes efficient use of memory. 17 | 18 | And Cranelift is being architected to allow flexibility in how one uses it. 19 | Sometimes that flexibility can be a burden, which we've recently started to 20 | address in a new set of crates, `cranelift-module`, `cranelift-jit`, and 21 | `cranelift-faerie`, which put the pieces together in some easy-to-use 22 | configurations for working with multiple functions at once. `cranelift-module` 23 | is a common interface for working with multiple functions and data interfaces 24 | at once. This interface can sit on top of `cranelift-jit`, which writes 25 | code and data to memory where they can be executed and accessed. And, it can 26 | sit on top of `cranelift-faerie`, which writes code and data to native .o files 27 | which can be linked into native executables. 28 | 29 | This post introduces Cranelift by walking through a JIT demo, using 30 | the [`cranelift-jit`](https://crates.io/crates/cranelift-jit) crate. 31 | Currently this demo works on Linux x86-64 platforms. It may also work on Mac 32 | x86-64 platforms, though I haven't specifically tested that yet. And Cranelift 33 | is being designed to support many other kinds of platforms in the future. 34 | 35 | ### A walkthrough 36 | 37 | First, let's take a quick look at the toy language in use. It's a very simple 38 | language, in which all variables have type `isize`. (Cranelift does have full 39 | support for other integer and floating-point types, so this is just to keep the 40 | toy language simple). 41 | 42 | For a quick flavor, here's our 43 | [first example](./src/bin/toy.rs#L63) 44 | in the toy language: 45 | 46 | ``` 47 | fn foo(a, b) -> (c) { 48 | c = if a { 49 | if b { 50 | 30 51 | } else { 52 | 40 53 | } 54 | } else { 55 | 50 56 | } 57 | c = c + 2 58 | } 59 | ``` 60 | 61 | The grammar for this toy language is defined [here](./src/frontend.rs#L23), and 62 | this demo uses the [peg](https://crates.io/crates/peg) parser generator library 63 | to generate actual parser code for it. 64 | 65 | The output of parsing is a [custom AST type](./src/frontend.rs#L1): 66 | 67 | ```rust 68 | pub enum Expr { 69 | Literal(String), 70 | Identifier(String), 71 | Assign(String, Box), 72 | Eq(Box, Box), 73 | Ne(Box, Box), 74 | Lt(Box, Box), 75 | Le(Box, Box), 76 | Gt(Box, Box), 77 | Ge(Box, Box), 78 | Add(Box, Box), 79 | Sub(Box, Box), 80 | Mul(Box, Box), 81 | Div(Box, Box), 82 | IfElse(Box, Vec, Vec), 83 | WhileLoop(Box, Vec), 84 | Call(String, Vec), 85 | GlobalDataAddr(String), 86 | } 87 | ``` 88 | 89 | It's pretty minimal and straightforward. The `IfElse` can return a value, to 90 | show how that's done in Cranelift (see below). 91 | 92 | The [first thing we do](./src/bin/toy.rs#L6) is create an instance of our `JIT`: 93 | 94 | ```rust 95 | let mut jit = jit::JIT::new(); 96 | ``` 97 | 98 | The `JIT` class is defined [here](./src/jit.rs#L9) and contains several fields: 99 | 100 | - `builder_context` - Cranelift uses this to reuse dynamic allocations between 101 | compiling multiple functions. 102 | - `ctx` - This is the main `Context` object for compiling functions. 103 | - `data_description` - Similar to `ctx`, but for "compiling" data sections. 104 | - `module` - The `Module` which holds information about all functions and data 105 | objects defined in the current `JIT`. 106 | 107 | Before we go any further, let's talk about the underlying model here. The 108 | `Module` class divides the world into two kinds of things: functions, and data 109 | objects. Both functions and data objects have *names*, and can be imported into 110 | a module, defined and only referenced locally, or defined and exported for use 111 | in outside code. Functions are immutable, while data objects can be declared 112 | either readonly or writable. 113 | 114 | Both functions and data objects can contain references to other functions and 115 | data objects. Cranelift is designed to allow the low-level parts operate on each 116 | function and data object independently, so each function and data object maintains 117 | its own individual namespace of imported names. The 118 | [`Module`](https://docs.rs/cranelift-module/0.6.8/cranelift_module/trait.Module.html) 119 | struct takes care of maintaining a set of declarations for use across multiple 120 | functions and data objects. 121 | 122 | These concepts are sufficiently general that they're applicable to JITing as 123 | well as native object files (more discussion below!), and `Module` provides an 124 | interface which abstracts over both. 125 | 126 | Once we've [initialized the JIT data structures](./src/jit.rs#L28), we then use 127 | our `JIT` to [compile](./src/jit.rs#L52) some functions. 128 | 129 | The `JIT`'s `compile` function takes a string containing a function in the toy 130 | language. It [parses](./src/jit.rs#L55) the string into an AST, and then 131 | [translates](./src/jit.rs#L58) the AST into Cranelift IR. 132 | 133 | Our toy language only supports one type, so we start by [declaring that 134 | type](./src/jit.rs#L123) for convenience. 135 | 136 | We then start translating the function by adding [the function 137 | parameters](./src/jit.rs#L125) and [return types](./src/jit.rs#L131) to the 138 | Cranelift function signature. 139 | 140 | Then we [create](./src/jit.rs#L134) a 141 | [FunctionBuilder](https://docs.rs/cranelift-frontend/latest/cranelift_frontend/struct.FunctionBuilder.html) 142 | which is a utility for building up the contents of a Cranelift IR function. As 143 | we'll see below, `FunctionBuilder` includes functionality for constructing SSA 144 | form automatically so that users don't have to worry about it. 145 | 146 | Next, we [start](./src/jit.rs#L137) an initial basic block (block), which is the 147 | entry block of the function, and the place where we'll insert some code. 148 | 149 | - A basic block is a sequence of IR instructions which have a single entry 150 | point, and no branches until the very end, so execution always starts at the 151 | top and proceeds straight through to the end. 152 | 153 | Cranelift's basic blocks can have parameters. These take the place of PHI 154 | functions in other IRs. 155 | 156 | Here's an example of a block, showing branches (`brif` and `jump`) that are at 157 | the end of the block, and demonstrating some block parameters. 158 | 159 | ``` 160 | block0(v0: i32, v1: i32, v2: i32, v507: i64): 161 | v508 = iconst.i32 0 162 | v509 = iconst.i64 0 163 | v404 = ifcmp_imm v2, 0 164 | v10 = iadd_imm v2, -7 165 | v405 = ifcmp_imm v2, 7 166 | brif ugt v405, block29(v10) 167 | jump block29(v508) 168 | ``` 169 | 170 | The `FunctionBuilder` library will take care of inserting block parameters 171 | automatically, so frontends that don't need to use them directly generally don't 172 | need to worry about them, though one place they do come up is that incoming 173 | arguments to a function are represented as block parameters to the entry 174 | block. We must tell Cranelift to add the parameters, using 175 | [`append_block_params_for_function_params`](https://docs.rs/cranelift-frontend/latest/cranelift_frontend/struct.FunctionBuilder.html#method.append_block_params_for_function_params) 176 | like [so](./src/jit.rs#L143). 177 | 178 | The `FunctionBuilder` keeps track of a "current" block that new instructions are 179 | to be inserted into; we next [inform](./src/jit.rs#L146) it of our new block, 180 | using 181 | [`switch_to_block`](https://docs.rs/cranelift-frontend/latest/cranelift_frontend/struct.FunctionBuilder.html#method.switch_to_block), 182 | so that we can start inserting instructions into it. 183 | 184 | The one major concept about blocks is that the `FunctionBuilder` wants to know when 185 | all branches which could branch to a block have been seen, at which point it can 186 | *seal* the block, which allows it to perform SSA construction. All blocks must be 187 | sealed by the end of the function. We 188 | [seal](./src/jit.rs#L151) 189 | a block with 190 | [`seal_block`](https://docs.rs/cranelift-frontend/latest/cranelift_frontend/struct.FunctionBuilder.html#method.seal_block). 191 | 192 | Next, our toy language doesn't have explicit variable declarations, so we walk the 193 | AST to discover all the variables, so that we can 194 | [declare](./src/jit.rs#L156) 195 | them to the `FunctionBuilder`. These variables need not be in SSA form; the 196 | `FunctionBuilder` will take care of constructing SSA form internally. 197 | 198 | For convenience when walking the function body, the demo here 199 | [uses](./src/jit.rs#L159) 200 | a `FunctionTranslator` object, which holds the `FunctionBuilder`, the current 201 | `Module`, as well as the symbol table for looking up variables. Now we can start 202 | [walking the function body](./src/jit.rs#L166). 203 | 204 | [AST translation](./src/jit.rs#L196) utilizes the instruction-building features 205 | of `FunctionBuilder`. Let's start with a simple example translating integer 206 | literals: 207 | 208 | ```rust 209 | Expr::Literal(literal) => { 210 | let imm: i32 = literal.parse().unwrap(); 211 | self.builder.ins().iconst(self.int, i64::from(imm)) 212 | } 213 | ``` 214 | 215 | The first part is just extracting the integer value from the AST. The next line 216 | is the builder line: 217 | 218 | - The `.ins()` returns an "insertion object", which allows inserting an 219 | instruction at the end of the currently active block. 220 | - `iconst` is the name of the builder routine for creating [integer 221 | constants](https://docs.rs/cranelift-codegen/latest/cranelift_codegen/ir/trait.InstBuilder.html#method.iconst) 222 | in Cranelift. Every instruction in the IR can be created directly through 223 | such a function call. 224 | 225 | Translation of [Add nodes](./src/jit.rs#L203) and other arithmetic operations is 226 | similarly straightforward. 227 | 228 | Translation of [variable references](./src/jit.rs#L235) is mostly handled by 229 | `FunctionBuilder`'s `use_var` function: 230 | 231 | ```rust 232 | Expr::Identifier(name) => { 233 | // `use_var` is used to read the value of a variable. 234 | let variable = self.variables.get(&name).expect("variable not defined"); 235 | self.builder.use_var(*variable) 236 | } 237 | ``` 238 | `use_var` is for reading the value of a (non-SSA) variable. (Internally, 239 | `FunctionBuilder` constructs SSA form to satisfy all uses). 240 | 241 | Its companion is `def_var`, which is used to write the value of a (non-SSA) 242 | variable, which we use to implement assignment: 243 | 244 | ```rust 245 | fn translate_assign(&mut self, name: String, expr: Expr) -> Value { 246 | // `def_var` is used to write the value of a variable. Note that 247 | // variables can have multiple definitions. Cranelift will 248 | // convert them into SSA form for itself automatically. 249 | let new_value = self.translate_expr(*expr); 250 | let variable = self.variables.get(&name).unwrap(); 251 | self.builder.def_var(*variable, new_value); 252 | new_value 253 | } 254 | ``` 255 | 256 | Next, let's dive into [if-else](./src/jit.rs#L241) expressions. In order to 257 | demonstrate explicit SSA construction, this demo gives if-else expressions 258 | return values. The way this looks in Cranelift is that the true and false arms 259 | of the if-else both have branches to a common merge point, and they each pass 260 | their "return value" as a block parameter to the merge point. 261 | 262 | Note that we seal the blocks we create once we know we'll have no more predecessors, 263 | which is something that a typical AST makes it easy to know. 264 | 265 | Putting it all together, here's the Cranelift IR for the function named 266 | [foo](./src/toy.rs#L63) in the demo program, which contains multiple ifs: 267 | 268 | ``` 269 | function u0:0(i64, i64) -> i64 system_v { 270 | block0(v0: i64, v1: i64): 271 | v2 = iconst.i64 0 272 | brz v0, block2 273 | jump block1 274 | 275 | block1: 276 | v4 = iconst.i64 0 277 | brz.i64 v1, block5 278 | jump block4 279 | 280 | block4: 281 | v6 = iconst.i64 0 282 | v7 = iconst.i64 30 283 | jump block6(v7) 284 | 285 | block5: 286 | v8 = iconst.i64 0 287 | v9 = iconst.i64 40 288 | jump block6(v9) 289 | 290 | block6(v5: i64): 291 | jump block3(v5) 292 | 293 | block2: 294 | v10 = iconst.i64 0 295 | v11 = iconst.i64 50 296 | jump block3(v11) 297 | 298 | block3(v3: i64): 299 | v12 = iconst.i64 2 300 | v13 = iadd v3, v12 301 | return v13 302 | } 303 | ``` 304 | 305 | The [while loop](./src/jit.rs#L323) translation is also straightforward. 306 | 307 | Here's the Cranelift IR for the function named [iterative_fib](./src/toy.rs#L94) 308 | in the demo program, which contains a while loop: 309 | 310 | ``` 311 | function u0:0(i64) -> i64 system_v { 312 | block0(v0: i64): 313 | v1 = iconst.i64 0 314 | v2 = iconst.i64 0 315 | v3 = icmp eq v0, v2 316 | v4 = bint.i64 v3 317 | brz v4, block2 318 | jump block1 319 | 320 | block1: 321 | v6 = iconst.i64 0 322 | v7 = iconst.i64 0 323 | jump block3(v7, v7) 324 | 325 | block2: 326 | v8 = iconst.i64 0 327 | v9 = iconst.i64 1 328 | v10 = isub.i64 v0, v9 329 | v11 = iconst.i64 0 330 | v12 = iconst.i64 1 331 | jump block4(v10, v12, v11) 332 | 333 | block4(v13: i64, v17: i64, v18: i64): 334 | v14 = iconst.i64 0 335 | v15 = icmp ne v13, v14 336 | v16 = bint.i64 v15 337 | brz v16, block6 338 | jump block5 339 | 340 | block5: 341 | v19 = iadd.i64 v17, v18 342 | v20 = iconst.i64 1 343 | v21 = isub.i64 v13, v20 344 | jump block4(v21, v19, v17) 345 | 346 | block6: 347 | v22 = iconst.i64 0 348 | jump block3(v22, v17) 349 | 350 | block3(v5: i64, v23: i64): 351 | return v23 352 | } 353 | ``` 354 | 355 | For [calls](./src/jit.rs#L355), the basic steps are to determine the call 356 | signature, declare the function to be called, put the values to be passed in an 357 | array, and then call the `call` function. 358 | 359 | The translation for [global data symbols](./src/jit.rs#L381), is similar; first 360 | declare the symbol to the module, then declare it to the current function, and 361 | then use the `symbol_value` instruction to produce the value. 362 | 363 | And with that, we can return to our main `toy.rs` file and run some more examples. 364 | There are examples of recursive and iterative fibonacci, which demonstrate more use 365 | of calls and control flow. 366 | 367 | And there's a hello world example which demonstrates several other features. 368 | 369 | This program needs to allocate some [data](./src/toy.rs#L33) to hold the string 370 | data. Inside jit.rs, [`create_data`](./src/jit.rs#L95) we initialize a 371 | `DataDescription` with the contents of the hello string, and also declare a data 372 | object. Then we use the `DataDescription` object to define the object. At that 373 | point, we're done with the `DataDescription` object and can clear it. We then call 374 | `finalize_data` to perform linking (although our simple hello string doesn't 375 | make any references so there isn't anything to do) and to obtain the final 376 | runtime address of the data, which we then convert back into a Rust slice for 377 | convenience. 378 | 379 | And to show off a handy feature of the jit backend, it can look up symbols 380 | with `libc::dlsym`, so you can call libc functions such as `puts` (being careful 381 | to NUL-terminate your strings!). Unfortunately, `printf` requires varargs, which 382 | Cranelift does not yet support. 383 | 384 | And with all that, we can say "hello world!". 385 | 386 | 387 | ### Native object files 388 | 389 | Because of the `Module` abstraction, this demo can be adapted to write out an ELF 390 | .o file rather than JITing the code to memory with only minor changes, and I've done 391 | so in a branch [here](https://github.com/bytecodealliance/simplejit-demo/tree/faerie). 392 | This writes a `test.o` file, which on an x86-64 ELF platform you can link with 393 | `cc test.o` and it produces an executable that calls the generated functions, 394 | including printing "hello world!". 395 | 396 | Another branch [here](https://github.com/bytecodealliance/simplejit-demo/tree/faerie-macho) 397 | shows how to write Mach-O object files. 398 | 399 | Object files are written using the [faerie](https://github.com/m4b/faerie) 400 | library. 401 | 402 | ### Have fun! 403 | 404 | Cranelift is still evolving, so if there are things here which are confusing or 405 | awkward, please let us know, via [github 406 | issues](https://github.com/bytecodealliance/wasmtime/issues?q=is%3Aissue+is%3Aopen+label%3Acranelift) 407 | or just stop by the [gitter chat](https://gitter.im/CraneStation/Lobby/~chat). 408 | Very few things in Cranelift's design are set in stone at this time, and we're 409 | really interested to hear from people about what makes sense what doesn't. 410 | -------------------------------------------------------------------------------- /src/bin/toy.rs: -------------------------------------------------------------------------------- 1 | use core::mem; 2 | use cranelift_jit_demo::jit; 3 | 4 | fn main() -> Result<(), String> { 5 | // Create the JIT instance, which manages all generated functions and data. 6 | let mut jit = jit::JIT::default(); 7 | println!("the answer is: {}", run_foo(&mut jit)?); 8 | println!( 9 | "recursive_fib(10) = {}", 10 | run_recursive_fib_code(&mut jit, 10)? 11 | ); 12 | println!( 13 | "iterative_fib(10) = {}", 14 | run_iterative_fib_code(&mut jit, 10)? 15 | ); 16 | run_hello(&mut jit)?; 17 | Ok(()) 18 | } 19 | 20 | fn run_foo(jit: &mut jit::JIT) -> Result { 21 | unsafe { run_code(jit, FOO_CODE, (1, 0)) } 22 | } 23 | 24 | fn run_recursive_fib_code(jit: &mut jit::JIT, input: isize) -> Result { 25 | unsafe { run_code(jit, RECURSIVE_FIB_CODE, input) } 26 | } 27 | 28 | fn run_iterative_fib_code(jit: &mut jit::JIT, input: isize) -> Result { 29 | unsafe { run_code(jit, ITERATIVE_FIB_CODE, input) } 30 | } 31 | 32 | fn run_hello(jit: &mut jit::JIT) -> Result { 33 | jit.create_data("hello_string", "hello world!\0".as_bytes().to_vec())?; 34 | unsafe { run_code(jit, HELLO_CODE, ()) } 35 | } 36 | 37 | /// Executes the given code using the cranelift JIT compiler. 38 | /// 39 | /// Feeds the given input into the JIT compiled function and returns the resulting output. 40 | /// 41 | /// # Safety 42 | /// 43 | /// This function is unsafe since it relies on the caller to provide it with the correct 44 | /// input and output types. Using incorrect types at this point may corrupt the program's state. 45 | unsafe fn run_code(jit: &mut jit::JIT, code: &str, input: I) -> Result { unsafe { 46 | // Pass the string to the JIT, and it returns a raw pointer to machine code. 47 | let code_ptr = jit.compile(code)?; 48 | // Cast the raw pointer to a typed function pointer. This is unsafe, because 49 | // this is the critical point where you have to trust that the generated code 50 | // is safe to be called. 51 | let code_fn = mem::transmute::<_, fn(I) -> O>(code_ptr); 52 | // And now we can call it! 53 | Ok(code_fn(input)) 54 | }} 55 | 56 | // A small test function. 57 | // 58 | // The `(c)` declares a return variable; the function returns whatever value 59 | // it was assigned when the function exits. Note that there are multiple 60 | // assignments, so the input is not in SSA form, but that's ok because 61 | // Cranelift handles all the details of translating into SSA form itself. 62 | const FOO_CODE: &str = r#" 63 | fn foo(a, b) -> (c) { 64 | c = if a { 65 | if b { 66 | 30 67 | } else { 68 | 40 69 | } 70 | } else { 71 | 50 72 | } 73 | c = c + 2 74 | } 75 | "#; 76 | 77 | /// Another example: Recursive fibonacci. 78 | const RECURSIVE_FIB_CODE: &str = r#" 79 | fn recursive_fib(n) -> (r) { 80 | r = if n == 0 { 81 | 0 82 | } else { 83 | if n == 1 { 84 | 1 85 | } else { 86 | recursive_fib(n - 1) + recursive_fib(n - 2) 87 | } 88 | } 89 | } 90 | "#; 91 | 92 | /// Another example: Iterative fibonacci. 93 | const ITERATIVE_FIB_CODE: &str = r#" 94 | fn iterative_fib(n) -> (r) { 95 | if n == 0 { 96 | r = 0 97 | } else { 98 | n = n - 1 99 | a = 0 100 | r = 1 101 | while n != 0 { 102 | t = r 103 | r = r + a 104 | a = t 105 | n = n - 1 106 | } 107 | } 108 | } 109 | "#; 110 | 111 | /// Let's say hello, by calling into libc. The puts function is resolved by 112 | /// dlsym to the libc function, and the string &hello_string is defined below. 113 | const HELLO_CODE: &str = r#" 114 | fn hello() -> (r) { 115 | puts(&hello_string) 116 | } 117 | "#; 118 | -------------------------------------------------------------------------------- /src/frontend.rs: -------------------------------------------------------------------------------- 1 | /// The AST node for expressions. 2 | pub enum Expr { 3 | Literal(String), 4 | Identifier(String), 5 | Assign(String, Box), 6 | Eq(Box, Box), 7 | Ne(Box, Box), 8 | Lt(Box, Box), 9 | Le(Box, Box), 10 | Gt(Box, Box), 11 | Ge(Box, Box), 12 | Add(Box, Box), 13 | Sub(Box, Box), 14 | Mul(Box, Box), 15 | Div(Box, Box), 16 | IfElse(Box, Vec, Vec), 17 | WhileLoop(Box, Vec), 18 | Call(String, Vec), 19 | GlobalDataAddr(String), 20 | } 21 | 22 | peg::parser!(pub grammar parser() for str { 23 | pub rule function() -> (String, Vec, String, Vec) 24 | = [' ' | '\t' | '\n']* "fn" _ name:identifier() _ 25 | "(" params:((_ i:identifier() _ {i}) ** ",") ")" _ 26 | "->" _ 27 | "(" returns:(_ i:identifier() _ {i}) ")" _ 28 | "{" _ "\n" 29 | stmts:statements() 30 | _ "}" _ "\n" _ 31 | { (name, params, returns, stmts) } 32 | 33 | rule statements() -> Vec 34 | = s:(statement()*) { s } 35 | 36 | rule statement() -> Expr 37 | = _ e:expression() _ "\n" { e } 38 | 39 | rule expression() -> Expr 40 | = if_else() 41 | / while_loop() 42 | / assignment() 43 | / binary_op() 44 | 45 | rule if_else() -> Expr 46 | = "if" _ e:expression() _ "{" _ "\n" 47 | then_body:statements() _ "}" _ "else" _ "{" _ "\n" 48 | else_body:statements() _ "}" 49 | { Expr::IfElse(Box::new(e), then_body, else_body) } 50 | 51 | rule while_loop() -> Expr 52 | = "while" _ e:expression() _ "{" _ "\n" 53 | loop_body:statements() _ "}" 54 | { Expr::WhileLoop(Box::new(e), loop_body) } 55 | 56 | rule assignment() -> Expr 57 | = i:identifier() _ "=" _ e:expression() {Expr::Assign(i, Box::new(e))} 58 | 59 | rule binary_op() -> Expr = precedence!{ 60 | a:@ _ "==" _ b:(@) { Expr::Eq(Box::new(a), Box::new(b)) } 61 | a:@ _ "!=" _ b:(@) { Expr::Ne(Box::new(a), Box::new(b)) } 62 | a:@ _ "<" _ b:(@) { Expr::Lt(Box::new(a), Box::new(b)) } 63 | a:@ _ "<=" _ b:(@) { Expr::Le(Box::new(a), Box::new(b)) } 64 | a:@ _ ">" _ b:(@) { Expr::Gt(Box::new(a), Box::new(b)) } 65 | a:@ _ ">=" _ b:(@) { Expr::Ge(Box::new(a), Box::new(b)) } 66 | -- 67 | a:@ _ "+" _ b:(@) { Expr::Add(Box::new(a), Box::new(b)) } 68 | a:@ _ "-" _ b:(@) { Expr::Sub(Box::new(a), Box::new(b)) } 69 | -- 70 | a:@ _ "*" _ b:(@) { Expr::Mul(Box::new(a), Box::new(b)) } 71 | a:@ _ "/" _ b:(@) { Expr::Div(Box::new(a), Box::new(b)) } 72 | -- 73 | i:identifier() _ "(" args:((_ e:expression() _ {e}) ** ",") ")" { Expr::Call(i, args) } 74 | i:identifier() { Expr::Identifier(i) } 75 | l:literal() { l } 76 | } 77 | 78 | rule identifier() -> String 79 | = quiet!{ n:$(['a'..='z' | 'A'..='Z' | '_']['a'..='z' | 'A'..='Z' | '0'..='9' | '_']*) { n.to_owned() } } 80 | / expected!("identifier") 81 | 82 | rule literal() -> Expr 83 | = n:$(['0'..='9']+) { Expr::Literal(n.to_owned()) } 84 | / "&" i:identifier() { Expr::GlobalDataAddr(i) } 85 | 86 | rule _() = quiet!{[' ' | '\t']*} 87 | }); 88 | -------------------------------------------------------------------------------- /src/jit.rs: -------------------------------------------------------------------------------- 1 | use crate::frontend::*; 2 | use cranelift::prelude::*; 3 | use cranelift_jit::{JITBuilder, JITModule}; 4 | use cranelift_module::{DataDescription, Linkage, Module}; 5 | use std::collections::HashMap; 6 | use std::slice; 7 | 8 | /// The basic JIT class. 9 | pub struct JIT { 10 | /// The function builder context, which is reused across multiple 11 | /// FunctionBuilder instances. 12 | builder_context: FunctionBuilderContext, 13 | 14 | /// The main Cranelift context, which holds the state for codegen. Cranelift 15 | /// separates this from `Module` to allow for parallel compilation, with a 16 | /// context per thread, though this isn't in the simple demo here. 17 | ctx: codegen::Context, 18 | 19 | /// The data description, which is to data objects what `ctx` is to functions. 20 | data_description: DataDescription, 21 | 22 | /// The module, with the jit backend, which manages the JIT'd 23 | /// functions. 24 | module: JITModule, 25 | } 26 | 27 | impl Default for JIT { 28 | fn default() -> Self { 29 | let mut flag_builder = settings::builder(); 30 | flag_builder.set("use_colocated_libcalls", "false").unwrap(); 31 | flag_builder.set("is_pic", "false").unwrap(); 32 | let isa_builder = cranelift_native::builder().unwrap_or_else(|msg| { 33 | panic!("host machine is not supported: {}", msg); 34 | }); 35 | let isa = isa_builder 36 | .finish(settings::Flags::new(flag_builder)) 37 | .unwrap(); 38 | let builder = JITBuilder::with_isa(isa, cranelift_module::default_libcall_names()); 39 | 40 | let module = JITModule::new(builder); 41 | Self { 42 | builder_context: FunctionBuilderContext::new(), 43 | ctx: module.make_context(), 44 | data_description: DataDescription::new(), 45 | module, 46 | } 47 | } 48 | } 49 | 50 | impl JIT { 51 | /// Compile a string in the toy language into machine code. 52 | pub fn compile(&mut self, input: &str) -> Result<*const u8, String> { 53 | // First, parse the string, producing AST nodes. 54 | let (name, params, the_return, stmts) = 55 | parser::function(input).map_err(|e| e.to_string())?; 56 | 57 | // Then, translate the AST nodes into Cranelift IR. 58 | self.translate(params, the_return, stmts)?; 59 | 60 | // Next, declare the function to jit. Functions must be declared 61 | // before they can be called, or defined. 62 | // 63 | // TODO: This may be an area where the API should be streamlined; should 64 | // we have a version of `declare_function` that automatically declares 65 | // the function? 66 | let id = self 67 | .module 68 | .declare_function(&name, Linkage::Export, &self.ctx.func.signature) 69 | .map_err(|e| e.to_string())?; 70 | 71 | // Define the function to jit. This finishes compilation, although 72 | // there may be outstanding relocations to perform. Currently, jit 73 | // cannot finish relocations until all functions to be called are 74 | // defined. For this toy demo for now, we'll just finalize the 75 | // function below. 76 | self.module 77 | .define_function(id, &mut self.ctx) 78 | .map_err(|e| e.to_string())?; 79 | 80 | // Now that compilation is finished, we can clear out the context state. 81 | self.module.clear_context(&mut self.ctx); 82 | 83 | // Finalize the functions which we just defined, which resolves any 84 | // outstanding relocations (patching in addresses, now that they're 85 | // available). 86 | self.module.finalize_definitions().unwrap(); 87 | 88 | // We can now retrieve a pointer to the machine code. 89 | let code = self.module.get_finalized_function(id); 90 | 91 | Ok(code) 92 | } 93 | 94 | /// Create a zero-initialized data section. 95 | pub fn create_data(&mut self, name: &str, contents: Vec) -> Result<&[u8], String> { 96 | // The steps here are analogous to `compile`, except that data is much 97 | // simpler than functions. 98 | self.data_description.define(contents.into_boxed_slice()); 99 | let id = self 100 | .module 101 | .declare_data(name, Linkage::Export, true, false) 102 | .map_err(|e| e.to_string())?; 103 | 104 | self.module 105 | .define_data(id, &self.data_description) 106 | .map_err(|e| e.to_string())?; 107 | self.data_description.clear(); 108 | self.module.finalize_definitions().unwrap(); 109 | let buffer = self.module.get_finalized_data(id); 110 | // TODO: Can we move the unsafe into cranelift? 111 | Ok(unsafe { slice::from_raw_parts(buffer.0, buffer.1) }) 112 | } 113 | 114 | // Translate from toy-language AST nodes into Cranelift IR. 115 | fn translate( 116 | &mut self, 117 | params: Vec, 118 | the_return: String, 119 | stmts: Vec, 120 | ) -> Result<(), String> { 121 | // Our toy language currently only supports I64 values, though Cranelift 122 | // supports other types. 123 | let int = self.module.target_config().pointer_type(); 124 | 125 | for _p in ¶ms { 126 | self.ctx.func.signature.params.push(AbiParam::new(int)); 127 | } 128 | 129 | // Our toy language currently only supports one return value, though 130 | // Cranelift is designed to support more. 131 | self.ctx.func.signature.returns.push(AbiParam::new(int)); 132 | 133 | // Create the builder to build a function. 134 | let mut builder = FunctionBuilder::new(&mut self.ctx.func, &mut self.builder_context); 135 | 136 | // Create the entry block, to start emitting code in. 137 | let entry_block = builder.create_block(); 138 | 139 | // Since this is the entry block, add block parameters corresponding to 140 | // the function's parameters. 141 | // 142 | // TODO: Streamline the API here. 143 | builder.append_block_params_for_function_params(entry_block); 144 | 145 | // Tell the builder to emit code in this block. 146 | builder.switch_to_block(entry_block); 147 | 148 | // And, tell the builder that this block will have no further 149 | // predecessors. Since it's the entry block, it won't have any 150 | // predecessors. 151 | builder.seal_block(entry_block); 152 | 153 | // The toy language allows variables to be declared implicitly. 154 | // Walk the AST and declare all implicitly-declared variables. 155 | let variables = 156 | declare_variables(int, &mut builder, ¶ms, &the_return, &stmts, entry_block); 157 | 158 | // Now translate the statements of the function body. 159 | let mut trans = FunctionTranslator { 160 | int, 161 | builder, 162 | variables, 163 | module: &mut self.module, 164 | }; 165 | for expr in stmts { 166 | trans.translate_expr(expr); 167 | } 168 | 169 | // Set up the return variable of the function. Above, we declared a 170 | // variable to hold the return value. Here, we just do a use of that 171 | // variable. 172 | let return_variable = trans.variables.get(&the_return).unwrap(); 173 | let return_value = trans.builder.use_var(*return_variable); 174 | 175 | // Emit the return instruction. 176 | trans.builder.ins().return_(&[return_value]); 177 | 178 | // Tell the builder we're done with this function. 179 | trans.builder.finalize(); 180 | Ok(()) 181 | } 182 | } 183 | 184 | /// A collection of state used for translating from toy-language AST nodes 185 | /// into Cranelift IR. 186 | struct FunctionTranslator<'a> { 187 | int: types::Type, 188 | builder: FunctionBuilder<'a>, 189 | variables: HashMap, 190 | module: &'a mut JITModule, 191 | } 192 | 193 | impl<'a> FunctionTranslator<'a> { 194 | /// When you write out instructions in Cranelift, you get back `Value`s. You 195 | /// can then use these references in other instructions. 196 | fn translate_expr(&mut self, expr: Expr) -> Value { 197 | match expr { 198 | Expr::Literal(literal) => { 199 | let imm: i32 = literal.parse().unwrap(); 200 | self.builder.ins().iconst(self.int, i64::from(imm)) 201 | } 202 | 203 | Expr::Add(lhs, rhs) => { 204 | let lhs = self.translate_expr(*lhs); 205 | let rhs = self.translate_expr(*rhs); 206 | self.builder.ins().iadd(lhs, rhs) 207 | } 208 | 209 | Expr::Sub(lhs, rhs) => { 210 | let lhs = self.translate_expr(*lhs); 211 | let rhs = self.translate_expr(*rhs); 212 | self.builder.ins().isub(lhs, rhs) 213 | } 214 | 215 | Expr::Mul(lhs, rhs) => { 216 | let lhs = self.translate_expr(*lhs); 217 | let rhs = self.translate_expr(*rhs); 218 | self.builder.ins().imul(lhs, rhs) 219 | } 220 | 221 | Expr::Div(lhs, rhs) => { 222 | let lhs = self.translate_expr(*lhs); 223 | let rhs = self.translate_expr(*rhs); 224 | self.builder.ins().udiv(lhs, rhs) 225 | } 226 | 227 | Expr::Eq(lhs, rhs) => self.translate_icmp(IntCC::Equal, *lhs, *rhs), 228 | Expr::Ne(lhs, rhs) => self.translate_icmp(IntCC::NotEqual, *lhs, *rhs), 229 | Expr::Lt(lhs, rhs) => self.translate_icmp(IntCC::SignedLessThan, *lhs, *rhs), 230 | Expr::Le(lhs, rhs) => self.translate_icmp(IntCC::SignedLessThanOrEqual, *lhs, *rhs), 231 | Expr::Gt(lhs, rhs) => self.translate_icmp(IntCC::SignedGreaterThan, *lhs, *rhs), 232 | Expr::Ge(lhs, rhs) => self.translate_icmp(IntCC::SignedGreaterThanOrEqual, *lhs, *rhs), 233 | Expr::Call(name, args) => self.translate_call(name, args), 234 | Expr::GlobalDataAddr(name) => self.translate_global_data_addr(name), 235 | Expr::Identifier(name) => { 236 | // `use_var` is used to read the value of a variable. 237 | let variable = self.variables.get(&name).expect("variable not defined"); 238 | self.builder.use_var(*variable) 239 | } 240 | Expr::Assign(name, expr) => self.translate_assign(name, *expr), 241 | Expr::IfElse(condition, then_body, else_body) => { 242 | self.translate_if_else(*condition, then_body, else_body) 243 | } 244 | Expr::WhileLoop(condition, loop_body) => { 245 | self.translate_while_loop(*condition, loop_body) 246 | } 247 | } 248 | } 249 | 250 | fn translate_assign(&mut self, name: String, expr: Expr) -> Value { 251 | // `def_var` is used to write the value of a variable. Note that 252 | // variables can have multiple definitions. Cranelift will 253 | // convert them into SSA form for itself automatically. 254 | let new_value = self.translate_expr(expr); 255 | let variable = self.variables.get(&name).unwrap(); 256 | self.builder.def_var(*variable, new_value); 257 | new_value 258 | } 259 | 260 | fn translate_icmp(&mut self, cmp: IntCC, lhs: Expr, rhs: Expr) -> Value { 261 | let lhs = self.translate_expr(lhs); 262 | let rhs = self.translate_expr(rhs); 263 | self.builder.ins().icmp(cmp, lhs, rhs) 264 | } 265 | 266 | fn translate_if_else( 267 | &mut self, 268 | condition: Expr, 269 | then_body: Vec, 270 | else_body: Vec, 271 | ) -> Value { 272 | let condition_value = self.translate_expr(condition); 273 | 274 | let then_block = self.builder.create_block(); 275 | let else_block = self.builder.create_block(); 276 | let merge_block = self.builder.create_block(); 277 | 278 | // If-else constructs in the toy language have a return value. 279 | // In traditional SSA form, this would produce a PHI between 280 | // the then and else bodies. Cranelift uses block parameters, 281 | // so set up a parameter in the merge block, and we'll pass 282 | // the return values to it from the branches. 283 | self.builder.append_block_param(merge_block, self.int); 284 | 285 | // Test the if condition and conditionally branch. 286 | self.builder 287 | .ins() 288 | .brif(condition_value, then_block, &[], else_block, &[]); 289 | 290 | self.builder.switch_to_block(then_block); 291 | self.builder.seal_block(then_block); 292 | let mut then_return = self.builder.ins().iconst(self.int, 0); 293 | for expr in then_body { 294 | then_return = self.translate_expr(expr); 295 | } 296 | 297 | // Jump to the merge block, passing it the block return value. 298 | self.builder.ins().jump(merge_block, &[then_return]); 299 | 300 | self.builder.switch_to_block(else_block); 301 | self.builder.seal_block(else_block); 302 | let mut else_return = self.builder.ins().iconst(self.int, 0); 303 | for expr in else_body { 304 | else_return = self.translate_expr(expr); 305 | } 306 | 307 | // Jump to the merge block, passing it the block return value. 308 | self.builder.ins().jump(merge_block, &[else_return]); 309 | 310 | // Switch to the merge block for subsequent statements. 311 | self.builder.switch_to_block(merge_block); 312 | 313 | // We've now seen all the predecessors of the merge block. 314 | self.builder.seal_block(merge_block); 315 | 316 | // Read the value of the if-else by reading the merge block 317 | // parameter. 318 | let phi = self.builder.block_params(merge_block)[0]; 319 | 320 | phi 321 | } 322 | 323 | fn translate_while_loop(&mut self, condition: Expr, loop_body: Vec) -> Value { 324 | let header_block = self.builder.create_block(); 325 | let body_block = self.builder.create_block(); 326 | let exit_block = self.builder.create_block(); 327 | 328 | self.builder.ins().jump(header_block, &[]); 329 | self.builder.switch_to_block(header_block); 330 | 331 | let condition_value = self.translate_expr(condition); 332 | self.builder 333 | .ins() 334 | .brif(condition_value, body_block, &[], exit_block, &[]); 335 | 336 | self.builder.switch_to_block(body_block); 337 | self.builder.seal_block(body_block); 338 | 339 | for expr in loop_body { 340 | self.translate_expr(expr); 341 | } 342 | self.builder.ins().jump(header_block, &[]); 343 | 344 | self.builder.switch_to_block(exit_block); 345 | 346 | // We've reached the bottom of the loop, so there will be no 347 | // more backedges to the header to exits to the bottom. 348 | self.builder.seal_block(header_block); 349 | self.builder.seal_block(exit_block); 350 | 351 | // Just return 0 for now. 352 | self.builder.ins().iconst(self.int, 0) 353 | } 354 | 355 | fn translate_call(&mut self, name: String, args: Vec) -> Value { 356 | let mut sig = self.module.make_signature(); 357 | 358 | // Add a parameter for each argument. 359 | for _arg in &args { 360 | sig.params.push(AbiParam::new(self.int)); 361 | } 362 | 363 | // For simplicity for now, just make all calls return a single I64. 364 | sig.returns.push(AbiParam::new(self.int)); 365 | 366 | // TODO: Streamline the API here? 367 | let callee = self 368 | .module 369 | .declare_function(&name, Linkage::Import, &sig) 370 | .expect("problem declaring function"); 371 | let local_callee = self.module.declare_func_in_func(callee, self.builder.func); 372 | 373 | let mut arg_values = Vec::new(); 374 | for arg in args { 375 | arg_values.push(self.translate_expr(arg)) 376 | } 377 | let call = self.builder.ins().call(local_callee, &arg_values); 378 | self.builder.inst_results(call)[0] 379 | } 380 | 381 | fn translate_global_data_addr(&mut self, name: String) -> Value { 382 | let sym = self 383 | .module 384 | .declare_data(&name, Linkage::Export, true, false) 385 | .expect("problem declaring data object"); 386 | let local_id = self.module.declare_data_in_func(sym, self.builder.func); 387 | 388 | let pointer = self.module.target_config().pointer_type(); 389 | self.builder.ins().symbol_value(pointer, local_id) 390 | } 391 | } 392 | 393 | fn declare_variables( 394 | int: types::Type, 395 | builder: &mut FunctionBuilder, 396 | params: &[String], 397 | the_return: &str, 398 | stmts: &[Expr], 399 | entry_block: Block, 400 | ) -> HashMap { 401 | let mut variables = HashMap::new(); 402 | let mut index = 0; 403 | 404 | for (i, name) in params.iter().enumerate() { 405 | // TODO: cranelift_frontend should really have an API to make it easy to set 406 | // up param variables. 407 | let val = builder.block_params(entry_block)[i]; 408 | let var = declare_variable(int, builder, &mut variables, &mut index, name); 409 | builder.def_var(var, val); 410 | } 411 | let zero = builder.ins().iconst(int, 0); 412 | let return_variable = declare_variable(int, builder, &mut variables, &mut index, the_return); 413 | builder.def_var(return_variable, zero); 414 | for expr in stmts { 415 | declare_variables_in_stmt(int, builder, &mut variables, &mut index, expr); 416 | } 417 | 418 | variables 419 | } 420 | 421 | /// Recursively descend through the AST, translating all implicit 422 | /// variable declarations. 423 | fn declare_variables_in_stmt( 424 | int: types::Type, 425 | builder: &mut FunctionBuilder, 426 | variables: &mut HashMap, 427 | index: &mut usize, 428 | expr: &Expr, 429 | ) { 430 | match *expr { 431 | Expr::Assign(ref name, _) => { 432 | declare_variable(int, builder, variables, index, name); 433 | } 434 | Expr::IfElse(ref _condition, ref then_body, ref else_body) => { 435 | for stmt in then_body { 436 | declare_variables_in_stmt(int, builder, variables, index, stmt); 437 | } 438 | for stmt in else_body { 439 | declare_variables_in_stmt(int, builder, variables, index, stmt); 440 | } 441 | } 442 | Expr::WhileLoop(ref _condition, ref loop_body) => { 443 | for stmt in loop_body { 444 | declare_variables_in_stmt(int, builder, variables, index, stmt); 445 | } 446 | } 447 | _ => (), 448 | } 449 | } 450 | 451 | /// Declare a single variable declaration. 452 | fn declare_variable( 453 | int: types::Type, 454 | builder: &mut FunctionBuilder, 455 | variables: &mut HashMap, 456 | index: &mut usize, 457 | name: &str, 458 | ) -> Variable { 459 | let var = Variable::new(*index); 460 | if !variables.contains_key(name) { 461 | variables.insert(name.into(), var); 462 | builder.declare_var(var, int); 463 | *index += 1; 464 | } 465 | var 466 | } 467 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod frontend; 2 | pub mod jit; 3 | --------------------------------------------------------------------------------