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}