1use std::collections::HashMap;
12use std::mem::{offset_of, size_of};
13
14use cranelift_codegen::ir::condcodes::{FloatCC, IntCC};
15use cranelift_codegen::ir::types::{F64, I8, I32, I64};
16use cranelift_codegen::ir::{
17 Block, InstBuilder, MemFlags, StackSlot, StackSlotData, StackSlotKind, Value,
18};
19use cranelift_frontend::{FunctionBuilder, Variable};
20use cranelift_module::{DataId, FuncId, Module};
21use nifty_check::hir::{
22 BinaryOp, Block as Stmts, Dialog, Expr, ExprKind, Iterable, LocalId, Loop, Place, PlaceRoot,
23 PlaceStep, Segment, StateId, Stmt, Type, WindowAction, WindowId,
24};
25use nifty_rt::{PathStep, PlaceDesc, StepKind};
26use nifty_syntax::Span;
27
28mod download;
29
30use super::Shared;
31use super::types::is_object;
32use crate::CodegenError;
33
34const SLOT: i64 = 8;
36
37pub(super) type Result<T> = std::result::Result<T, CodegenError>;
38
39#[derive(Clone, Copy, Debug)]
41pub(super) struct Operand {
42 pub value: Value,
43 pub owned: bool,
44}
45
46impl Operand {
47 pub fn owned(value: Value) -> Self {
48 Self { value, owned: true }
49 }
50
51 pub fn borrowed(value: Value) -> Self {
52 Self {
53 value,
54 owned: false,
55 }
56 }
57}
58
59#[derive(Clone, Debug)]
62pub(super) struct Alias {
63 pub root: Root,
64 pub steps: Vec<Step>,
65}
66
67#[derive(Clone, Copy, Debug)]
68pub(super) enum Root {
69 State(StateId),
70 Local(usize),
72}
73
74#[derive(Clone, Copy, Debug)]
75pub(super) enum Step {
76 Field(usize),
77 Index(Index),
78}
79
80#[derive(Clone, Copy, Debug)]
82pub(super) enum Index {
83 Value(Value),
85 Var(Variable),
87 Slot(usize),
89}
90
91#[derive(Clone, Copy, Debug)]
93pub(super) struct Returns {
94 pub out: Value,
96}
97
98pub(super) struct Body<'a, 'b, 'p, M: Module> {
99 pub b: &'a mut FunctionBuilder<'b>,
100 pub module: &'a mut M,
101 pub shared: &'a mut Shared<'p>,
102 pub context: Value,
103 pub frame: Value,
105 pub locals: &'a [Type],
107 pub aliases: HashMap<LocalId, Alias>,
108 pub returns: Option<Returns>,
109 fail: Option<Block>,
110 scratch: Option<StackSlot>,
112 temporaries: Vec<(Value, Type)>,
115}
116
117impl<'a, 'b, 'p, M: Module> Body<'a, 'b, 'p, M> {
118 pub fn new(
119 b: &'a mut FunctionBuilder<'b>,
120 module: &'a mut M,
121 shared: &'a mut Shared<'p>,
122 context: Value,
123 frame: Value,
124 locals: &'a [Type],
125 ) -> Self {
126 Self {
127 b,
128 module,
129 shared,
130 context,
131 frame,
132 locals,
133 aliases: HashMap::new(),
134 returns: None,
135 fail: None,
136 scratch: None,
137 temporaries: Vec::new(),
138 }
139 }
140
141 pub fn finish(mut self) {
143 self.succeed();
144 if let Some(fail) = self.fail {
145 self.b.switch_to_block(fail);
146 let failed = self.b.ins().iconst(I8, 0);
147 self.b.ins().return_(&[failed]);
148 }
149 }
150
151 pub fn succeed_here(&mut self) {
153 self.succeed();
154 self.continue_after_terminator();
155 }
156
157 fn succeed(&mut self) {
158 let succeeded = self.b.ins().iconst(I8, 1);
159 self.b.ins().return_(&[succeeded]);
160 }
161
162 fn continue_after_terminator(&mut self) {
164 let next = self.b.create_block();
165 self.b.switch_to_block(next);
166 }
167
168 pub fn call(&mut self, function: FuncId, args: &[Value]) -> Vec<Value> {
171 let callee = self.module.declare_func_in_func(function, self.b.func);
172 let call = self.b.ins().call(callee, args);
173 self.b.inst_results(call).to_vec()
174 }
175
176 pub fn call1(&mut self, function: FuncId, args: &[Value]) -> Value {
177 self.call(function, args)[0]
178 }
179
180 pub fn call_checked(&mut self, function: FuncId, args: &[Value]) {
182 let ok = self.call1(function, args);
183 self.check(ok);
184 }
185
186 fn check(&mut self, ok: Value) {
187 let fail = self.fail_block();
188 let next = self.b.create_block();
189 self.b.ins().brif(ok, next, &[], fail, &[]);
190 self.b.switch_to_block(next);
191 }
192
193 fn fail_block(&mut self) -> Block {
194 *self.fail.get_or_insert_with(|| self.b.create_block())
195 }
196
197 fn fail_if(&mut self, condition: Value, failure: FuncId, span: Span) -> Result<()> {
199 let failing = self.b.create_block();
200 let next = self.b.create_block();
201 self.b.ins().brif(condition, failing, &[], next, &[]);
202 self.b.switch_to_block(failing);
203 let context = self.context;
204 let span = self.span(span)?;
205 self.call(failure, &[context, span]);
206 let fail = self.fail_block();
207 self.b.ins().jump(fail, &[]);
208 self.b.switch_to_block(next);
209 Ok(())
210 }
211
212 pub fn span(&mut self, span: Span) -> Result<Value> {
213 let id = self.shared.spans.id(span)?;
214 Ok(self.b.ins().iconst(I32, i64::from(id)))
215 }
216
217 pub fn u32(&mut self, value: usize) -> Result<Value> {
218 let value = u32::try_from(value)
219 .map_err(|_| CodegenError::new(format!("{value} is too large for an id")))?;
220 Ok(self.b.ins().iconst(I32, i64::from(value)))
221 }
222
223 fn usize(&mut self, value: usize) -> Result<Value> {
224 let value =
225 i64::try_from(value).map_err(|_| CodegenError::new(format!("{value} is too large")))?;
226 Ok(self.b.ins().iconst(I64, value))
227 }
228
229 pub fn address(&mut self, data: DataId) -> Value {
230 let global = self.module.declare_data_in_func(data, self.b.func);
231 self.b.ins().global_value(I64, global)
232 }
233
234 pub fn desc(&mut self, ty: &Type) -> Result<Value> {
235 let shared = &mut *self.shared;
236 let id = shared.types.desc(self.module, &shared.runtime, ty)?;
237 Ok(self.address(id))
238 }
239
240 fn stack(&mut self, size: usize) -> Result<Value> {
242 let size = u32::try_from(size)
243 .map_err(|_| CodegenError::new("a function needs too much stack"))?;
244 let slot = self.b.create_sized_stack_slot(StackSlotData::new(
245 StackSlotKind::ExplicitSlot,
246 size,
247 3,
248 ));
249 Ok(self.b.ins().stack_addr(I64, slot, 0))
250 }
251
252 fn scratch(&mut self) -> Value {
254 let slot = *self.scratch.get_or_insert_with(|| {
255 self.b
256 .create_sized_stack_slot(StackSlotData::new(StackSlotKind::ExplicitSlot, 8, 3))
257 });
258 self.b.ins().stack_addr(I64, slot, 0)
259 }
260
261 fn array(&mut self, values: &[Value]) -> Result<Value> {
263 if values.is_empty() {
264 return Ok(self.b.ins().iconst(I64, 0));
265 }
266 let array = self.stack(values.len() * 8)?;
267 for (index, value) in values.iter().enumerate() {
268 let offset = offset(index * 8)?;
269 self.b
270 .ins()
271 .store(MemFlags::trusted(), *value, array, offset);
272 }
273 Ok(array)
274 }
275
276 fn load(&mut self, address: Value) -> Value {
277 self.b.ins().load(I64, MemFlags::trusted(), address, 0)
278 }
279
280 pub fn own(&mut self, operand: Operand, ty: &Type) -> Result<Value> {
284 if !operand.owned && is_object(ty) {
285 let desc = self.desc(ty)?;
286 let retain = self.shared.runtime.retain;
287 self.call(retain, &[operand.value, desc]);
288 }
289 Ok(operand.value)
290 }
291
292 pub fn discard(&mut self, operand: Operand, ty: &Type) -> Result<()> {
294 if operand.owned && is_object(ty) {
295 self.release(operand.value, ty)?;
296 }
297 Ok(())
298 }
299
300 pub fn release(&mut self, value: Value, ty: &Type) -> Result<()> {
301 let desc = self.desc(ty)?;
302 let release = self.shared.runtime.release;
303 self.call(release, &[value, desc]);
304 Ok(())
305 }
306
307 pub fn store(&mut self, place: Value, value: Value, ty: &Type) -> Result<()> {
310 if is_object(ty) {
311 let desc = self.desc(ty)?;
312 let store = self.shared.runtime.store;
313 self.call(store, &[place, value, desc]);
314 } else {
315 self.b.ins().store(MemFlags::trusted(), value, place, 0);
316 }
317 Ok(())
318 }
319
320 pub fn slot(&mut self, local: usize) -> Result<Value> {
321 let offset = i64::try_from(local)
322 .ok()
323 .and_then(|local| local.checked_mul(SLOT))
324 .ok_or_else(|| CodegenError::new("a frame has too many locals"))?;
325 Ok(self.b.ins().iadd_imm(self.frame, offset))
326 }
327
328 pub fn release_locals(&mut self) -> Result<()> {
330 for (local, ty) in self.locals.iter().enumerate() {
331 if is_object(ty) {
332 let slot = self.slot(local)?;
333 let value = self.load(slot);
334 self.release(value, ty)?;
335 }
336 }
337 Ok(())
338 }
339
340 pub fn path(&mut self, place: &Place) -> Result<Alias> {
344 let mut steps = Vec::new();
345 for step in &place.path {
346 steps.push(match step {
347 PlaceStep::Field(index) => Step::Field(*index),
348 PlaceStep::Index(index) => Step::Index(Index::Value(self.int(index)?)),
349 });
350 }
351 let mut path = match place.root {
352 PlaceRoot::State(state) => Alias {
353 root: Root::State(state),
354 steps: Vec::new(),
355 },
356 PlaceRoot::Local(local) => self.aliases.get(&local).cloned().unwrap_or(Alias {
357 root: Root::Local(local.0),
358 steps: Vec::new(),
359 }),
360 };
361 path.steps.extend(steps);
362 Ok(path)
363 }
364
365 fn index(&mut self, index: Index) -> Result<Value> {
366 Ok(match index {
367 Index::Value(value) => value,
368 Index::Var(variable) => self.b.use_var(variable),
369 Index::Slot(local) => {
370 let at = offset(local * 8)?;
371 self.b.ins().load(I64, MemFlags::trusted(), self.frame, at)
372 }
373 })
374 }
375
376 pub fn pointer(&mut self, path: &Alias, span: Span) -> Result<Value> {
379 let mut pointer = match path.root {
380 Root::State(state) => {
381 let state = self.u32(state.0)?;
382 let state_mut = self.shared.runtime.state_mut;
383 self.call1(state_mut, &[self.context, state])
384 }
385 Root::Local(local) => self.slot(local)?,
386 };
387 for step in &path.steps {
388 pointer = match *step {
389 Step::Field(field) => {
390 let field = self.usize(field)?;
391 let field_mut = self.shared.runtime.struct_field_mut;
392 self.call1(field_mut, &[pointer, field])
393 }
394 Step::Index(index) => {
395 let index = self.index(index)?;
396 let out = self.scratch();
397 let span = self.span(span)?;
398 let item_mut = self.shared.runtime.list_item_mut;
399 self.call_checked(item_mut, &[self.context, pointer, index, span, out]);
400 self.load(out)
401 }
402 };
403 }
404 Ok(pointer)
405 }
406
407 pub fn read(&mut self, path: &Alias, span: Span) -> Result<Value> {
409 let mut value = match path.root {
410 Root::State(state) => {
411 let state = self.u32(state.0)?;
412 let read = self.shared.runtime.state;
413 self.call1(read, &[self.context, state])
414 }
415 Root::Local(local) => {
416 let slot = self.slot(local)?;
417 self.load(slot)
418 }
419 };
420 for step in &path.steps {
421 value = match *step {
422 Step::Field(field) => {
423 let field = self.usize(field)?;
424 let get = self.shared.runtime.struct_get;
425 self.call1(get, &[value, field])
426 }
427 Step::Index(index) => {
428 let index = self.index(index)?;
429 self.list_item(value, index, span)?
430 }
431 };
432 }
433 Ok(value)
434 }
435
436 pub fn list_item(&mut self, list: Value, index: Value, span: Span) -> Result<Value> {
438 let out = self.scratch();
439 let span = self.span(span)?;
440 let get = self.shared.runtime.list_get;
441 self.call_checked(get, &[self.context, list, index, span, out]);
442 Ok(self.load(out))
443 }
444
445 pub fn block(&mut self, stmts: &Stmts) -> Result<()> {
448 stmts.iter().try_for_each(|stmt| self.stmt(stmt))
449 }
450
451 fn stmt(&mut self, stmt: &Stmt) -> Result<()> {
452 match stmt {
453 Stmt::Let(local, value) => {
454 let operand = self.expr(value)?;
455 let value_owned = self.own(operand, &value.ty)?;
456 let slot = self.slot(local.0)?;
457 self.store(slot, value_owned, &value.ty)
458 }
459 Stmt::Assign { place, op, value } => self.assign(place, *op, value),
460 Stmt::If {
461 branches,
462 otherwise,
463 } => self.if_stmt(branches, otherwise.as_ref()),
464 Stmt::For { head, body } => match &head.iterable {
465 Iterable::Value(list) => self.for_value(head, list, body),
466 Iterable::Place(place) => self.for_place(head, place, body),
467 Iterable::Range { start, end } => self.for_range(head.item, start, end, body),
468 },
469 Stmt::Return(value) => self.return_stmt(value.as_ref()),
470 Stmt::Expr(expr) => {
471 let operand = self.expr(expr)?;
472 self.discard(operand, &expr.ty)
473 }
474 }
475 }
476
477 fn assign(&mut self, place: &Place, op: Option<BinaryOp>, value: &Expr) -> Result<()> {
479 let operand = self.expr(value)?;
480 let value_owned = self.own(operand, &value.ty)?;
481 let path = self.path(place)?;
482 let pointer = self.pointer(&path, place.span)?;
483 let result = match op {
484 Some(op) => {
485 let current = Operand::borrowed(self.load(pointer));
486 let result = self.apply(
487 op,
488 &place.ty,
489 current,
490 Operand::owned(value_owned),
491 place.span,
492 )?;
493 let result_ty = op_type(op, &place.ty);
494 self.own(result, &result_ty)?
495 }
496 None => value_owned,
497 };
498 self.store(pointer, result, &place.ty)
499 }
500
501 fn if_stmt(&mut self, branches: &[(Expr, Stmts)], otherwise: Option<&Stmts>) -> Result<()> {
502 let done = self.b.create_block();
503 for (condition, body) in branches {
504 let condition = self.expr(condition)?.value;
505 let then = self.b.create_block();
506 let next = self.b.create_block();
507 self.b.ins().brif(condition, then, &[], next, &[]);
508 self.b.switch_to_block(then);
509 self.block(body)?;
510 self.b.ins().jump(done, &[]);
511 self.b.switch_to_block(next);
512 }
513 if let Some(otherwise) = otherwise {
514 self.block(otherwise)?;
515 }
516 self.b.ins().jump(done, &[]);
517 self.b.switch_to_block(done);
518 Ok(())
519 }
520
521 fn for_value(&mut self, head: &Loop, list: &Expr, body: &Stmts) -> Result<()> {
523 let operand = self.expr(list)?;
524 let list_owned = self.own(operand, &list.ty)?;
525 self.temporaries.push((list_owned, list.ty.clone()));
526 let element = element_type(&list.ty)?;
527 let len_fn = self.shared.runtime.list_len;
528 let len = self.call1(len_fn, &[list_owned]);
529 self.count_up(len, |this, index| {
530 let item = this.list_item(list_owned, index, list.span)?;
531 let item = this.own(Operand::borrowed(item), &element)?;
532 let slot = this.slot(head.item.0)?;
533 this.store(slot, item, &element)?;
534 this.set_index(head.index, index)?;
535 this.block(body)
536 })?;
537 self.temporaries.pop();
538 self.release(list_owned, &list.ty)
539 }
540
541 fn for_place(&mut self, head: &Loop, place: &Place, body: &Stmts) -> Result<()> {
544 let path = self.path(place)?;
545 let counter = self.b.declare_var(I64);
546 let mut item = path.clone();
547 item.steps.push(Step::Index(Index::Var(counter)));
548 self.aliases.insert(head.item, item);
549 let zero = self.b.ins().iconst(I64, 0);
550 let len_fn = self.shared.runtime.list_len;
551 self.count_with(
552 counter,
553 zero,
554 |this| {
555 let list = this.read(&path, place.span)?;
556 Ok(this.call1(len_fn, &[list]))
557 },
558 |this, index| {
559 this.set_index(head.index, index)?;
560 this.block(body)
561 },
562 )?;
563 self.aliases.remove(&head.item);
564 Ok(())
565 }
566
567 fn for_range(&mut self, local: LocalId, start: &Expr, end: &Expr, body: &Stmts) -> Result<()> {
569 let start = self.int(start)?;
570 let end = self.int(end)?;
571 self.count(start, end, |this, count| {
572 let slot = this.slot(local.0)?;
573 this.store(slot, count, &Type::Int)?;
574 this.block(body)
575 })
576 }
577
578 fn set_index(&mut self, index: Option<LocalId>, position: Value) -> Result<()> {
580 let Some(index) = index else {
581 return Ok(());
582 };
583 let slot = self.slot(index.0)?;
584 self.store(slot, position, &Type::Int)
585 }
586
587 pub fn count_up(
589 &mut self,
590 len: Value,
591 step: impl FnMut(&mut Self, Value) -> Result<()>,
592 ) -> Result<()> {
593 let zero = self.b.ins().iconst(I64, 0);
594 self.count(zero, len, step)
595 }
596
597 pub fn count(
599 &mut self,
600 start: Value,
601 end: Value,
602 step: impl FnMut(&mut Self, Value) -> Result<()>,
603 ) -> Result<()> {
604 let counter = self.b.declare_var(I64);
605 self.count_with(counter, start, |_| Ok(end), step)
606 }
607
608 fn count_with(
611 &mut self,
612 counter: Variable,
613 start: Value,
614 mut end: impl FnMut(&mut Self) -> Result<Value>,
615 mut step: impl FnMut(&mut Self, Value) -> Result<()>,
616 ) -> Result<()> {
617 self.b.def_var(counter, start);
618 let header = self.b.create_block();
619 let looping = self.b.create_block();
620 let done = self.b.create_block();
621 self.b.ins().jump(header, &[]);
622 self.b.switch_to_block(header);
623 let end = end(self)?;
624 let count = self.b.use_var(counter);
625 let ended = self
626 .b
627 .ins()
628 .icmp(IntCC::SignedGreaterThanOrEqual, count, end);
629 self.b.ins().brif(ended, done, &[], looping, &[]);
630 self.b.switch_to_block(looping);
631 let count = self.b.use_var(counter);
632 step(self, count)?;
633 let count = self.b.use_var(counter);
634 let next = self.b.ins().iadd_imm(count, 1);
635 self.b.def_var(counter, next);
636 self.b.ins().jump(header, &[]);
637 self.b.switch_to_block(done);
638 Ok(())
639 }
640
641 fn return_stmt(&mut self, value: Option<&Expr>) -> Result<()> {
642 let returns = self
643 .returns
644 .ok_or_else(|| CodegenError::new("`return` outside a function"))?;
645 if let Some(value) = value {
646 let operand = self.expr(value)?;
647 let result = self.own(operand, &value.ty)?;
648 self.b
649 .ins()
650 .store(MemFlags::trusted(), result, returns.out, 0);
651 }
652 for (temporary, ty) in self.temporaries.clone().iter().rev() {
653 self.release(*temporary, ty)?;
654 }
655 self.release_locals()?;
656 self.succeed();
657 self.continue_after_terminator();
658 Ok(())
659 }
660
661 fn int(&mut self, expr: &Expr) -> Result<Value> {
665 Ok(self.expr(expr)?.value)
666 }
667
668 pub fn expr(&mut self, expr: &Expr) -> Result<Operand> {
669 let span = expr.span;
670 Ok(match &expr.kind {
671 ExprKind::Int(value) => Operand::owned(self.b.ins().iconst(I64, *value)),
672 ExprKind::Float(value) => {
673 let bits = i64::from_ne_bytes(value.to_bits().to_ne_bytes());
674 Operand::owned(self.b.ins().iconst(I64, bits))
675 }
676 ExprKind::Bool(value) => Operand::owned(self.b.ins().iconst(I64, i64::from(*value))),
677 ExprKind::String(text) => Operand::owned(self.string(text)?),
678 ExprKind::Interpolate(segments) => Operand::owned(self.interpolate(segments)?),
679 ExprKind::State(state) => {
680 let path = Alias {
681 root: Root::State(*state),
682 steps: Vec::new(),
683 };
684 Operand::borrowed(self.read(&path, span)?)
685 }
686 ExprKind::Local(local) => {
687 let path = self.aliases.get(local).cloned().unwrap_or(Alias {
688 root: Root::Local(local.0),
689 steps: Vec::new(),
690 });
691 Operand::borrowed(self.read(&path, span)?)
692 }
693 ExprKind::List(items) => Operand::owned(self.aggregate(items, &expr.ty)?),
694 ExprKind::Construct(_, fields) => Operand::owned(self.aggregate(fields, &expr.ty)?),
695 ExprKind::Field(base, field) => {
696 let object = self.expr(base)?;
697 let field = self.usize(*field)?;
698 let get = self.shared.runtime.struct_get;
699 let value = self.call1(get, &[object.value, field]);
700 self.part_of(object, &base.ty, value, &expr.ty)?
701 }
702 ExprKind::Index(base, index) => {
703 let list = self.expr(base)?;
704 let list = self.hold(list, &base.ty, index)?;
705 let index = self.int(index)?;
706 let item = self.list_item(list.value, index, span)?;
707 self.part_of(list, &base.ty, item, &expr.ty)?
708 }
709 ExprKind::Not(operand) => {
710 let value = self.int(operand)?;
711 let not = self.b.ins().icmp_imm(IntCC::Equal, value, 0);
712 Operand::owned(self.b.ins().uextend(I64, not))
713 }
714 ExprKind::Negate(operand) => {
715 let value = self.int(operand)?;
716 Operand::owned(if operand.ty == Type::Float {
717 let float = self.float(value);
718 let negated = self.b.ins().fneg(float);
719 self.bits(negated)
720 } else {
721 let overflows = self.b.ins().icmp_imm(IntCC::Equal, value, i64::MIN);
722 let too_large = self.shared.runtime.fail_too_large;
723 self.fail_if(overflows, too_large, span)?;
724 self.b.ins().ineg(value)
725 })
726 }
727 ExprKind::Binary(op, lhs, rhs) => self.binary(*op, lhs, rhs, span)?,
728 ExprKind::Call(function, args) => self.call_fn(function.0, args, &expr.ty, span)?,
729 ExprKind::Len(value) => Operand::owned(self.len(value)?),
730 ExprKind::IsEmpty(value) => {
731 let len = self.len(value)?;
732 let empty = self.b.ins().icmp_imm(IntCC::Equal, len, 0);
733 Operand::owned(self.b.ins().uextend(I64, empty))
734 }
735 ExprKind::Push(place, value) => {
736 self.push(place, value)?;
737 self.nothing()
738 }
739 ExprKind::Remove(place, index) => {
740 self.remove(place, index, span)?;
741 self.nothing()
742 }
743 ExprKind::Download(download) => {
744 self.download(download, span)?;
745 self.nothing()
746 }
747 ExprKind::FormatBytes(bytes) => Operand::owned(self.format_bytes(bytes)?),
748 ExprKind::DownloadCommand(command, id) => {
749 self.download_command(*command, id)?;
750 self.nothing()
751 }
752 ExprKind::Dialog(dialog) => {
753 self.dialog(dialog, span)?;
754 self.nothing()
755 }
756 ExprKind::Window(action, window) => {
757 self.window_request(*action, *window)?;
758 self.nothing()
759 }
760 ExprKind::Error => {
761 return Err(CodegenError::new("programs with errors can't be compiled"));
762 }
763 })
764 }
765
766 fn nothing(&mut self) -> Operand {
768 Operand::owned(self.b.ins().iconst(I64, 0))
769 }
770
771 fn aggregate(&mut self, parts: &[Expr], ty: &Type) -> Result<Value> {
773 let values = self.owned_all(parts)?;
774 let array = self.array(&values)?;
775 let desc = self.desc(ty)?;
776 Ok(if let Type::List(_) = ty {
777 let len = self.usize(values.len())?;
778 let new = self.shared.runtime.list_new;
779 self.call1(new, &[desc, array, len])
780 } else {
781 let new = self.shared.runtime.struct_new;
782 self.call1(new, &[desc, array])
783 })
784 }
785
786 fn push(&mut self, place: &Place, value: &Expr) -> Result<()> {
788 let operand = self.expr(value)?;
789 let item = self.own(operand, &value.ty)?;
790 let path = self.path(place)?;
791 let list = self.pointer(&path, place.span)?;
792 let push = self.shared.runtime.list_push;
793 self.call(push, &[list, item]);
794 Ok(())
795 }
796
797 fn remove(&mut self, place: &Place, index: &Expr, span: Span) -> Result<()> {
798 let index = self.int(index)?;
799 let path = self.path(place)?;
800 let list = self.pointer(&path, place.span)?;
801 let span = self.span(span)?;
802 let remove = self.shared.runtime.list_remove;
803 self.call_checked(remove, &[self.context, list, index, span]);
804 Ok(())
805 }
806
807 fn owned_all(&mut self, exprs: &[Expr]) -> Result<Vec<Value>> {
809 exprs
810 .iter()
811 .map(|expr| {
812 let operand = self.expr(expr)?;
813 self.own(operand, &expr.ty)
814 })
815 .collect()
816 }
817
818 fn hold(&mut self, operand: Operand, ty: &Type, next: &Expr) -> Result<Operand> {
820 self.hold_across(operand, ty, [next])
821 }
822
823 fn hold_across<'e>(
826 &mut self,
827 operand: Operand,
828 ty: &Type,
829 later: impl IntoIterator<Item = &'e Expr>,
830 ) -> Result<Operand> {
831 if operand.owned || later.into_iter().all(is_pure) {
832 Ok(operand)
833 } else {
834 Ok(Operand::owned(self.own(operand, ty)?))
835 }
836 }
837
838 fn part_of(
841 &mut self,
842 container: Operand,
843 container_ty: &Type,
844 part: Value,
845 part_ty: &Type,
846 ) -> Result<Operand> {
847 if !container.owned {
848 return Ok(Operand::borrowed(part));
849 }
850 let part = self.own(Operand::borrowed(part), part_ty)?;
851 self.release(container.value, container_ty)?;
852 Ok(Operand::owned(part))
853 }
854
855 fn string(&mut self, text: &str) -> Result<Value> {
856 let data = self.shared.text(self.module, text)?;
857 let bytes = self.address(data);
858 let len = self.usize(text.len())?;
859 let new = self.shared.runtime.string_new;
860 Ok(self.call1(new, &[bytes, len]))
861 }
862
863 fn interpolate(&mut self, segments: &[Segment]) -> Result<Value> {
864 let new = self.shared.runtime.text_new;
865 let text = self.call1(new, &[]);
866 for segment in segments {
867 match segment {
868 Segment::Text(part) => {
869 let data = self.shared.text(self.module, part)?;
870 let bytes = self.address(data);
871 let len = self.usize(part.len())?;
872 let push = self.shared.runtime.text_push_str;
873 self.call(push, &[text, bytes, len]);
874 }
875 Segment::Value(value) => {
876 let operand = self.expr(value)?;
877 let desc = self.desc(&value.ty)?;
878 let push = self.shared.runtime.text_push;
879 self.call(push, &[text, operand.value, desc]);
880 self.discard(operand, &value.ty)?;
881 }
882 }
883 }
884 let finish = self.shared.runtime.text_finish;
885 Ok(self.call1(finish, &[text]))
886 }
887
888 fn len(&mut self, value: &Expr) -> Result<Value> {
889 let operand = self.expr(value)?;
890 let len_fn = if value.ty == Type::String {
891 self.shared.runtime.string_len
892 } else {
893 self.shared.runtime.list_len
894 };
895 let len = self.call1(len_fn, &[operand.value]);
896 self.discard(operand, &value.ty)?;
897 Ok(len)
898 }
899
900 fn float(&mut self, bits: Value) -> Value {
901 self.b.ins().bitcast(F64, MemFlags::new(), bits)
902 }
903
904 fn bits(&mut self, float: Value) -> Value {
905 self.b.ins().bitcast(I64, MemFlags::new(), float)
906 }
907
908 fn binary(&mut self, op: BinaryOp, lhs: &Expr, rhs: &Expr, span: Span) -> Result<Operand> {
909 if matches!(op, BinaryOp::And | BinaryOp::Or) {
910 return self.short_circuit(op, lhs, rhs);
911 }
912 let left = self.expr(lhs)?;
913 let left = self.hold(left, &lhs.ty, rhs)?;
914 let right = self.expr(rhs)?;
915 self.apply(op, &lhs.ty, left, right, span)
916 }
917
918 fn short_circuit(&mut self, op: BinaryOp, lhs: &Expr, rhs: &Expr) -> Result<Operand> {
919 let left = self.int(lhs)?;
920 let right_block = self.b.create_block();
921 let done = self.b.create_block();
922 self.b.append_block_param(done, I64);
923 if op == BinaryOp::And {
924 self.b
925 .ins()
926 .brif(left, right_block, &[], done, &[left.into()]);
927 } else {
928 self.b
929 .ins()
930 .brif(left, done, &[left.into()], right_block, &[]);
931 }
932 self.b.switch_to_block(right_block);
933 let right = self.int(rhs)?;
934 self.b.ins().jump(done, &[right.into()]);
935 self.b.switch_to_block(done);
936 Ok(Operand::owned(self.b.block_params(done)[0]))
937 }
938
939 pub fn apply(
941 &mut self,
942 op: BinaryOp,
943 ty: &Type,
944 left: Operand,
945 right: Operand,
946 span: Span,
947 ) -> Result<Operand> {
948 let (a, b) = (left.value, right.value);
949 let result = match (op, ty) {
950 (BinaryOp::Eq | BinaryOp::NotEq, ty) if is_object(ty) => {
951 let desc = self.desc(ty)?;
952 let equal_fn = self.shared.runtime.equal;
953 let equal = self.call1(equal_fn, &[a, b, desc]);
954 let equal = self.b.ins().uextend(I64, equal);
955 if op == BinaryOp::Eq {
956 equal
957 } else {
958 self.b.ins().bxor_imm(equal, 1)
959 }
960 }
961 (BinaryOp::Add, Type::String) => {
962 let concat = self.shared.runtime.string_concat;
963 self.call1(concat, &[a, b])
964 }
965 (_, Type::String) => {
966 let compare_fn = self.shared.runtime.string_compare;
967 let order = self.call1(compare_fn, &[a, b]);
968 let holds = self.b.ins().icmp_imm(int_condition(op)?, order, 0);
969 self.b.ins().uextend(I64, holds)
970 }
971 (_, Type::Float) => self.float_op(op, a, b)?,
972 (_, Type::Int | Type::Bool) => self.int_op(op, a, b, span)?,
973 (op, ty) => {
974 return Err(CodegenError::new(format!("can't apply {op:?} to {ty:?}")));
975 }
976 };
977 self.discard(left, ty)?;
978 self.discard(right, ty)?;
979 Ok(Operand::owned(result))
980 }
981
982 fn int_op(&mut self, op: BinaryOp, a: Value, b: Value, span: Span) -> Result<Value> {
983 let too_large = self.shared.runtime.fail_too_large;
984 Ok(match op {
985 BinaryOp::Add | BinaryOp::Sub | BinaryOp::Mul => {
986 let (result, overflowed) = match op {
987 BinaryOp::Add => self.b.ins().sadd_overflow(a, b),
988 BinaryOp::Sub => self.b.ins().ssub_overflow(a, b),
989 _ => self.b.ins().smul_overflow(a, b),
990 };
991 self.fail_if(overflowed, too_large, span)?;
992 result
993 }
994 BinaryOp::Div | BinaryOp::Rem => {
995 let by_zero = self.b.ins().icmp_imm(IntCC::Equal, b, 0);
996 let divide_by_zero = self.shared.runtime.fail_divide_by_zero;
997 self.fail_if(by_zero, divide_by_zero, span)?;
998 let minimum = self.b.ins().icmp_imm(IntCC::Equal, a, i64::MIN);
999 let minus_one = self.b.ins().icmp_imm(IntCC::Equal, b, -1);
1000 let overflows = self.b.ins().band(minimum, minus_one);
1001 self.fail_if(overflows, too_large, span)?;
1002 if op == BinaryOp::Div {
1003 self.b.ins().sdiv(a, b)
1004 } else {
1005 self.b.ins().srem(a, b)
1006 }
1007 }
1008 op => {
1009 let holds = self.b.ins().icmp(int_condition(op)?, a, b);
1010 self.b.ins().uextend(I64, holds)
1011 }
1012 })
1013 }
1014
1015 fn float_op(&mut self, op: BinaryOp, a: Value, b: Value) -> Result<Value> {
1016 let (x, y) = (self.float(a), self.float(b));
1017 let result = match op {
1018 BinaryOp::Add => self.b.ins().fadd(x, y),
1019 BinaryOp::Sub => self.b.ins().fsub(x, y),
1020 BinaryOp::Mul => self.b.ins().fmul(x, y),
1021 BinaryOp::Div => self.b.ins().fdiv(x, y),
1022 BinaryOp::Rem => {
1023 let rem = self.shared.runtime.float_rem;
1024 self.call1(rem, &[x, y])
1025 }
1026 op => {
1027 let condition = match op {
1028 BinaryOp::Eq => FloatCC::Equal,
1029 BinaryOp::NotEq => FloatCC::NotEqual,
1030 BinaryOp::Lt => FloatCC::LessThan,
1031 BinaryOp::LtEq => FloatCC::LessThanOrEqual,
1032 BinaryOp::Gt => FloatCC::GreaterThan,
1033 BinaryOp::GtEq => FloatCC::GreaterThanOrEqual,
1034 op => return Err(CodegenError::new(format!("can't apply {op:?} to floats"))),
1035 };
1036 let holds = self.b.ins().fcmp(condition, x, y);
1037 return Ok(self.b.ins().uextend(I64, holds));
1038 }
1039 };
1040 Ok(self.bits(result))
1041 }
1042
1043 fn call_fn(
1046 &mut self,
1047 function: usize,
1048 args: &[Expr],
1049 ty: &Type,
1050 span: Span,
1051 ) -> Result<Operand> {
1052 let mut values = vec![self.context];
1053 values.extend(self.owned_all(args)?);
1054 let id = self.u32(function)?;
1055 let span = self.span(span)?;
1056 let enter = self.shared.runtime.enter;
1057 self.call_checked(enter, &[self.context, id, span]);
1058 let out = self.stack(8)?;
1059 let zero = self.b.ins().iconst(I64, 0);
1060 self.b.ins().store(MemFlags::trusted(), zero, out, 0);
1061 values.push(out);
1062 let compiled = self.shared.functions[function];
1063 self.call_checked(compiled, &values);
1064 let leave = self.shared.runtime.leave;
1065 self.call(leave, &[self.context]);
1066 let result = self.load(out);
1067 if *ty == Type::Void {
1068 Ok(Operand::owned(zero))
1069 } else {
1070 Ok(Operand::owned(result))
1071 }
1072 }
1073
1074 fn dialog(&mut self, dialog: &Dialog, span: Span) -> Result<()> {
1077 let title = self.expr(&dialog.title)?;
1078 let title = self.hold(title, &Type::String, &dialog.types)?;
1079 let types = self.expr(&dialog.types)?;
1080 let types = self.hold(types, &dialog.types.ty, &dialog.folder)?;
1081 let folder = self.expr(&dialog.folder)?;
1082 let folder = self.hold(folder, &Type::String, &dialog.name)?;
1083 let name = self.expr(&dialog.name)?;
1084 let answer = self.path(&dialog.answer)?;
1085 let Some(answer) = self.place_desc(&answer)? else {
1086 return self.fail_now(self.shared.runtime.fail_dialog_into_variable, span);
1087 };
1088 let kind = self.b.ins().iconst(I32, i64::from(dialog.kind as u32));
1089 let span = self.span(span)?;
1090 let pick = self.shared.runtime.pick;
1091 self.call_checked(
1092 pick,
1093 &[
1094 self.context,
1095 kind,
1096 answer,
1097 title.value,
1098 types.value,
1099 folder.value,
1100 name.value,
1101 span,
1102 ],
1103 );
1104 self.discard(title, &Type::String)?;
1105 self.discard(types, &dialog.types.ty)?;
1106 self.discard(folder, &Type::String)?;
1107 self.discard(name, &Type::String)
1108 }
1109
1110 fn window_request(&mut self, action: WindowAction, window: WindowId) -> Result<()> {
1112 let action = self.b.ins().iconst(I32, i64::from(action as u32));
1113 let window = u32::try_from(window.0)
1114 .map_err(|_| CodegenError::new("the program has too many windows"))?;
1115 let window = self.b.ins().iconst(I32, i64::from(window));
1116 let request = self.shared.runtime.window;
1117 self.call(request, &[self.context, action, window]);
1118 Ok(())
1119 }
1120
1121 fn fail_now(&mut self, fail: FuncId, span: Span) -> Result<()> {
1123 let span = self.span(span)?;
1124 self.call(fail, &[self.context, span]);
1125 let fail = self.fail_block();
1126 self.b.ins().jump(fail, &[]);
1127 self.continue_after_terminator();
1128 Ok(())
1129 }
1130
1131 fn place_desc(&mut self, path: &Alias) -> Result<Option<Value>> {
1133 let Root::State(state) = path.root else {
1134 return Ok(None);
1135 };
1136 let step_size = size_of::<PathStep>();
1137 let steps = self.stack(path.steps.len().max(1) * step_size)?;
1138 for (index, step) in path.steps.iter().enumerate() {
1139 let (kind, value) = match *step {
1140 Step::Field(field) => (StepKind::Field, self.usize(field)?),
1141 Step::Index(position) => (StepKind::Index, self.index(position)?),
1142 };
1143 let kind = self.b.ins().iconst(I8, i64::from(kind as u8));
1144 let kind_at = offset(index * step_size + offset_of!(PathStep, kind))?;
1145 let index_at = offset(index * step_size + offset_of!(PathStep, index))?;
1146 self.b
1147 .ins()
1148 .store(MemFlags::trusted(), kind, steps, kind_at);
1149 self.b
1150 .ins()
1151 .store(MemFlags::trusted(), value, steps, index_at);
1152 }
1153 let desc = self.stack(size_of::<PlaceDesc>())?;
1154 let state = self.u32(state.0)?;
1155 let len = self.usize(path.steps.len())?;
1156 let fields = [
1157 (state, offset_of!(PlaceDesc, state)),
1158 (steps, offset_of!(PlaceDesc, steps)),
1159 (len, offset_of!(PlaceDesc, len)),
1160 ];
1161 for (value, at) in fields {
1162 let at = offset(at)?;
1163 self.b.ins().store(MemFlags::trusted(), value, desc, at);
1164 }
1165 Ok(Some(desc))
1166 }
1167}
1168
1169fn offset(bytes: usize) -> Result<i32> {
1171 i32::try_from(bytes).map_err(|_| CodegenError::new("a frame or place is too large"))
1172}
1173
1174pub(super) fn is_pure(expr: &Expr) -> bool {
1177 match &expr.kind {
1178 ExprKind::Int(_)
1179 | ExprKind::Float(_)
1180 | ExprKind::Bool(_)
1181 | ExprKind::String(_)
1182 | ExprKind::State(_)
1183 | ExprKind::Local(_) => true,
1184 ExprKind::Interpolate(segments) => segments.iter().all(|segment| match segment {
1185 Segment::Text(_) => true,
1186 Segment::Value(value) => is_pure(value),
1187 }),
1188 ExprKind::List(items) | ExprKind::Construct(_, items) => items.iter().all(is_pure),
1189 ExprKind::Field(base, _)
1190 | ExprKind::Not(base)
1191 | ExprKind::Negate(base)
1192 | ExprKind::FormatBytes(base)
1193 | ExprKind::Len(base)
1194 | ExprKind::IsEmpty(base) => is_pure(base),
1195 ExprKind::Index(base, index) | ExprKind::Binary(_, base, index) => {
1196 is_pure(base) && is_pure(index)
1197 }
1198 ExprKind::Call(..)
1199 | ExprKind::Push(..)
1200 | ExprKind::Remove(..)
1201 | ExprKind::Download(_)
1202 | ExprKind::DownloadCommand(..)
1203 | ExprKind::Dialog(_)
1204 | ExprKind::Window(..)
1205 | ExprKind::Error => false,
1206 }
1207}
1208
1209fn int_condition(op: BinaryOp) -> Result<IntCC> {
1210 Ok(match op {
1211 BinaryOp::Eq => IntCC::Equal,
1212 BinaryOp::NotEq => IntCC::NotEqual,
1213 BinaryOp::Lt => IntCC::SignedLessThan,
1214 BinaryOp::LtEq => IntCC::SignedLessThanOrEqual,
1215 BinaryOp::Gt => IntCC::SignedGreaterThan,
1216 BinaryOp::GtEq => IntCC::SignedGreaterThanOrEqual,
1217 op => return Err(CodegenError::new(format!("{op:?} isn't a comparison"))),
1218 })
1219}
1220
1221fn op_type(op: BinaryOp, operands: &Type) -> Type {
1223 match op {
1224 BinaryOp::Eq
1225 | BinaryOp::NotEq
1226 | BinaryOp::Lt
1227 | BinaryOp::LtEq
1228 | BinaryOp::Gt
1229 | BinaryOp::GtEq
1230 | BinaryOp::And
1231 | BinaryOp::Or => Type::Bool,
1232 BinaryOp::Add | BinaryOp::Sub | BinaryOp::Mul | BinaryOp::Div | BinaryOp::Rem => {
1233 operands.clone()
1234 }
1235 }
1236}
1237
1238pub(super) fn element_type(list: &Type) -> Result<Type> {
1239 match list {
1240 Type::List(element) => Ok(Type::clone(element)),
1241 other => Err(CodegenError::new(format!("{other:?} isn't a list"))),
1242 }
1243}