rune/compile/
assembly.rs

1//! Helpers for building assembly.
2
3use core::fmt;
4
5use crate as rune;
6use crate::alloc::fmt::TryWrite;
7use crate::alloc::prelude::*;
8use crate::alloc::{hash_map, HashMap};
9use crate::ast::{Span, Spanned};
10use crate::compile::{self, Location};
11use crate::runtime::{Inst, InstAddress, Label, Output};
12use crate::{Hash, SourceId};
13
14#[derive(Debug, TryClone)]
15pub(crate) enum AssemblyInst {
16    Jump {
17        label: Label,
18    },
19    JumpIf {
20        addr: InstAddress,
21        label: Label,
22    },
23    JumpIfNot {
24        addr: InstAddress,
25        label: Label,
26    },
27    IterNext {
28        addr: InstAddress,
29        label: Label,
30        out: Output,
31    },
32    Raw {
33        raw: Inst,
34    },
35}
36
37/// Helper structure to build instructions and maintain certain invariants.
38#[derive(Debug, TryClone, Default)]
39pub(crate) struct Assembly {
40    /// The location that caused the assembly.
41    location: Location,
42    /// Registered label by offset.
43    pub(crate) labels: HashMap<usize, (usize, Vec<Label>)>,
44    /// Instructions with spans.
45    pub(crate) instructions: Vec<(AssemblyInst, Span)>,
46    /// Comments associated with instructions.
47    pub(crate) comments: HashMap<usize, String>,
48    /// The number of labels.
49    pub(crate) label_count: usize,
50    /// The collection of functions required by this assembly.
51    pub(crate) required_functions: HashMap<Hash, Vec<(Span, SourceId)>>,
52}
53
54impl Assembly {
55    /// Construct a new assembly.
56    pub(crate) fn new(location: Location, label_count: usize) -> Self {
57        Self {
58            location,
59            labels: Default::default(),
60            instructions: Default::default(),
61            comments: Default::default(),
62            label_count,
63            required_functions: Default::default(),
64        }
65    }
66
67    /// Construct and return a new label.
68    pub(crate) fn new_label(&mut self, name: &'static str) -> Label {
69        let label = Label::new(name, self.label_count);
70        self.label_count += 1;
71        label
72    }
73
74    /// Apply the label at the current instruction offset.
75    pub(crate) fn label(&mut self, label: &Label) -> compile::Result<()> {
76        let len = self.labels.len();
77
78        match self.labels.entry(self.instructions.len()) {
79            hash_map::Entry::Occupied(e) => {
80                let &mut (len, ref mut labels) = e.into_mut();
81                label.set_jump(len);
82                labels.try_push(label.try_clone()?)?;
83            }
84            hash_map::Entry::Vacant(e) => {
85                label.set_jump(len);
86                e.try_insert((len, try_vec![label.try_clone()?]))?;
87            }
88        }
89
90        Ok(())
91    }
92
93    /// Add a jump to the given label.
94    pub(crate) fn jump(&mut self, label: &Label, span: &dyn Spanned) -> compile::Result<()> {
95        self.inner_push(
96            AssemblyInst::Jump {
97                label: label.try_clone()?,
98            },
99            span,
100        )?;
101
102        Ok(())
103    }
104
105    /// Add a conditional jump to the given label.
106    pub(crate) fn jump_if(
107        &mut self,
108        addr: InstAddress,
109        label: &Label,
110        span: &dyn Spanned,
111    ) -> compile::Result<()> {
112        self.inner_push(
113            AssemblyInst::JumpIf {
114                addr,
115                label: label.try_clone()?,
116            },
117            span,
118        )?;
119
120        Ok(())
121    }
122
123    /// Add jump-if-not instruction to a label.
124    pub(crate) fn jump_if_not(
125        &mut self,
126        addr: InstAddress,
127        label: &Label,
128        span: &dyn Spanned,
129    ) -> compile::Result<()> {
130        self.inner_push(
131            AssemblyInst::JumpIfNot {
132                addr,
133                label: label.try_clone()?,
134            },
135            span,
136        )?;
137
138        Ok(())
139    }
140
141    /// Add an instruction that advanced an iterator.
142    pub(crate) fn iter_next(
143        &mut self,
144        addr: InstAddress,
145        label: &Label,
146        span: &dyn Spanned,
147        out: Output,
148    ) -> compile::Result<()> {
149        self.inner_push(
150            AssemblyInst::IterNext {
151                addr,
152                label: label.try_clone()?,
153                out,
154            },
155            span,
156        )?;
157
158        Ok(())
159    }
160
161    /// Push a raw instruction.
162    pub(crate) fn push(&mut self, raw: Inst, span: &dyn Spanned) -> compile::Result<()> {
163        self.inner_push(AssemblyInst::Raw { raw }, span)?;
164        Ok(())
165    }
166
167    /// Push a raw instruction.
168    pub(crate) fn push_with_comment(
169        &mut self,
170        raw: Inst,
171        span: &dyn Spanned,
172        comment: &dyn fmt::Display,
173    ) -> compile::Result<()> {
174        let index = self.instructions.len();
175        let c = self.comments.entry(index).or_try_default()?;
176
177        if !c.is_empty() {
178            c.try_push_str("; ")?;
179        }
180
181        write!(c, "{comment}")?;
182        self.push(raw, span)?;
183        Ok(())
184    }
185
186    fn inner_push(&mut self, inst: AssemblyInst, span: &dyn Spanned) -> compile::Result<()> {
187        if let AssemblyInst::Raw {
188            raw: Inst::Call { hash, .. },
189        } = &inst
190        {
191            self.required_functions
192                .entry(*hash)
193                .or_try_default()?
194                .try_push((span.span(), self.location.source_id))?;
195        }
196
197        self.instructions.try_push((inst, span.span()))?;
198        Ok(())
199    }
200}