1use super::{Capture, CodeUnit, CodeUnitId, DebugInfo, Format, Handler, Insn, Opcode};
8use crate::heap::atom::PropertyKey;
9use crate::vm::ic::{FIELD_NO_SITE, GLOBAL_NO_SITE, IcKind};
10
11#[derive(Default)]
12pub struct CodeBuilder {
13 code: Vec<Insn>,
14 handlers: Vec<Handler>,
15}
16
17impl CodeBuilder {
18 pub fn new() -> CodeBuilder {
19 CodeBuilder::default()
20 }
21
22 #[inline]
24 pub fn pc(&self) -> u32 {
25 self.code.len() as u32
26 }
27
28 #[track_caller]
29 fn push(&mut self, format: Format, insn: Insn) -> u32 {
30 debug_assert_eq!(
31 insn.op.format(),
32 format,
33 "{} built with the wrong format",
34 insn.op.name()
35 );
36 let pc = self.pc();
37 self.code.push(insn);
38 pc
39 }
40
41 pub fn raw(&mut self, insn: Insn) -> u32 {
43 let format = insn.op.format();
44 self.push(format, insn)
45 }
46
47 #[track_caller]
48 pub fn op(&mut self, op: Opcode) -> u32 {
49 self.push(Format::None, Insn::new(op, 0, 0, 0, 0))
50 }
51
52 #[track_caller]
53 pub fn a(&mut self, op: Opcode, a: u8) -> u32 {
54 self.push(Format::A, Insn::new(op, a, 0, 0, 0))
55 }
56
57 #[track_caller]
58 pub fn ab(&mut self, op: Opcode, a: u8, b: u8) -> u32 {
59 self.push(Format::AB, Insn::new(op, a, b, 0, 0))
60 }
61
62 #[track_caller]
63 pub fn abc(&mut self, op: Opcode, a: u8, b: u8, c: u8) -> u32 {
64 self.push(Format::ABC, Insn::new(op, a, b, c, 0))
65 }
66
67 #[track_caller]
68 pub fn a_int(&mut self, op: Opcode, a: u8, value: i32) -> u32 {
69 self.push(Format::AInt, Insn::new(op, a, 0, 0, value as u32))
70 }
71
72 #[track_caller]
73 pub fn ab_int(&mut self, op: Opcode, a: u8, b: u8, value: i32) -> u32 {
74 self.push(Format::ABInt, Insn::new(op, a, b, 0, value as u32))
75 }
76
77 #[track_caller]
78 pub fn a_const(&mut self, op: Opcode, a: u8, index: u32) -> u32 {
79 self.push(Format::AConst, Insn::new(op, a, 0, 0, index))
80 }
81
82 #[track_caller]
83 pub fn key(&mut self, op: Opcode, key: PropertyKey) -> u32 {
84 self.push(Format::Key, Insn::new(op, 0, 0, 0, key.raw()))
85 }
86
87 #[track_caller]
88 pub fn a_key(&mut self, op: Opcode, a: u8, key: PropertyKey) -> u32 {
89 self.push(Format::AKey, Insn::new(op, a, 0, 0, key.raw()))
90 }
91
92 #[track_caller]
93 pub fn ab_key(&mut self, op: Opcode, a: u8, b: u8, key: PropertyKey) -> u32 {
94 self.push(Format::ABKey, Insn::new(op, a, b, 0, key.raw()))
95 }
96
97 #[track_caller]
98 pub fn a_capture(&mut self, op: Opcode, a: u8, capture: u32) -> u32 {
99 self.push(Format::ACapture, Insn::new(op, a, 0, 0, capture))
100 }
101
102 #[track_caller]
103 pub fn a_count(&mut self, op: Opcode, a: u8, count: u32) -> u32 {
104 self.push(Format::ACount, Insn::new(op, a, 0, 0, count))
105 }
106
107 #[track_caller]
108 pub fn ab_range(&mut self, op: Opcode, a: u8, first: u8, count: u32) -> u32 {
109 self.push(Format::ABRange, Insn::new(op, a, first, 0, count))
110 }
111
112 #[track_caller]
113 pub fn a_child(&mut self, op: Opcode, a: u8, child: u32) -> u32 {
114 self.push(Format::AChild, Insn::new(op, a, 0, 0, child))
115 }
116
117 #[track_caller]
118 pub fn call(&mut self, op: Opcode, dst: u8, callee: u8, argc: u8) -> u32 {
119 self.push(Format::Call, Insn::new(op, dst, callee, argc, 0))
120 }
121
122 #[track_caller]
123 pub fn jump(&mut self, op: Opcode, target: u32) -> u32 {
124 self.push(Format::Jump, Insn::new(op, 0, 0, 0, target))
125 }
126
127 #[track_caller]
128 pub fn a_jump(&mut self, op: Opcode, a: u8, target: u32) -> u32 {
129 self.push(Format::AJump, Insn::new(op, a, 0, 0, target))
130 }
131
132 #[track_caller]
133 pub fn ab_jump(&mut self, op: Opcode, a: u8, b: u8, target: u32) -> u32 {
134 self.push(Format::ABJump, Insn::new(op, a, b, 0, target))
135 }
136
137 #[track_caller]
138 pub fn a_short_jump(&mut self, op: Opcode, a: u8, value: i16, target: u32) -> u32 {
139 let [b, c] = value.to_le_bytes();
140 self.push(Format::AShortJump, Insn::new(op, a, b, c, target))
141 }
142
143 #[track_caller]
144 pub fn a_wide(&mut self, op: Opcode, a: u8, register: u32) -> u32 {
145 self.push(Format::AWide, Insn::new(op, a, 0, 0, register))
146 }
147
148 pub fn retarget(&mut self, pc: u32, a: u8) {
151 self.code[pc as usize].a = a;
152 }
153
154 #[track_caller]
157 pub fn patch_target(&mut self, pc: u32, target: u32) {
158 let insn = &mut self.code[pc as usize];
159 assert!(insn.has_target(), "{} has no jump target", insn.op.name());
160 insn.imm = target;
161 }
162
163 pub fn handler(&mut self, handler: Handler) {
166 self.handlers.push(handler);
167 }
168
169 pub fn code(&self) -> &[Insn] {
170 &self.code
171 }
172
173 pub fn mark_back_edges(&mut self) {
177 for (pc, insn) in self.code.iter_mut().enumerate() {
178 if insn.op == Opcode::Jmp && insn.imm as usize <= pc {
179 insn.op = Opcode::JmpBack;
180 }
181 }
182 }
183
184 pub fn finish(mut self, layout: UnitLayout) -> CodeUnit {
188 let ic_count = number_sites(&mut self.code);
189 CodeUnit {
190 code: self.code.into(),
191 handlers: self.handlers.into(),
192 register_count: layout.register_count,
193 parameter_count: layout.parameter_count,
194 local_count: layout.local_count,
195 flags: layout.flags,
196 this_slots: layout.this_slots,
197 constant_count: layout.constant_count,
198 spill_register: layout.spill_register.unwrap_or(super::NO_SPILL),
199 spill_slots: layout.spill_slots,
200 ic_base: 0,
201 ic_count,
202 children: layout.children.into(),
203 captures: layout.captures.into(),
204 debug: DebugInfo {
205 name: layout.name.into(),
206 filename: layout.filename,
207 positions: Box::new([]),
208 definition: None,
209 },
210 name_atom: layout.name_atom,
211 length: layout.length,
212 source: layout.source,
213 direct_eval_sites: Box::new([]),
214 atoms: Box::new([]),
215 }
216 }
217}
218
219fn number_sites(code: &mut [Insn]) -> u32 {
225 let is_field = |insn: &Insn| matches!(IcKind::of(insn.op), Some(IcKind::Get | IcKind::Set));
226 let is_global = |insn: &Insn| IcKind::of(insn.op) == Some(IcKind::Global);
227 let fields = code.iter().filter(|insn| is_field(insn)).count() as u32;
228 let reserved = u32::from(FIELD_NO_SITE);
229 let mut next = 0u32;
230 for insn in code.iter_mut().filter(|insn| is_field(insn)) {
231 insn.c = next.min(reserved) as u8;
232 next += 1;
233 }
234 next = if fields > reserved {
235 reserved + 1
236 } else {
237 fields
238 };
239 let globals = code.iter().filter(|insn| is_global(insn)).count() as u32;
240 let first_global = next;
241 for insn in code.iter_mut().filter(|insn| is_global(insn)) {
242 let site = next.min(GLOBAL_NO_SITE);
243 insn.b = site as u8;
244 insn.c = (site >> 8) as u8;
245 next += 1;
246 }
247 if first_global + globals > GLOBAL_NO_SITE {
248 next = GLOBAL_NO_SITE + 1;
249 }
250 for insn in code.iter_mut().filter(|insn| insn.op == Opcode::New) {
251 insn.imm = next;
252 next += 1;
253 }
254 next
255}
256
257#[derive(Clone, Debug, Default)]
259pub struct UnitLayout {
260 pub register_count: u16,
261 pub parameter_count: u16,
262 pub local_count: u16,
263 pub flags: u16,
264 pub this_slots: u16,
265 pub constant_count: u32,
266 pub spill_register: Option<u16>,
267 pub spill_slots: u16,
268 pub children: Vec<CodeUnitId>,
269 pub captures: Vec<Capture>,
270 pub name: String,
271 pub name_atom: Option<crate::value::StringId>,
273 pub length: u16,
275 pub source: Option<Box<[u8]>>,
279 pub filename: Option<std::rc::Rc<str>>,
281}