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