Skip to main content

nifty_codegen/compile/
body.rs

1//! Compiling one function body: statements, expressions and places, with the reference
2//! counting that keeps values' sharing invisible.
3//!
4//! An expression gives an [`Operand`] that is either owned, which its user must release
5//! or hand on, or borrowed from wherever it lives. A borrowed operand is only kept while
6//! nothing that could change state runs, so what it borrows from stays alive.
7//!
8//! A failing runtime call returns `false` through the function's failure block. A failure
9//! ends the app, so values the function holds then aren't released.
10
11use 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
34/// Every slot is 8 bytes.
35const SLOT: i64 = 8;
36
37pub(super) type Result<T> = std::result::Result<T, CodegenError>;
38
39/// A value an expression gave, and whether the code holding it owns it.
40#[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/// Where a `for` variable over a place finds its item: a root, then steps whose list
60/// positions were worked out when the loop began.
61#[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    /// A local's slot in the frame.
71    Local(usize),
72}
73
74#[derive(Clone, Copy, Debug)]
75pub(super) enum Step {
76    Field(usize),
77    Index(Index),
78}
79
80/// Where a list position is kept.
81#[derive(Clone, Copy, Debug)]
82pub(super) enum Index {
83    /// Worked out in this function.
84    Value(Value),
85    /// A loop counter in this function.
86    Var(Variable),
87    /// A frame slot, for UI whose parts are separate functions.
88    Slot(usize),
89}
90
91/// How `return` leaves the function being compiled, if it may.
92#[derive(Clone, Copy, Debug)]
93pub(super) struct Returns {
94    /// Where the result goes.
95    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    /// The locals' slots.
104    pub frame: Value,
105    /// The type of each of the frame's locals.
106    pub locals: &'a [Type],
107    pub aliases: HashMap<LocalId, Alias>,
108    pub returns: Option<Returns>,
109    fail: Option<Block>,
110    /// Where calls write their results, read straight after each call.
111    scratch: Option<StackSlot>,
112    /// Owned values held across statements, such as the list a loop walks, released if
113    /// the function returns from inside.
114    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    /// Returns `true` from where the body ended, then emits the failure block.
142    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    /// Returns `true` here, and carries on in a new block.
152    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    /// Continues in a new block, after a terminator.
163    fn continue_after_terminator(&mut self) {
164        let next = self.b.create_block();
165        self.b.switch_to_block(next);
166    }
167
168    // Runtime calls.
169
170    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    /// Makes a call that returns whether it succeeded, going to the failure block if not.
181    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    /// Fails with `failure` at `span` when `condition` holds.
198    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    /// A stack slot of `size` bytes, aligned for slots, and its address.
241    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    /// The address of the slot calls write their results to.
253    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    /// `values`, stored in order in a stack array, and its address.
262    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    // Ownership.
281
282    /// `operand`, owned: retained if it was borrowed.
283    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    /// Lets go of `operand`: released if it was owned.
293    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    /// Writes an owned `value` of type `ty` to the slot at `place`, releasing what was
308    /// there.
309    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    /// Releases every local of the frame, as a function does before it returns.
329    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    // Places.
341
342    /// Where `place` is: its root, then its steps, with its list positions worked out now.
343    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    /// The slot `path` leads to, made its only owner's at each step so it can be changed.
377    /// Writing through it marks a state root changed.
378    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    /// The value `path` leads to, borrowed.
408    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    /// The item at `index` of the borrowed `list`, borrowed from it.
437    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    // Statements.
446
447    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    /// The value is worked out before the place, as in the interpreter.
478    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    /// Walks a copy of the list, putting each item in the loop's item.
522    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    /// Walks the list at `place`, with the loop's item standing for each of its items in
542    /// turn. The length is read each time round, so the body may change the list.
543    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    /// Counts `local` from `start` up to, but not including, `end`.
568    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    /// Puts a round's position in the loop's index, when the loop names one.
579    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    /// Runs `step` with each index from 0 up to `len`.
588    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    /// Runs `step` with each count from `start` up to, but not including, `end`.
598    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    /// Runs `step` with each value of `counter` from `start` while it is below what `end`
609    /// gives; `end` is worked out again each time round.
610    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    // Expressions.
662
663    /// An `int` (or `bool`) expression's value.
664    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    /// What an expression of type `void` gives.
767    fn nothing(&mut self) -> Operand {
768        Operand::owned(self.b.ins().iconst(I64, 0))
769    }
770
771    /// A list or struct of type `ty` holding `parts`, worked out in order.
772    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    /// The value is worked out before the place, as in the interpreter.
787    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    /// Each expression's value, owned, in order.
808    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    /// `operand`, kept while `next` is worked out: owned unless `next` can't change state.
819    fn hold(&mut self, operand: Operand, ty: &Type, next: &Expr) -> Result<Operand> {
820        self.hold_across(operand, ty, [next])
821    }
822
823    /// `operand`, kept while each of `later` is worked out: owned unless none of them can
824    /// change state.
825    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    /// A part borrowed from `container`: still borrowed if the container was, and
839    /// otherwise owned, since the container is released.
840    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    /// Applies `op` to operands of type `ty`, releasing whichever are owned.
940    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    /// Calls Nifty function `function`. Its arguments are worked out before the call depth
1044    /// is checked, as in the interpreter.
1045    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    /// `pick_file($path, …)` and the other file dialogs: asks for one, shown once the event
1075    /// finishes, whose answer goes to the bound state.
1076    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    /// `open_window(…)` or `close_window(…)`: asks for it, done once the event finishes.
1111    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    /// Calls the runtime's `fail(context, span)` and leaves through the failure block.
1122    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    /// `path` as a `nifty_rt::PlaceDesc` on the stack, or `None` if it isn't in state.
1132    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
1169/// A byte offset as Cranelift takes it.
1170fn offset(bytes: usize) -> Result<i32> {
1171    i32::try_from(bytes).map_err(|_| CodegenError::new("a frame or place is too large"))
1172}
1173
1174/// Whether working out `expr` can't change state or locals, so values borrowed before it
1175/// stay alive.
1176pub(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
1221/// The type `op` gives when applied to `operands`.
1222fn 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}