lust/jit/codegen/
builder.rs

1use super::*;
2use crate::VM;
3impl JitCompiler {
4    pub fn new() -> Self {
5        Self {
6            ops: Assembler::new().unwrap(),
7            leaked_constants: Vec::new(),
8        }
9    }
10
11    pub fn compile_trace(
12        &mut self,
13        trace: &Trace,
14        trace_id: TraceId,
15        parent: Option<TraceId>,
16        hoisted_constants: Vec<(u8, Value)>,
17    ) -> Result<CompiledTrace> {
18        let mut guards = Vec::new();
19        let mut guard_index = 0i32;
20        dynasm!(self.ops
21            ; push rbp
22            ; mov rbp, rsp
23            ; push rbx
24            ; push r12
25            ; push r13
26            ; push r14
27            ; push r15
28            ; mov r12, rdi
29            ; mov r13, rsi
30        );
31        for (dest, value) in &hoisted_constants {
32            self.compile_load_const(*dest, value)?;
33        }
34
35        for (_i, op) in trace.ops.iter().enumerate() {
36            match op {
37                TraceOp::LoadConst { dest, value } => {
38                    self.compile_load_const(*dest, value)?;
39                }
40
41                TraceOp::Move { dest, src } => {
42                    self.compile_move(*dest, *src)?;
43                }
44
45                TraceOp::Add {
46                    dest,
47                    lhs,
48                    rhs,
49                    lhs_type,
50                    rhs_type,
51                } => {
52                    self.compile_add_specialized(*dest, *lhs, *rhs, *lhs_type, *rhs_type)?;
53                }
54
55                TraceOp::Sub {
56                    dest,
57                    lhs,
58                    rhs,
59                    lhs_type,
60                    rhs_type,
61                } => {
62                    self.compile_sub_specialized(*dest, *lhs, *rhs, *lhs_type, *rhs_type)?;
63                }
64
65                TraceOp::Mul {
66                    dest,
67                    lhs,
68                    rhs,
69                    lhs_type,
70                    rhs_type,
71                } => {
72                    self.compile_mul_specialized(*dest, *lhs, *rhs, *lhs_type, *rhs_type)?;
73                }
74
75                TraceOp::Div {
76                    dest,
77                    lhs,
78                    rhs,
79                    lhs_type,
80                    rhs_type,
81                } => {
82                    self.compile_div_specialized(*dest, *lhs, *rhs, *lhs_type, *rhs_type)?;
83                }
84
85                TraceOp::Mod {
86                    dest,
87                    lhs,
88                    rhs,
89                    lhs_type,
90                    rhs_type,
91                } => {
92                    self.compile_mod_specialized(*dest, *lhs, *rhs, *lhs_type, *rhs_type)?;
93                }
94
95                TraceOp::Neg { dest, src } => {
96                    self.compile_neg(*dest, *src)?;
97                }
98
99                TraceOp::Lt { dest, lhs, rhs } => {
100                    self.compile_lt(*dest, *lhs, *rhs)?;
101                }
102
103                TraceOp::Le { dest, lhs, rhs } => {
104                    self.compile_le(*dest, *lhs, *rhs)?;
105                }
106
107                TraceOp::Gt { dest, lhs, rhs } => {
108                    self.compile_gt(*dest, *lhs, *rhs)?;
109                }
110
111                TraceOp::Ge { dest, lhs, rhs } => {
112                    self.compile_ge(*dest, *lhs, *rhs)?;
113                }
114
115                TraceOp::Eq { dest, lhs, rhs } => {
116                    self.compile_eq(*dest, *lhs, *rhs)?;
117                }
118
119                TraceOp::Ne { dest, lhs, rhs } => {
120                    self.compile_ne(*dest, *lhs, *rhs)?;
121                }
122
123                TraceOp::And { dest, lhs, rhs } => {
124                    self.compile_and(*dest, *lhs, *rhs)?;
125                }
126
127                TraceOp::Or { dest, lhs, rhs } => {
128                    self.compile_or(*dest, *lhs, *rhs)?;
129                }
130
131                TraceOp::Not { dest, src } => {
132                    self.compile_not(*dest, *src)?;
133                }
134
135                TraceOp::Concat { dest, lhs, rhs } => {
136                    self.compile_concat(*dest, *lhs, *rhs)?;
137                }
138
139                TraceOp::GetIndex { dest, array, index } => {
140                    self.compile_get_index(*dest, *array, *index)?;
141                }
142
143                TraceOp::ArrayLen { dest, array } => {
144                    self.compile_array_len(*dest, *array)?;
145                }
146
147                TraceOp::GuardNativeFunction { register, function } => {
148                    let expected_ptr = function.pointer();
149                    crate::jit::log(|| format!("🔒 JIT: guard native reg {}", register));
150                    let guard = self.compile_guard_native_function(
151                        *register,
152                        expected_ptr,
153                        guard_index as usize,
154                    )?;
155                    guards.push(guard);
156                    guard_index += 1;
157                }
158
159                TraceOp::CallNative {
160                    dest,
161                    callee,
162                    function,
163                    first_arg,
164                    arg_count,
165                } => {
166                    let expected_ptr = function.pointer();
167                    self.compile_call_native(*dest, *callee, expected_ptr, *first_arg, *arg_count)?;
168                }
169
170                TraceOp::CallMethod {
171                    dest,
172                    object,
173                    method_name,
174                    first_arg,
175                    arg_count,
176                } => {
177                    self.compile_call_method(*dest, *object, method_name, *first_arg, *arg_count)?;
178                }
179
180                TraceOp::GetField {
181                    dest,
182                    object,
183                    field_name,
184                    field_index,
185                    value_type,
186                    is_weak,
187                } => {
188                    self.compile_get_field(
189                        *dest,
190                        *object,
191                        field_name,
192                        *field_index,
193                        *value_type,
194                        *is_weak,
195                    )?;
196                }
197
198                TraceOp::SetField {
199                    object,
200                    field_name,
201                    value,
202                    field_index,
203                    value_type,
204                    is_weak,
205                } => {
206                    self.compile_set_field(
207                        *object,
208                        field_name,
209                        *value,
210                        *field_index,
211                        *value_type,
212                        *is_weak,
213                    )?;
214                }
215
216                TraceOp::NewStruct {
217                    dest,
218                    struct_name,
219                    field_names,
220                    field_registers,
221                } => {
222                    self.compile_new_struct(*dest, struct_name, field_names, field_registers)?;
223                }
224
225                TraceOp::Guard {
226                    register,
227                    expected_type,
228                } => {
229                    let guard =
230                        self.compile_guard(*register, *expected_type, guard_index as usize)?;
231                    guards.push(guard);
232                    guard_index += 1;
233                }
234
235                TraceOp::GuardLoopContinue {
236                    condition_register,
237                    bailout_ip,
238                } => {
239                    let guard = self.compile_loop_continue_guard(
240                        *condition_register,
241                        *bailout_ip,
242                        guard_index as usize,
243                    )?;
244                    guards.push(guard);
245                    guard_index += 1;
246                }
247
248                TraceOp::NestedLoopCall {
249                    function_idx,
250                    loop_start_ip,
251                    bailout_ip,
252                } => {
253                    jit::log(|| {
254                        format!(
255                            "🔗 JIT: Nested loop detected at func {} ip {} (guard #{})",
256                            function_idx, loop_start_ip, guard_index
257                        )
258                    });
259                    guards.push(Guard {
260                        index: guard_index as usize,
261                        bailout_ip: *bailout_ip,
262                        kind: GuardKind::NestedLoop {
263                            function_idx: *function_idx,
264                            loop_start_ip: *loop_start_ip,
265                        },
266                        fail_count: 0,
267                        side_trace: None,
268                    });
269                    let current_guard_index = guard_index;
270                    dynasm!(self.ops
271                        ; mov eax, DWORD (current_guard_index + 1)
272                        ; jmp >exit
273                    );
274                    guard_index += 1;
275                }
276
277                TraceOp::Return { .. } => {}
278            }
279        }
280
281        dynasm!(self.ops
282            ; xor eax, eax
283            ; exit:
284            ; pop r15
285            ; pop r14
286            ; pop r13
287            ; pop r12
288            ; pop rbx
289            ; pop rbp
290            ; ret
291            ; fail:
292            ; mov eax, DWORD -1
293            ; jmp <exit
294        );
295        let ops = mem::replace(&mut self.ops, Assembler::new().unwrap());
296        let buffer = ops.finalize().unwrap();
297        let entry_point = buffer.ptr(dynasmrt::AssemblyOffset(0));
298        let entry: extern "C" fn(*mut Value, *mut VM, *const Function) -> i32 =
299            unsafe { mem::transmute(entry_point) };
300        Box::leak(Box::new(buffer));
301        let leaked_constants = mem::take(&mut self.leaked_constants);
302        Ok(CompiledTrace {
303            id: trace_id,
304            entry,
305            trace: trace.clone(),
306            guards,
307            parent,
308            side_traces: Vec::new(),
309            leaked_constants,
310            hoisted_constants,
311        })
312    }
313}