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(
168                        *dest,
169                        *callee,
170                        expected_ptr,
171                        *first_arg,
172                        *arg_count,
173                    )?;
174                }
175
176                TraceOp::CallMethod {
177                    dest,
178                    object,
179                    method_name,
180                    first_arg,
181                    arg_count,
182                } => {
183                    self.compile_call_method(*dest, *object, method_name, *first_arg, *arg_count)?;
184                }
185
186                TraceOp::GetField {
187                    dest,
188                    object,
189                    field_name,
190                    field_index,
191                    value_type,
192                    is_weak,
193                } => {
194                    self.compile_get_field(
195                        *dest,
196                        *object,
197                        field_name,
198                        *field_index,
199                        *value_type,
200                        *is_weak,
201                    )?;
202                }
203
204                TraceOp::SetField {
205                    object,
206                    field_name,
207                    value,
208                    field_index,
209                    value_type,
210                    is_weak,
211                } => {
212                    self.compile_set_field(
213                        *object,
214                        field_name,
215                        *value,
216                        *field_index,
217                        *value_type,
218                        *is_weak,
219                    )?;
220                }
221
222                TraceOp::NewStruct {
223                    dest,
224                    struct_name,
225                    field_names,
226                    field_registers,
227                } => {
228                    self.compile_new_struct(*dest, struct_name, field_names, field_registers)?;
229                }
230
231                TraceOp::Guard {
232                    register,
233                    expected_type,
234                } => {
235                    let guard =
236                        self.compile_guard(*register, *expected_type, guard_index as usize)?;
237                    guards.push(guard);
238                    guard_index += 1;
239                }
240
241                TraceOp::GuardLoopContinue {
242                    condition_register,
243                    bailout_ip,
244                } => {
245                    let guard = self.compile_loop_continue_guard(
246                        *condition_register,
247                        *bailout_ip,
248                        guard_index as usize,
249                    )?;
250                    guards.push(guard);
251                    guard_index += 1;
252                }
253
254                TraceOp::NestedLoopCall {
255                    function_idx,
256                    loop_start_ip,
257                    bailout_ip,
258                } => {
259                    jit::log(|| {
260                        format!(
261                            "🔗 JIT: Nested loop detected at func {} ip {} (guard #{})",
262                            function_idx, loop_start_ip, guard_index
263                        )
264                    });
265                    guards.push(Guard {
266                        index: guard_index as usize,
267                        bailout_ip: *bailout_ip,
268                        kind: GuardKind::NestedLoop {
269                            function_idx: *function_idx,
270                            loop_start_ip: *loop_start_ip,
271                        },
272                        fail_count: 0,
273                        side_trace: None,
274                    });
275                    let current_guard_index = guard_index;
276                    dynasm!(self.ops
277                        ; mov eax, DWORD (current_guard_index + 1)
278                        ; jmp >exit
279                    );
280                    guard_index += 1;
281                }
282
283                TraceOp::Return { .. } => {}
284            }
285        }
286
287        dynasm!(self.ops
288            ; xor eax, eax
289            ; exit:
290            ; pop r15
291            ; pop r14
292            ; pop r13
293            ; pop r12
294            ; pop rbx
295            ; pop rbp
296            ; ret
297            ; fail:
298            ; mov eax, DWORD -1
299            ; jmp <exit
300        );
301        let ops = mem::replace(&mut self.ops, Assembler::new().unwrap());
302        let buffer = ops.finalize().unwrap();
303        let entry_point = buffer.ptr(dynasmrt::AssemblyOffset(0));
304        let entry: extern "C" fn(*mut Value, *mut VM, *const Function) -> i32 =
305            unsafe { mem::transmute(entry_point) };
306        Box::leak(Box::new(buffer));
307        let leaked_constants = mem::take(&mut self.leaked_constants);
308        Ok(CompiledTrace {
309            id: trace_id,
310            entry,
311            trace: trace.clone(),
312            guards,
313            parent,
314            side_traces: Vec::new(),
315            leaked_constants,
316            hoisted_constants,
317        })
318    }
319}