Skip to main content

nifty_codegen/compile/
ui.rs

1//! Windows and views: their templates, and the functions behind each template's dynamic
2//! parts, which the runtime calls with the template's frame.
3//!
4//! A `for` over a place shows each item through an alias, as the interpreter does. Its
5//! list positions are worked out when the region is built and kept in extra slots after
6//! the frame's own locals, so every function that reads the item finds the same one.
7
8use std::collections::HashMap;
9use std::mem::{offset_of, size_of};
10
11use cranelift_codegen::ir::types::{I8, I64};
12use cranelift_codegen::ir::{InstBuilder, MemFlags, Value};
13use cranelift_module::{FuncId, Module};
14use nifty_check::hir::{
15    Binding, Block as Stmts, Dynamic, Expr, Iterable, LocalId, Loop, NavItem, Node, Nodes, Place,
16    PlaceRoot, PlaceStep, StateSet, Type, ViewId,
17};
18use nifty_rt::{
19    BindingDesc, FrameDesc, RegionDesc, Slice, TemplateDesc, TemplateKind, TypeDesc, ViewDesc,
20    WindowDesc,
21};
22use nifty_runtime::{ImageSize, LabelStyle, StackStyle, ToggleStyle};
23
24use super::body::{Alias, Body, Index, Operand, Root, Step, element_type};
25use super::{List, Result, Shared, put_str};
26use crate::CodegenError;
27use crate::emit::{Blob, Emitter, Target};
28
29/// The frame a window's or view's templates share: its locals, then the extra slots its
30/// `for` regions keep list positions in.
31pub(super) struct Frame<'p> {
32    locals: &'p [Type],
33    extra: usize,
34}
35
36impl<'p> Frame<'p> {
37    pub(super) fn new(locals: &'p [Type]) -> Self {
38        Self { locals, extra: 0 }
39    }
40}
41
42impl Frame<'_> {
43    fn add_slot(&mut self) -> usize {
44        self.extra += 1;
45        self.locals.len() + self.extra - 1
46    }
47
48    /// The type of every slot; the extra ones hold list positions.
49    fn types(&self) -> Vec<Type> {
50        let mut types = self.locals.to_vec();
51        types.extend(std::iter::repeat_n(Type::Int, self.extra));
52        types
53    }
54}
55
56/// The aliases templates inside a region can see.
57pub(super) type Aliases = HashMap<LocalId, Alias>;
58
59/// Where a template's fields go: its blob and its offset in it.
60struct Slot<'b> {
61    blob: &'b mut Blob,
62    at: usize,
63}
64
65impl Slot<'_> {
66    fn function(&mut self, field: usize, function: FuncId) {
67        self.blob
68            .put_pointer(self.at + field, Target::Function(function));
69    }
70}
71
72pub(super) struct Ui<'a, 'p, M: Module> {
73    pub(super) emitter: &'a mut Emitter<M>,
74    pub(super) shared: &'a mut Shared<'p>,
75}
76
77pub(super) fn windows<M: Module>(
78    emitter: &mut Emitter<M>,
79    shared: &mut Shared<'_>,
80) -> Result<List> {
81    let program = shared.program;
82    let mut ui = Ui { emitter, shared };
83    let size = size_of::<WindowDesc>();
84    let mut blob = Blob::zeroed(program.windows.len() * size);
85    for (index, window) in program.windows.iter().enumerate() {
86        let mut frame = Frame::new(&window.locals);
87        let children = ui.templates(&mut frame, &Aliases::new(), &window.children)?;
88        let locals = ui.frame_types(&frame)?;
89        let at = index * size;
90        put_str(
91            ui.emitter,
92            ui.shared,
93            &mut blob,
94            at + offset_of!(WindowDesc, title),
95            &window.title,
96        )?;
97        blob.put_f32(at + offset_of!(WindowDesc, width), window.size.width);
98        blob.put_f32(at + offset_of!(WindowDesc, height), window.size.height);
99        blob.put_u8(
100            at + offset_of!(WindowDesc, backdrop),
101            window.backdrop.code(),
102        );
103        blob.put_u8(
104            at + offset_of!(WindowDesc, starts_open),
105            u8::from(window.starts_open),
106        );
107        put_stack(&mut blob, at + offset_of!(WindowDesc, style), window.style);
108        children.put(&mut blob, at + offset_of!(WindowDesc, children));
109        locals.put(
110            &mut blob,
111            at + offset_of!(WindowDesc, frame) + offset_of!(FrameDesc, locals),
112        );
113    }
114    ui.list(blob, program.windows.len())
115}
116
117pub(super) fn views<M: Module>(emitter: &mut Emitter<M>, shared: &mut Shared<'_>) -> Result<List> {
118    let program = shared.program;
119    let mut ui = Ui { emitter, shared };
120    let size = size_of::<ViewDesc>();
121    let mut blob = Blob::zeroed(program.views.len() * size);
122    for (index, view) in program.views.iter().enumerate() {
123        let mut frame = Frame::new(&view.locals);
124        let body = ui.templates(&mut frame, &Aliases::new(), &view.body)?;
125        let locals = ui.frame_types(&frame)?;
126        let at = index * size;
127        body.put(&mut blob, at + offset_of!(ViewDesc, body));
128        locals.put(
129            &mut blob,
130            at + offset_of!(ViewDesc, frame) + offset_of!(FrameDesc, locals),
131        );
132    }
133    ui.list(blob, program.views.len())
134}
135
136impl<M: Module> Ui<'_, '_, M> {
137    pub(super) fn list(&mut self, blob: Blob, len: usize) -> Result<List> {
138        if len == 0 {
139            return Ok(List::EMPTY);
140        }
141        Ok(List {
142            data: Some(self.emitter.data(blob)?),
143            len,
144        })
145    }
146
147    pub(super) fn frame_types(&mut self, frame: &Frame<'_>) -> Result<List> {
148        let types = frame
149            .types()
150            .iter()
151            .map(|ty| {
152                self.shared
153                    .types
154                    .desc(self.emitter.module_mut(), &self.shared.runtime, ty)
155            })
156            .collect::<Result<Vec<_>>>()?;
157        List::emit(
158            self.emitter,
159            types.len(),
160            size_of::<&TypeDesc>(),
161            |blob, index, at| {
162                blob.put_pointer(at, Target::Data(types[index]));
163                Ok(())
164            },
165        )
166    }
167
168    /// Emits `nodes` as an array of templates.
169    fn templates(
170        &mut self,
171        frame: &mut Frame<'_>,
172        aliases: &Aliases,
173        nodes: &Nodes,
174    ) -> Result<List> {
175        let size = size_of::<TemplateDesc>();
176        let mut blob = Blob::zeroed(nodes.len() * size);
177        for (index, node) in nodes.iter().enumerate() {
178            let mut slot = Slot {
179                blob: &mut blob,
180                at: index * size,
181            };
182            let kind = self.template(frame, aliases, node, &mut slot)?;
183            blob.put_u8(index * size + offset_of!(TemplateDesc, kind), kind as u8);
184        }
185        self.list(blob, nodes.len())
186    }
187
188    fn template(
189        &mut self,
190        frame: &mut Frame<'_>,
191        aliases: &Aliases,
192        node: &Node,
193        slot: &mut Slot<'_>,
194    ) -> Result<TemplateKind> {
195        Ok(match node {
196            Node::Stack {
197                style,
198                children,
199                scrolls,
200            } => {
201                put_stack(slot.blob, slot.at + offset_of!(TemplateDesc, stack), *style);
202                let children = self.templates(frame, aliases, children)?;
203                children.put(slot.blob, slot.at + offset_of!(TemplateDesc, children));
204                if *scrolls {
205                    TemplateKind::Scroll
206                } else {
207                    TemplateKind::Stack
208                }
209            }
210            Node::If {
211                branches,
212                otherwise,
213                deps,
214            } => {
215                self.if_region(frame, aliases, branches, otherwise, slot)?;
216                self.deps(slot, deps)?;
217                TemplateKind::Region
218            }
219            Node::For { head, body, deps } => {
220                self.for_region(frame, aliases, head, body, slot)?;
221                self.deps(slot, deps)?;
222                TemplateKind::Region
223            }
224            Node::View { view, args, deps } => {
225                let expand = self.view(frame, aliases, *view, args)?;
226                self.region(slot, expand, &[])?;
227                self.deps(slot, deps)?;
228                TemplateKind::Region
229            }
230            control => self.control(frame, aliases, control, slot)?,
231        })
232    }
233
234    /// A control's template: the functions behind what it shows and edits.
235    fn control(
236        &mut self,
237        frame: &mut Frame<'_>,
238        aliases: &Aliases,
239        node: &Node,
240        slot: &mut Slot<'_>,
241    ) -> Result<TemplateKind> {
242        match node {
243            Node::Label { .. }
244            | Node::Image { .. }
245            | Node::Progress { .. }
246            | Node::Spacer
247            | Node::Divider => self.display(frame, aliases, node, slot),
248            _ => self.input(frame, aliases, node, slot),
249        }
250    }
251
252    /// A control that only shows something.
253    fn display(
254        &mut self,
255        frame: &mut Frame<'_>,
256        aliases: &Aliases,
257        node: &Node,
258        slot: &mut Slot<'_>,
259    ) -> Result<TemplateKind> {
260        Ok(match node {
261            Node::Label { text, style } => {
262                put_label(slot.blob, slot.at + offset_of!(TemplateDesc, label), *style);
263                self.shown(frame, aliases, text, slot, offset_of!(TemplateDesc, text))?;
264                self.deps(slot, &text.deps)?;
265                TemplateKind::Label
266            }
267            Node::Image { path, size } => {
268                put_image(slot.blob, slot.at + offset_of!(TemplateDesc, image), *size);
269                self.shown(frame, aliases, path, slot, offset_of!(TemplateDesc, text))?;
270                self.deps(slot, &path.deps)?;
271                TemplateKind::Image
272            }
273            Node::Progress { value } => {
274                if let Some(value) = value {
275                    self.shown(frame, aliases, value, slot, offset_of!(TemplateDesc, value))?;
276                    self.deps(slot, &value.deps)?;
277                }
278                TemplateKind::Progress
279            }
280            Node::Spacer => TemplateKind::Spacer,
281            Node::Divider => TemplateKind::Divider,
282            _ => return Err(CodegenError::new(format!("{node:?} isn't a display"))),
283        })
284    }
285
286    /// A control the user works: the functions behind what it shows, edits and runs.
287    fn input(
288        &mut self,
289        frame: &mut Frame<'_>,
290        aliases: &Aliases,
291        node: &Node,
292        slot: &mut Slot<'_>,
293    ) -> Result<TemplateKind> {
294        Ok(match node {
295            Node::Button {
296                title,
297                style,
298                icon,
299                selected,
300                action,
301            } => {
302                slot.blob
303                    .put_u8(slot.at + offset_of!(TemplateDesc, button), style.code());
304                slot.blob
305                    .put_u8(slot.at + offset_of!(TemplateDesc, icon), icon.code());
306                self.button(frame, aliases, (title, selected.as_deref()), action, slot)?;
307                TemplateKind::Button
308            }
309            Node::TextField {
310                value,
311                placeholder,
312                lines,
313                action,
314            } => {
315                self.binding(frame, aliases, value, slot)?;
316                self.optional_action(frame, aliases, action.as_ref(), slot)?;
317                let at = slot.at + offset_of!(TemplateDesc, placeholder);
318                put_str(self.emitter, self.shared, slot.blob, at, placeholder)?;
319                slot.blob
320                    .put_u32(slot.at + offset_of!(TemplateDesc, lines), *lines);
321                self.deps(slot, &value.deps)?;
322                TemplateKind::TextField
323            }
324            Node::Checkbox {
325                checked,
326                title,
327                style,
328            } => {
329                self.binding(frame, aliases, checked, slot)?;
330                let mut deps = checked.deps.clone();
331                if let Some(title) = title {
332                    self.shown(frame, aliases, title, slot, offset_of!(TemplateDesc, text))?;
333                    deps.extend(&title.deps);
334                }
335                self.deps(slot, &deps)?;
336                match style {
337                    ToggleStyle::Checkbox => TemplateKind::Checkbox,
338                    ToggleStyle::Switch => TemplateKind::Switch,
339                }
340            }
341            Node::Slider { value, min, max } => {
342                self.binding(frame, aliases, value, slot)?;
343                slot.blob
344                    .put_i64(slot.at + offset_of!(TemplateDesc, min), *min);
345                slot.blob
346                    .put_i64(slot.at + offset_of!(TemplateDesc, max), *max);
347                self.deps(slot, &value.deps)?;
348                TemplateKind::Slider
349            }
350            Node::Picker {
351                selection,
352                options,
353                deps,
354            } => {
355                self.binding(frame, aliases, selection, slot)?;
356                self.shown(
357                    frame,
358                    aliases,
359                    options,
360                    slot,
361                    offset_of!(TemplateDesc, value),
362                )?;
363                self.deps(slot, deps)?;
364                TemplateKind::Picker
365            }
366            Node::NavItem(item) => {
367                self.nav_item(frame, aliases, item, slot)?;
368                TemplateKind::NavItem
369            }
370            Node::ContextMenu { entries } => {
371                let menu = self.menu_entries(frame, aliases, entries)?;
372                menu.put(slot.blob, slot.at + offset_of!(TemplateDesc, menu));
373                TemplateKind::ContextMenu
374            }
375            Node::DropTarget { dropped, action } => {
376                self.binding(frame, aliases, dropped, slot)?;
377                self.optional_action(frame, aliases, action.as_ref(), slot)?;
378                TemplateKind::DropTarget
379            }
380            _ => return Err(CodegenError::new(format!("{node:?} isn't an input"))),
381        })
382    }
383
384    /// Shows `body` once for each item `head` loops over.
385    fn for_region(
386        &mut self,
387        frame: &mut Frame<'_>,
388        aliases: &Aliases,
389        head: &Loop,
390        body: &Nodes,
391        slot: &mut Slot<'_>,
392    ) -> Result<()> {
393        let (expand, list) = match &head.iterable {
394            Iterable::Value(list) => {
395                let body = self.templates(frame, aliases, body)?;
396                (self.for_value(frame, aliases, head, list)?, body)
397            }
398            Iterable::Place(place) => self.for_place(frame, aliases, head, place, body)?,
399            Iterable::Range { start, end } => {
400                let body = self.templates(frame, aliases, body)?;
401                (self.for_range(frame, aliases, head.item, start, end)?, body)
402            }
403        };
404        self.region(slot, expand, &[list])
405    }
406
407    /// A button's title, whether it is selected if it shows that, and its action.
408    fn button(
409        &mut self,
410        frame: &mut Frame<'_>,
411        aliases: &Aliases,
412        (title, selected): (&Dynamic, Option<&Dynamic>),
413        action: &Stmts,
414        slot: &mut Slot<'_>,
415    ) -> Result<()> {
416        let title_fn = self.value(frame, aliases, title)?;
417        slot.function(offset_of!(TemplateDesc, text), title_fn);
418        let mut deps = title.deps.clone();
419        if let Some(selected) = selected {
420            let selected_fn = self.value(frame, aliases, selected)?;
421            slot.function(offset_of!(TemplateDesc, value), selected_fn);
422            deps.extend(&selected.deps);
423        }
424        let action_fn = self.action(frame, aliases, action)?;
425        slot.function(offset_of!(TemplateDesc, action), action_fn);
426        self.deps(slot, &deps)
427    }
428
429    fn nav_item(
430        &mut self,
431        frame: &mut Frame<'_>,
432        aliases: &Aliases,
433        item: &NavItem,
434        slot: &mut Slot<'_>,
435    ) -> Result<()> {
436        slot.blob
437            .put_u8(slot.at + offset_of!(TemplateDesc, icon), item.icon.code());
438        self.binding(frame, aliases, &item.selection, slot)?;
439        let title_fn = self.value(frame, aliases, &item.title)?;
440        slot.function(offset_of!(TemplateDesc, text), title_fn);
441        let value_fn = self.value(frame, aliases, &item.value)?;
442        slot.function(offset_of!(TemplateDesc, value), value_fn);
443        if let Some(badge) = &item.badge {
444            let badge_fn = self.value(frame, aliases, badge)?;
445            slot.function(offset_of!(TemplateDesc, badge), badge_fn);
446        }
447        self.deps(slot, &item.deps)
448    }
449
450    /// Shows the first branch whose condition holds, or else `otherwise`, which is the
451    /// last list.
452    fn if_region(
453        &mut self,
454        frame: &mut Frame<'_>,
455        aliases: &Aliases,
456        branches: &[(std::rc::Rc<Expr>, Nodes)],
457        otherwise: &Nodes,
458        slot: &mut Slot<'_>,
459    ) -> Result<()> {
460        let mut lists = Vec::new();
461        for (_, body) in branches {
462            lists.push(self.templates(frame, aliases, body)?);
463        }
464        lists.push(self.templates(frame, aliases, otherwise)?);
465        let expand = self.expansion(frame, aliases, |body, sink| {
466            let show = body.shared.runtime.show_same;
467            for (index, (condition, _)) in branches.iter().enumerate() {
468                let holds = body.expr(condition)?.value;
469                let showing = body.b.create_block();
470                let next = body.b.create_block();
471                body.b.ins().brif(holds, showing, &[], next, &[]);
472                body.b.switch_to_block(showing);
473                let list = body.u32(index)?;
474                body.call(show, &[sink, list]);
475                body.succeed_here();
476                body.b.switch_to_block(next);
477            }
478            let otherwise = body.u32(branches.len())?;
479            body.call(show, &[sink, otherwise]);
480            Ok(())
481        })?;
482        self.region(slot, expand, &lists)
483    }
484
485    /// Shows the body once per item of a copy of the list, with the item in the loop's
486    /// item.
487    fn for_value(
488        &mut self,
489        frame: &Frame<'_>,
490        aliases: &Aliases,
491        head: &Loop,
492        list: &Expr,
493    ) -> Result<FuncId> {
494        let element = element_type(&list.ty)?;
495        self.expansion(frame, aliases, |body, sink| {
496            let operand = body.expr(list)?;
497            let list_owned = body.own(operand, &list.ty)?;
498            let len_fn = body.shared.runtime.list_len;
499            let len = body.call1(len_fn, &[list_owned]);
500            body.count_up(len, |body, index| {
501                let item = body.list_item(list_owned, index, list.span)?;
502                let item = body.own(Operand::borrowed(item), &element)?;
503                let locals = show_round(body, sink)?;
504                store_index(body, locals, head.index, index)?;
505                let slot = body.b.ins().iadd_imm(locals, slot_offset(head.item.0)?);
506                body.store(slot, item, &element)
507            })?;
508            body.release(list_owned, &list.ty)
509        })
510    }
511
512    /// Shows the body once per count from `start` up to, but not including, `end`, with the
513    /// count in `local`.
514    fn for_range(
515        &mut self,
516        frame: &Frame<'_>,
517        aliases: &Aliases,
518        local: LocalId,
519        start: &Expr,
520        end: &Expr,
521    ) -> Result<FuncId> {
522        self.expansion(frame, aliases, |body, sink| {
523            let start = body.expr(start)?.value;
524            let end = body.expr(end)?.value;
525            body.count(start, end, |body, count| {
526                let locals = show_round(body, sink)?;
527                store_int(body, locals, local.0, count)
528            })
529        })
530    }
531
532    /// Shows the body once per item of the list at `place`, through an alias whose list
533    /// positions are kept in extra frame slots.
534    fn for_place(
535        &mut self,
536        frame: &mut Frame<'_>,
537        aliases: &Aliases,
538        head: &Loop,
539        place: &Place,
540        nodes: &Nodes,
541    ) -> Result<(FuncId, List)> {
542        let mut item = match place.root {
543            PlaceRoot::State(state) => Alias {
544                root: Root::State(state),
545                steps: Vec::new(),
546            },
547            PlaceRoot::Local(root) => aliases.get(&root).cloned().unwrap_or(Alias {
548                root: Root::Local(root.0),
549                steps: Vec::new(),
550            }),
551        };
552        let mut positions = Vec::new();
553        for step in &place.path {
554            item.steps.push(match step {
555                PlaceStep::Field(field) => Step::Field(*field),
556                PlaceStep::Index(_) => {
557                    let slot = frame.add_slot();
558                    positions.push(slot);
559                    Step::Index(Index::Slot(slot))
560                }
561            });
562        }
563        let counter = frame.add_slot();
564        item.steps.push(Step::Index(Index::Slot(counter)));
565        let mut inner = aliases.clone();
566        inner.insert(head.item, item);
567        let list = self.templates(frame, &inner, nodes)?;
568        let expand = self.expansion(frame, aliases, |body, sink| {
569            let path = body.path(place)?;
570            let worked_out: Vec<Value> = path
571                .steps
572                .iter()
573                .filter_map(|step| match step {
574                    Step::Index(Index::Value(value)) => Some(*value),
575                    _ => None,
576                })
577                .collect();
578            let list_value = body.read(&path, place.span)?;
579            let len_fn = body.shared.runtime.list_len;
580            let len = body.call1(len_fn, &[list_value]);
581            body.count_up(len, |body, index| {
582                let locals = show_round(body, sink)?;
583                for (slot, value) in positions.iter().zip(&worked_out) {
584                    store_int(body, locals, *slot, *value)?;
585                }
586                store_index(body, locals, head.index, index)?;
587                store_int(body, locals, counter, index)
588            })
589        })?;
590        Ok((expand, list))
591    }
592
593    /// Shows the view's body in a new frame holding its arguments.
594    fn view(
595        &mut self,
596        frame: &Frame<'_>,
597        aliases: &Aliases,
598        view: ViewId,
599        args: &[Expr],
600    ) -> Result<FuncId> {
601        self.expansion(frame, aliases, |body, sink| {
602            let id = body.u32(view.0)?;
603            let show = body.shared.runtime.show_view;
604            let locals = body.call1(show, &[sink, id]);
605            for (index, arg) in args.iter().enumerate() {
606                let operand = body.expr(arg)?;
607                let value = body.own(operand, &arg.ty)?;
608                let slot = body.b.ins().iadd_imm(locals, slot_offset(index)?);
609                body.store(slot, value, &arg.ty)?;
610            }
611            Ok(())
612        })
613    }
614
615    /// A function the runtime calls with the context, the template's frame, and `extra`
616    /// if there is one. It returns `true` where `build` leaves it.
617    fn function(
618        &mut self,
619        frame: &Frame<'_>,
620        aliases: &Aliases,
621        extra: bool,
622        build: impl FnOnce(&mut Body<'_, '_, '_, M>, Value) -> Result<()>,
623    ) -> Result<FuncId> {
624        let params: &[_] = if extra { &[I64, I64, I64] } else { &[I64, I64] };
625        let signature = self.emitter.signature(params, &[I8]);
626        let (shared, locals) = (&mut *self.shared, frame.locals);
627        self.emitter.function(signature, |b, module| {
628            let entry = b
629                .current_block()
630                .ok_or_else(|| CodegenError::new("a function has no entry block"))?;
631            let params = b.block_params(entry).to_vec();
632            let mut body = Body::new(b, module, shared, params[0], params[1], locals);
633            body.aliases.clone_from(aliases);
634            build(&mut body, params[params.len() - 1])?;
635            body.finish();
636            Ok(())
637        })
638    }
639
640    /// A region's expansion, which tells the sink it's given what the region shows.
641    fn expansion(
642        &mut self,
643        frame: &Frame<'_>,
644        aliases: &Aliases,
645        build: impl FnOnce(&mut Body<'_, '_, '_, M>, Value) -> Result<()>,
646    ) -> Result<FuncId> {
647        self.function(frame, aliases, true, build)
648    }
649
650    /// The function that works out `shown`, put in the template's field at offset `field`.
651    fn shown(
652        &mut self,
653        frame: &Frame<'_>,
654        aliases: &Aliases,
655        shown: &Dynamic,
656        slot: &mut Slot<'_>,
657        field: usize,
658    ) -> Result<()> {
659        let function = self.value(frame, aliases, shown)?;
660        slot.function(field, function);
661        Ok(())
662    }
663
664    /// Writes what the UI shows, owned, to the pointer it's given.
665    fn value(&mut self, frame: &Frame<'_>, aliases: &Aliases, shown: &Dynamic) -> Result<FuncId> {
666        self.function(frame, aliases, true, |body, out| {
667            let operand = body.expr(&shown.expr)?;
668            let value = body.own(operand, &shown.expr.ty)?;
669            body.b.ins().store(MemFlags::trusted(), value, out, 0);
670            Ok(())
671        })
672    }
673
674    pub(super) fn action(
675        &mut self,
676        frame: &Frame<'_>,
677        aliases: &Aliases,
678        action: &Stmts,
679    ) -> Result<FuncId> {
680        self.function(frame, aliases, false, |body, _| body.block(action))
681    }
682
683    /// The action of a control that may have one, such as a text field's on Return.
684    fn optional_action(
685        &mut self,
686        frame: &Frame<'_>,
687        aliases: &Aliases,
688        action: Option<&Stmts>,
689        slot: &mut Slot<'_>,
690    ) -> Result<()> {
691        if let Some(action) = action {
692            let action_fn = self.action(frame, aliases, action)?;
693            slot.function(offset_of!(TemplateDesc, action), action_fn);
694        }
695        Ok(())
696    }
697
698    /// The functions that read a binding's place, and write the user's edits to it.
699    fn binding(
700        &mut self,
701        frame: &Frame<'_>,
702        aliases: &Aliases,
703        binding: &Binding,
704        slot: &mut Slot<'_>,
705    ) -> Result<()> {
706        let place = &binding.place;
707        let read = self.function(frame, aliases, true, |body, out| {
708            let path = body.path(place)?;
709            let value = body.read(&path, place.span)?;
710            let value = body.own(Operand::borrowed(value), &place.ty)?;
711            body.b.ins().store(MemFlags::trusted(), value, out, 0);
712            Ok(())
713        })?;
714        let write = self.function(frame, aliases, true, |body, value| {
715            let path = body.path(place)?;
716            // The interpreter writes edits of a place outside state into a copy of the
717            // frame, where nothing sees them, so they're dropped here too.
718            if !matches!(path.root, Root::State(_)) {
719                return body.discard(Operand::owned(value), &place.ty);
720            }
721            let pointer = body.pointer(&path, place.span)?;
722            body.store(pointer, value, &place.ty)
723        })?;
724        let binding_at = offset_of!(TemplateDesc, binding);
725        slot.function(binding_at + offset_of!(BindingDesc, read), read);
726        slot.function(binding_at + offset_of!(BindingDesc, write), write);
727        Ok(())
728    }
729
730    fn region(&mut self, slot: &mut Slot<'_>, expand: FuncId, lists: &[List]) -> Result<()> {
731        let region_at = offset_of!(TemplateDesc, region);
732        slot.function(region_at + offset_of!(RegionDesc, expand), expand);
733        let size = size_of::<Slice<TemplateDesc>>();
734        let lists = List::emit(self.emitter, lists.len(), size, |blob, index, at| {
735            lists[index].put(blob, at);
736            Ok(())
737        })?;
738        lists.put(
739            slot.blob,
740            slot.at + region_at + offset_of!(RegionDesc, lists),
741        );
742        Ok(())
743    }
744
745    fn deps(&mut self, slot: &mut Slot<'_>, states: &StateSet) -> Result<()> {
746        let ids = states
747            .iter()
748            .map(|state| {
749                u32::try_from(state.0)
750                    .map_err(|_| CodegenError::new("the program has too many states"))
751            })
752            .collect::<Result<Vec<_>>>()?;
753        let list = List::emit(
754            self.emitter,
755            ids.len(),
756            size_of::<u32>(),
757            |blob, index, at| {
758                blob.put_u32(at, ids[index]);
759                Ok(())
760            },
761        )?;
762        list.put(slot.blob, slot.at + offset_of!(TemplateDesc, deps));
763        Ok(())
764    }
765}
766
767/// Tells the sink to show a `for` region's body once more, and gives that round's frame.
768fn show_round<M: Module>(body: &mut Body<'_, '_, '_, M>, sink: Value) -> Result<Value> {
769    let show = body.shared.runtime.show_item;
770    let first = body.u32(0)?;
771    Ok(body.call1(show, &[sink, first]))
772}
773
774/// Puts a round's position in the loop's index, when the loop names one.
775fn store_index<M: Module>(
776    body: &mut Body<'_, '_, '_, M>,
777    locals: Value,
778    index: Option<LocalId>,
779    position: Value,
780) -> Result<()> {
781    match index {
782        Some(index) => store_int(body, locals, index.0, position),
783        None => Ok(()),
784    }
785}
786
787/// Stores an `int` in a frame's slot.
788fn store_int<M: Module>(
789    body: &mut Body<'_, '_, '_, M>,
790    locals: Value,
791    slot: usize,
792    value: Value,
793) -> Result<()> {
794    let at =
795        i32::try_from(slot_offset(slot)?).map_err(|_| CodegenError::new("a frame is too large"))?;
796    body.b.ins().store(MemFlags::trusted(), value, locals, at);
797    Ok(())
798}
799
800fn put_stack(blob: &mut Blob, at: usize, style: StackStyle) {
801    blob.put_u8(at + offset_of!(StackStyle, axis), style.axis.code());
802    blob.put_f32(at + offset_of!(StackStyle, spacing), style.spacing);
803    blob.put_f32(at + offset_of!(StackStyle, padding), style.padding);
804    blob.put_u8(at + offset_of!(StackStyle, align), style.align.code());
805    blob.put_u8(at + offset_of!(StackStyle, justify), style.justify.code());
806    blob.put_u8(at + offset_of!(StackStyle, surface), style.surface.code());
807    blob.put_u8(at + offset_of!(StackStyle, grow), u8::from(style.grow));
808    blob.put_f32(at + offset_of!(StackStyle, width), style.width);
809    blob.put_f32(at + offset_of!(StackStyle, max_width), style.max_width);
810}
811
812fn put_label(blob: &mut Blob, at: usize, style: LabelStyle) {
813    blob.put_u8(at + offset_of!(LabelStyle, font), style.font.code());
814    blob.put_u8(at + offset_of!(LabelStyle, color), style.color.code());
815    blob.put_u8(at + offset_of!(LabelStyle, align), style.align.code());
816    blob.put_u8(
817        at + offset_of!(LabelStyle, selectable),
818        u8::from(style.selectable),
819    );
820}
821
822fn put_image(blob: &mut Blob, at: usize, size: ImageSize) {
823    blob.put_f32(at + offset_of!(ImageSize, width), size.width);
824    blob.put_f32(at + offset_of!(ImageSize, height), size.height);
825}
826
827fn slot_offset(local: usize) -> Result<i64> {
828    i64::try_from(local)
829        .ok()
830        .and_then(|local| local.checked_mul(8))
831        .ok_or_else(|| CodegenError::new("a frame is too large"))
832}