1use std::collections::HashSet;
21
22use starlark_syntax::slice_vec_ext::SliceExt;
23
24use crate::collections::Hashed;
25use crate::collections::SmallMap;
26use crate::eval::bc::compiler::if_compiler::write_if_else;
27use crate::eval::bc::instr_impl::*;
28use crate::eval::bc::slow_arg::BcInstrSlowArg;
29use crate::eval::bc::stack_ptr::BcSlot;
30use crate::eval::bc::stack_ptr::BcSlotIn;
31use crate::eval::bc::stack_ptr::BcSlotInRange;
32use crate::eval::bc::stack_ptr::BcSlotOut;
33use crate::eval::bc::writer::BcWriter;
34use crate::eval::compiler::expr::Builtin1;
35use crate::eval::compiler::expr::Builtin2;
36use crate::eval::compiler::expr::CompareOp;
37use crate::eval::compiler::expr::ExprCompiled;
38use crate::eval::compiler::expr::ExprLogicalBinOp;
39use crate::eval::compiler::expr::MaybeNot;
40use crate::eval::compiler::span::IrSpanned;
41use crate::eval::runtime::frame_span::FrameSpan;
42use crate::values::layout::value_not_special::FrozenValueNotSpecial;
43use crate::values::FrozenStringValue;
44use crate::values::FrozenValue;
45use crate::values::ValueLike;
46
47fn try_slot_range<'a>(
49 exprs: impl IntoIterator<Item = &'a IrSpanned<ExprCompiled>>,
50 bc: &BcWriter,
51) -> Option<BcSlotInRange> {
52 let mut range = BcSlotInRange::default();
53 for expr in exprs {
54 let local = expr.as_local_non_captured()?;
55 let slot = bc.try_definitely_assigned(local)?;
56 if !range.try_push(slot) {
57 return None;
58 }
59 }
60 Some(range)
61}
62
63pub(crate) fn write_exprs<'a>(
65 exprs: impl IntoIterator<Item = &'a IrSpanned<ExprCompiled>>,
66 bc: &mut BcWriter,
67 k: impl FnOnce(BcSlotInRange, &mut BcWriter),
68) {
69 let exprs: Vec<_> = exprs.into_iter().collect();
70
71 if let Some(slots) = try_slot_range(exprs.iter().copied(), bc) {
72 k(slots, bc);
73 } else {
74 bc.alloc_slots_for_exprs(exprs, |slot, expr, bc| expr.write_bc(slot.to_out(), bc), k)
75 }
76}
77
78pub(crate) fn write_expr_opt(
79 expr: &Option<IrSpanned<ExprCompiled>>,
80 bc: &mut BcWriter,
81 k: impl FnOnce(Option<BcSlotIn>, &mut BcWriter),
82) {
83 if let Some(expr) = expr {
84 expr.write_bc_cb(bc, |slot, bc| k(Some(slot), bc))
85 } else {
86 k(None, bc)
87 }
88}
89
90pub(crate) fn write_n_exprs<const N: usize>(
91 exprs: [&IrSpanned<ExprCompiled>; N],
92 bc: &mut BcWriter,
93 k: impl FnOnce([BcSlotIn; N], &mut BcWriter),
94) {
95 fn help<const N: usize>(
96 mut filled: [BcSlotIn; N],
97 rem_exprs: &[&IrSpanned<ExprCompiled>],
98 bc: &mut BcWriter,
99 k: impl FnOnce([BcSlotIn; N], &mut BcWriter),
100 ) {
101 match rem_exprs.split_first() {
102 Some((first, rem)) => first.write_bc_cb(bc, |first, bc| {
103 filled[N - rem.len() - 1] = first;
104 help(filled, rem, bc, k)
105 }),
106 None => k(filled, bc),
107 }
108 }
109
110 help([BcSlot(98765).to_in(); N], &exprs, bc, k)
111}
112
113impl ExprCompiled {
114 pub(crate) fn mark_definitely_assigned_after(&self, bc: &mut BcWriter) {
125 match self {
126 ExprCompiled::Value(_) => {}
127 ExprCompiled::Local(local) => bc.mark_definitely_assigned(*local),
128 ExprCompiled::LocalCaptured(_) => {}
129 ExprCompiled::Module(_) => {}
130 ExprCompiled::Tuple(xs) | ExprCompiled::List(xs) => {
131 for x in xs {
132 x.mark_definitely_assigned_after(bc);
133 }
134 }
135 ExprCompiled::Dict(xs) => {
136 for (k, v) in xs {
137 k.mark_definitely_assigned_after(bc);
138 v.mark_definitely_assigned_after(bc);
139 }
140 }
141 ExprCompiled::Compr(compr) => compr.mark_definitely_assigned_after(bc),
142 ExprCompiled::If(c_t_f) => {
143 let (c, _t, _f) = &**c_t_f;
144 c.mark_definitely_assigned_after(bc);
147 }
148 ExprCompiled::Slice(l_a_b_c) => {
149 let (l, a, b, c) = &**l_a_b_c;
150 l.mark_definitely_assigned_after(bc);
151 if let Some(a) = a {
152 a.mark_definitely_assigned_after(bc);
153 }
154 if let Some(b) = b {
155 b.mark_definitely_assigned_after(bc);
156 }
157 if let Some(c) = c {
158 c.mark_definitely_assigned_after(bc);
159 }
160 }
161 ExprCompiled::Builtin1(_op, expr) => {
162 expr.mark_definitely_assigned_after(bc);
163 }
164 ExprCompiled::LogicalBinOp(_op, a_b) => {
165 let (a, b) = &**a_b;
166 a.mark_definitely_assigned_after(bc);
169 let _ = b;
170 }
171 ExprCompiled::Seq(a_b) => {
172 let (a, b) = &**a_b;
173 a.mark_definitely_assigned_after(bc);
174 b.mark_definitely_assigned_after(bc);
175 }
176 ExprCompiled::Builtin2(_op, a_b) => {
177 let (a, b) = &**a_b;
178 a.mark_definitely_assigned_after(bc);
179 b.mark_definitely_assigned_after(bc);
180 }
181 ExprCompiled::Index2(a_i0_i1) => {
182 let (a, i0, i1) = &**a_i0_i1;
183 a.mark_definitely_assigned_after(bc);
184 i0.mark_definitely_assigned_after(bc);
185 i1.mark_definitely_assigned_after(bc);
186 }
187 ExprCompiled::Call(c) => c.mark_definitely_assigned_after(bc),
188 ExprCompiled::Def(d) => d.mark_definitely_assigned_after(bc),
189 }
190 }
191}
192
193impl IrSpanned<ExprCompiled> {
194 fn try_dict_of_consts(
195 xs: &[(IrSpanned<ExprCompiled>, IrSpanned<ExprCompiled>)],
196 ) -> Option<SmallMap<FrozenValue, FrozenValue>> {
197 let mut res = SmallMap::new();
198 for (k, v) in xs {
199 let k = k.as_value()?.get_hashed().ok()?;
200 let v = v.as_value()?;
201 let prev = res.insert_hashed(k, v);
202 if prev.is_some() {
203 return None;
207 }
208 }
209 Some(res)
210 }
211
212 fn try_dict_const_keys(
213 xs: &[(IrSpanned<ExprCompiled>, IrSpanned<ExprCompiled>)],
214 ) -> Option<Box<[Hashed<FrozenValue>]>> {
215 let mut keys = Vec::new();
216 let mut keys_unique = HashSet::new();
217 for (k, _) in xs {
218 let k = k.as_value()?.get_hashed().ok()?;
219 keys.push(k);
220 let inserted = keys_unique.insert(k);
221 if !inserted {
222 return None;
224 }
225 }
226 Some(keys.into_boxed_slice())
227 }
228
229 fn write_dict(
230 span: FrameSpan,
231 xs: &[(IrSpanned<ExprCompiled>, IrSpanned<ExprCompiled>)],
232 target: BcSlotOut,
233 bc: &mut BcWriter,
234 ) {
235 if xs.is_empty() {
236 bc.write_instr::<InstrDictNew>(span, target);
237 } else if let Some(d) = Self::try_dict_of_consts(xs) {
238 bc.write_instr::<InstrDictOfConsts>(span, (d, target));
239 } else if let Some(keys) = Self::try_dict_const_keys(xs) {
240 assert_eq!(keys.len(), xs.len());
241 write_exprs(xs.iter().map(|(_, v)| v), bc, |values, bc| {
242 assert_eq!(values.len() as usize, keys.len());
243 bc.write_instr::<InstrDictConstKeys>(span, (keys, values.to_range_from(), target));
244 });
245 } else {
246 let key_spans = xs.map(|(k, _v)| k.span);
247 write_exprs(xs.iter().flat_map(|(k, v)| [k, v]), bc, |kvs, bc| {
248 bc.write_instr_explicit::<InstrDictNPop>(
249 BcInstrSlowArg {
250 span,
251 spans: key_spans,
252 },
253 (kvs, target),
254 );
255 });
256 }
257 }
258
259 fn write_not(expr: &IrSpanned<ExprCompiled>, target: BcSlotOut, bc: &mut BcWriter) {
260 expr.write_bc_cb(bc, |slot, bc| {
261 bc.write_instr::<InstrNot>(expr.span, (slot, target));
262 });
263 }
264
265 fn write_equals_const(
266 span: FrameSpan,
267 a: &IrSpanned<ExprCompiled>,
268 b: FrozenValue,
269 target: BcSlotOut,
270 bc: &mut BcWriter,
271 ) {
272 a.write_bc_cb(bc, |a, bc| {
273 if let Some(b) = b.to_value().unpack_int_value() {
274 bc.write_instr::<InstrEqInt>(span, (a, b, target));
275 } else if b.eq_is_ptr_eq() {
276 bc.write_instr::<InstrEqPtr>(span, (a, b, target));
277 } else if let Some(b) = FrozenStringValue::new(b) {
278 bc.write_instr::<InstrEqStr>(span, (a, b, target));
279 } else if let Some(b) = FrozenValueNotSpecial::new(b) {
280 bc.write_instr::<InstrEqConst>(span, (a, b, target));
281 } else {
282 unreachable!("FrozenValue must be either i32, str or not-special");
283 }
284 });
285 }
286
287 fn write_equals(
288 span: FrameSpan,
289 a: &IrSpanned<ExprCompiled>,
290 b: &IrSpanned<ExprCompiled>,
291 target: BcSlotOut,
292 bc: &mut BcWriter,
293 ) {
294 if let Some(a) = a.as_value() {
295 Self::write_equals_const(span, b, a, target, bc);
296 } else if let Some(b) = b.as_value() {
297 Self::write_equals_const(span, a, b, target, bc);
298 } else {
299 write_n_exprs([a, b], bc, |[a, b], bc| {
300 bc.write_instr::<InstrEq>(span, (a, b, target));
301 });
302 }
303 }
304
305 pub(crate) fn write_bc(&self, target: BcSlotOut, bc: &mut BcWriter) {
306 let span = self.span;
307 match &self.node {
308 ExprCompiled::Value(v) => {
309 bc.write_const(span, *v, target);
310 }
311 ExprCompiled::Local(slot) => {
312 bc.write_load_local(span, *slot, target);
313 }
314 ExprCompiled::LocalCaptured(slot) => {
315 bc.write_load_local_captured(span, *slot, target);
316 }
317 ExprCompiled::Module(slot) => {
318 bc.write_instr::<InstrLoadModule>(span, (*slot, target));
319 }
320 ExprCompiled::Tuple(xs) => {
321 write_exprs(xs, bc, |xs, bc| {
322 bc.write_instr::<InstrTupleNPop>(span, (xs, target));
323 });
324 }
325 ExprCompiled::List(ref xs) => {
326 if xs.is_empty() {
327 bc.write_instr::<InstrListNew>(span, target);
328 } else if xs.iter().all(|x| x.as_value().is_some()) {
329 let content = xs.map(|v| v.as_value().unwrap()).into_boxed_slice();
330 bc.write_instr::<InstrListOfConsts>(span, (content, target));
331 } else {
332 write_exprs(xs, bc, |xs, bc| {
333 bc.write_instr::<InstrListNPop>(span, (xs, target));
334 });
335 }
336 }
337 ExprCompiled::Dict(ref xs) => Self::write_dict(span, xs, target, bc),
338 ExprCompiled::Compr(ref compr) => compr.write_bc(span, target, bc),
339 ExprCompiled::Slice(l_start_stop_step) => {
340 let (l, start, stop, step) = &**l_start_stop_step;
341 l.write_bc_cb(bc, |l, bc| {
342 write_expr_opt(start, bc, |start, bc| {
343 write_expr_opt(stop, bc, |stop, bc| {
344 write_expr_opt(step, bc, |step, bc| {
345 bc.write_instr::<InstrSlice>(span, (l, start, stop, step, target))
346 })
347 })
348 })
349 });
350 }
351 ExprCompiled::Builtin1(Builtin1::Not, expr) => Self::write_not(expr, target, bc),
352 ExprCompiled::Builtin1(op, expr) => {
353 expr.write_bc_cb(bc, |expr, bc| {
354 let arg = (expr, target);
355 match op {
356 Builtin1::Not => unreachable!("handled above"),
357 Builtin1::Minus => bc.write_instr::<InstrMinus>(span, arg),
358 Builtin1::Plus => bc.write_instr::<InstrPlus>(span, arg),
359 Builtin1::BitNot => bc.write_instr::<InstrBitNot>(span, arg),
360 Builtin1::TypeIs(t) => {
361 bc.write_instr::<InstrTypeIs>(span, (expr, *t, target))
362 }
363 Builtin1::PercentSOne(before, after) => bc
364 .write_instr::<InstrPercentSOne>(span, (*before, expr, *after, target)),
365 Builtin1::FormatOne(before, after) => {
366 bc.write_instr::<InstrFormatOne>(span, (*before, expr, *after, target))
367 }
368 Builtin1::Dot(field) => {
369 bc.write_instr::<InstrObjectField>(span, (expr, field.clone(), target))
370 }
371 }
372 });
373 }
374 ExprCompiled::If(cond_t_f) => {
375 let (cond, t, f) = &**cond_t_f;
376 write_if_else(
377 cond,
378 |bc| t.write_bc(target, bc),
379 |bc| f.write_bc(target, bc),
380 bc,
381 );
382 }
383 ExprCompiled::LogicalBinOp(op, l_r) => {
384 let (l, r) = &**l_r;
385 l.write_bc_cb(bc, |l_slot, bc| {
386 let maybe_not = match op {
387 ExprLogicalBinOp::And => MaybeNot::Id,
388 ExprLogicalBinOp::Or => MaybeNot::Not,
389 };
390 bc.write_if_else(
391 l_slot,
392 maybe_not,
393 l.span,
394 |bc| r.write_bc(target, bc),
395 |bc| bc.write_mov(span, l_slot, target),
396 );
397 });
398 }
399 ExprCompiled::Seq(l_r) => {
400 let (l, r) = &**l_r;
401 l.write_bc_for_effect(bc);
402 r.write_bc(target, bc);
403 }
404 ExprCompiled::Builtin2(Builtin2::Equals, l_r) => {
405 let (l, r) = &**l_r;
406 Self::write_equals(span, l, r, target, bc)
407 }
408 ExprCompiled::Builtin2(op, l_r) => {
409 let (l, r) = &**l_r;
410 write_n_exprs([l, r], bc, |[l, r], bc| {
411 let arg = (l, r, target);
412 match op {
413 Builtin2::Equals => unreachable!("handled above"),
414 Builtin2::Compare(CompareOp::Less) => {
415 bc.write_instr::<InstrLess>(span, arg)
416 }
417 Builtin2::Compare(CompareOp::Greater) => {
418 bc.write_instr::<InstrGreater>(span, arg)
419 }
420 Builtin2::Compare(CompareOp::LessOrEqual) => {
421 bc.write_instr::<InstrLessOrEqual>(span, arg)
422 }
423 Builtin2::Compare(CompareOp::GreaterOrEqual) => {
424 bc.write_instr::<InstrGreaterOrEqual>(span, arg)
425 }
426 Builtin2::In => bc.write_instr::<InstrIn>(span, arg),
427 Builtin2::Sub => bc.write_instr::<InstrSub>(span, arg),
428 Builtin2::Add => bc.write_instr::<InstrAdd>(span, arg),
429 Builtin2::Multiply => bc.write_instr::<InstrMultiply>(span, arg),
430 Builtin2::Divide => bc.write_instr::<InstrDivide>(span, arg),
431 Builtin2::FloorDivide => bc.write_instr::<InstrFloorDivide>(span, arg),
432 Builtin2::Percent => bc.write_instr::<InstrPercent>(span, arg),
433 Builtin2::BitAnd => bc.write_instr::<InstrBitAnd>(span, arg),
434 Builtin2::BitOr => bc.write_instr::<InstrBitOr>(span, arg),
435 Builtin2::BitXor => bc.write_instr::<InstrBitXor>(span, arg),
436 Builtin2::LeftShift => bc.write_instr::<InstrLeftShift>(span, arg),
437 Builtin2::RightShift => bc.write_instr::<InstrRightShift>(span, arg),
438 Builtin2::ArrayIndex => bc.write_instr::<InstrArrayIndex>(span, arg),
439 }
440 });
441 }
442 ExprCompiled::Index2(a_i0_i1) => {
443 let (a, i0, i1) = &**a_i0_i1;
444 write_n_exprs([a, i0, i1], bc, |[a, i0, i1], bc| {
445 bc.write_instr::<InstrArrayIndex2>(span, (a, i0, i1, target))
446 });
447 }
448 ExprCompiled::Call(ref call) => call.write_bc(target, bc),
449 ExprCompiled::Def(ref def) => def.write_bc(span, target, bc),
450 }
451 }
452
453 pub(crate) fn write_bc_cb<R>(
456 &self,
457 bc: &mut BcWriter,
458 k: impl FnOnce(BcSlotIn, &mut BcWriter) -> R,
459 ) -> R {
460 if let Some(local) = self.as_local_non_captured() {
461 if let Some(slot) = bc.try_definitely_assigned(local) {
464 return k(slot, bc);
465 }
466 }
467
468 bc.alloc_slot(|slot, bc| {
469 self.write_bc(slot.to_out(), bc);
470 k(slot.to_in(), bc)
471 })
472 }
473
474 pub(crate) fn write_bc_for_effect(&self, bc: &mut BcWriter) {
475 self.write_bc_cb(bc, |slot, _bc| {
476 let _ = slot;
477 });
478 }
479}