1mod body;
6mod menu;
7mod runtime;
8mod types;
9mod ui;
10
11use std::collections::HashMap;
12use std::mem::{offset_of, size_of};
13
14use cranelift_codegen::ir::types::{I8, I64};
15use cranelift_codegen::ir::{InstBuilder, MemFlags, StackSlotData, StackSlotKind};
16use cranelift_module::{DataDescription, DataId, FuncId, Module};
17use nifty_check::hir::{FnDef, Program, StateDef};
18use nifty_rt::{ABI_VERSION, FrameDesc, ProgramDesc, SourceLocation, SpanDesc, StateDesc, Str};
19use nifty_syntax::Span;
20
21use self::body::{Body, Operand, Returns};
22use self::runtime::Runtime;
23use self::types::Types;
24use crate::CodegenError;
25use crate::emit::{Blob, Emitter, Target};
26
27type Result<T> = std::result::Result<T, CodegenError>;
28
29struct Shared<'p> {
31 program: &'p Program,
32 runtime: Runtime,
33 types: Types,
34 functions: Vec<FuncId>,
36 spans: Spans,
37 texts: HashMap<String, DataId>,
39}
40
41impl Shared<'_> {
42 fn text<M: Module>(&mut self, module: &mut M, text: &str) -> Result<DataId> {
44 if let Some(id) = self.texts.get(text) {
45 return Ok(*id);
46 }
47 let id = module.declare_anonymous_data(false, false)?;
48 let mut description = DataDescription::new();
49 description.define(text.as_bytes().into());
50 module.define_data(id, &description)?;
51 self.texts.insert(text.to_owned(), id);
52 Ok(id)
53 }
54}
55
56#[derive(Debug, Default)]
58struct Spans {
59 ids: HashMap<Span, u32>,
60 spans: Vec<Span>,
61}
62
63impl Spans {
64 fn id(&mut self, span: Span) -> Result<u32> {
65 if let Some(id) = self.ids.get(&span) {
66 return Ok(*id);
67 }
68 let id = u32::try_from(self.spans.len())
69 .map_err(|_| CodegenError::new("the program has too many places that can fail"))?;
70 self.ids.insert(span, id);
71 self.spans.push(span);
72 Ok(id)
73 }
74}
75
76pub fn compile<M: Module>(
82 emitter: &mut Emitter<M>,
83 program: &Program,
84 locate: &dyn Fn(Span) -> SourceLocation,
85) -> Result<DataId> {
86 let runtime = Runtime::import(emitter)?;
87 let functions = program
88 .functions
89 .iter()
90 .map(|function| emitter.declare_function(&function_signature(emitter, function)))
91 .collect::<Result<_>>()?;
92 let mut shared = Shared {
93 program,
94 runtime,
95 types: Types::default(),
96 functions,
97 spans: Spans::default(),
98 texts: HashMap::new(),
99 };
100 for (index, function) in program.functions.iter().enumerate() {
101 let id = shared.functions[index];
102 compile_function(emitter, &mut shared, function, id)?;
103 }
104 let states = program
105 .states
106 .iter()
107 .map(|state| compile_state(emitter, &mut shared, state))
108 .collect::<Result<Vec<_>>>()?;
109 let windows = ui::windows(emitter, &mut shared)?;
110 let views = ui::views(emitter, &mut shared)?;
111 let (menus, menu_locals) = menu::menu_bar(emitter, &mut shared)?;
112 let state_list = state_list(emitter, &mut shared, &states)?;
113 let names = function_names(emitter, &mut shared)?;
114 let spans = span_list(emitter, &mut shared, locate)?;
115 shared.types.emit(emitter, &shared.runtime, program)?;
116
117 let mut desc = Blob::zeroed(size_of::<ProgramDesc>());
118 desc.put_u32(offset_of!(ProgramDesc, abi_version), ABI_VERSION);
119 for (field, list) in [
120 (offset_of!(ProgramDesc, states), state_list),
121 (offset_of!(ProgramDesc, windows), windows),
122 (offset_of!(ProgramDesc, views), views),
123 (offset_of!(ProgramDesc, functions), names),
124 (offset_of!(ProgramDesc, spans), spans),
125 (offset_of!(ProgramDesc, menus), menus),
126 (
127 offset_of!(ProgramDesc, menu_frame) + offset_of!(FrameDesc, locals),
128 menu_locals,
129 ),
130 ] {
131 list.put(&mut desc, field);
132 }
133 emitter.data(desc)
134}
135
136#[derive(Clone, Copy, Debug)]
138struct List {
139 data: Option<DataId>,
140 len: usize,
141}
142
143impl List {
144 const EMPTY: Self = Self { data: None, len: 0 };
145
146 fn emit<M: Module>(
149 emitter: &mut Emitter<M>,
150 len: usize,
151 size: usize,
152 mut write: impl FnMut(&mut Blob, usize, usize) -> Result<()>,
153 ) -> Result<Self> {
154 if len == 0 {
155 return Ok(Self::EMPTY);
156 }
157 let mut blob = Blob::zeroed(len * size);
158 for index in 0..len {
159 write(&mut blob, index, index * size)?;
160 }
161 Ok(Self {
162 data: Some(emitter.data(blob)?),
163 len,
164 })
165 }
166
167 fn put(self, blob: &mut Blob, offset: usize) {
168 blob.put_slice(offset, self.data.map(Target::Data), self.len);
169 }
170}
171
172fn put_str<M: Module>(
174 emitter: &mut Emitter<M>,
175 shared: &mut Shared<'_>,
176 blob: &mut Blob,
177 offset: usize,
178 text: &str,
179) -> Result<()> {
180 let bytes = (!text.is_empty())
181 .then(|| shared.text(emitter.module_mut(), text))
182 .transpose()?;
183 blob.put_slice(offset, bytes.map(Target::Data), text.len());
184 Ok(())
185}
186
187fn function_signature<M: Module>(
190 emitter: &Emitter<M>,
191 function: &FnDef,
192) -> cranelift_codegen::ir::Signature {
193 let mut params = vec![I64];
194 params.extend(std::iter::repeat_n(I64, function.params));
195 params.push(I64);
196 emitter.signature(¶ms, &[I8])
197}
198
199fn compile_function<M: Module>(
202 emitter: &mut Emitter<M>,
203 shared: &mut Shared<'_>,
204 function: &FnDef,
205 id: FuncId,
206) -> Result<()> {
207 let signature = function_signature(emitter, function);
208 emitter.define_function(id, signature, |b, module| {
209 let entry = b
210 .current_block()
211 .ok_or_else(|| CodegenError::new("a function has no entry block"))?;
212 let params = b.block_params(entry).to_vec();
213 let (context, out) = (params[0], params[params.len() - 1]);
214 let frame_size = u32::try_from(function.locals.len() * 8)
215 .map_err(|_| CodegenError::new(format!("`{}` has too many locals", function.name)))?;
216 let slot = b.create_sized_stack_slot(StackSlotData::new(
217 StackSlotKind::ExplicitSlot,
218 frame_size.max(8),
219 3,
220 ));
221 let frame = b.ins().stack_addr(I64, slot, 0);
222 let zero = b.ins().iconst(I64, 0);
223 for local in 0..function.locals.len() {
224 let value = params.get(local + 1).filter(|_| local < function.params);
225 let at =
226 i32::try_from(local * 8).map_err(|_| CodegenError::new("a frame is too large"))?;
227 b.ins().store(
228 MemFlags::trusted(),
229 value.copied().unwrap_or(zero),
230 frame,
231 at,
232 );
233 }
234 let mut body = Body::new(b, module, shared, context, frame, &function.locals);
235 body.returns = Some(Returns { out });
236 body.block(&function.body)?;
237 body.release_locals()?;
238 body.finish();
239 Ok(())
240 })
241}
242
243fn compile_state<M: Module>(
245 emitter: &mut Emitter<M>,
246 shared: &mut Shared<'_>,
247 state: &StateDef,
248) -> Result<FuncId> {
249 let signature = emitter.signature(&[I64, I64], &[I8]);
250 emitter.function(signature, |b, module| {
251 let entry = b
252 .current_block()
253 .ok_or_else(|| CodegenError::new("a function has no entry block"))?;
254 let (context, out) = (b.block_params(entry)[0], b.block_params(entry)[1]);
255 let frame = b.ins().iconst(I64, 0);
256 let mut body = Body::new(b, module, shared, context, frame, &[]);
257 let operand: Operand = body.expr(&state.init)?;
258 let value = body.own(operand, &state.ty)?;
259 body.b.ins().store(MemFlags::trusted(), value, out, 0);
260 body.finish();
261 Ok(())
262 })
263}
264
265fn state_list<M: Module>(
266 emitter: &mut Emitter<M>,
267 shared: &mut Shared<'_>,
268 inits: &[FuncId],
269) -> Result<List> {
270 let types = shared
271 .program
272 .states
273 .iter()
274 .map(|state| {
275 shared
276 .types
277 .desc(emitter.module_mut(), &shared.runtime, &state.ty)
278 })
279 .collect::<Result<Vec<_>>>()?;
280 List::emit(
281 emitter,
282 inits.len(),
283 size_of::<StateDesc>(),
284 |blob, index, at| {
285 blob.put_pointer(at + offset_of!(StateDesc, ty), Target::Data(types[index]));
286 blob.put_pointer(
287 at + offset_of!(StateDesc, init),
288 Target::Function(inits[index]),
289 );
290 Ok(())
291 },
292 )
293}
294
295fn function_names<M: Module>(emitter: &mut Emitter<M>, shared: &mut Shared<'_>) -> Result<List> {
296 let names = shared
297 .program
298 .functions
299 .iter()
300 .map(|function| shared.text(emitter.module_mut(), &function.name))
301 .collect::<Result<Vec<_>>>()?;
302 let functions = &shared.program.functions;
303 List::emit(emitter, names.len(), size_of::<Str>(), |blob, index, at| {
304 blob.put_slice(
305 at,
306 Some(Target::Data(names[index])),
307 functions[index].name.len(),
308 );
309 Ok(())
310 })
311}
312
313fn span_list<M: Module>(
314 emitter: &mut Emitter<M>,
315 shared: &mut Shared<'_>,
316 locate: &dyn Fn(Span) -> SourceLocation,
317) -> Result<List> {
318 let locations: Vec<SourceLocation> = shared
319 .spans
320 .spans
321 .iter()
322 .map(|span| locate(*span))
323 .collect();
324 let files = locations
325 .iter()
326 .map(|location| shared.text(emitter.module_mut(), &location.file))
327 .collect::<Result<Vec<_>>>()?;
328 List::emit(
329 emitter,
330 locations.len(),
331 size_of::<SpanDesc>(),
332 |blob, index, at| {
333 let location = &locations[index];
334 blob.put_slice(
335 at + offset_of!(SpanDesc, file),
336 Some(Target::Data(files[index])),
337 location.file.len(),
338 );
339 blob.put_u32(at + offset_of!(SpanDesc, line), location.line);
340 blob.put_u32(at + offset_of!(SpanDesc, column), location.column);
341 Ok(())
342 },
343 )
344}