1use 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#[derive(Debug, TryClone, Default)]
39pub(crate) struct Assembly {
40 location: Location,
42 pub(crate) labels: HashMap<usize, (usize, Vec<Label>)>,
44 pub(crate) instructions: Vec<(AssemblyInst, Span)>,
46 pub(crate) comments: HashMap<usize, String>,
48 pub(crate) label_count: usize,
50 pub(crate) required_functions: HashMap<Hash, Vec<(Span, SourceId)>>,
52}
53
54impl Assembly {
55 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 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 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 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 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 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 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 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 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}