Skip to main content

nifty_codegen/
emit.rs

1//! Writing functions and data into a Cranelift module: an object file for `nf build`, or
2//! memory for code loaded in-process.
3
4use std::fmt;
5
6use cranelift_codegen::Context;
7use cranelift_codegen::ir::{AbiParam, InstBuilder, Signature, Type, types};
8use cranelift_codegen::isa::OwnedTargetIsa;
9use cranelift_codegen::settings::{self, Configurable};
10use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext};
11use cranelift_module::{DataDescription, DataId, FuncId, Linkage, Module};
12use nifty_rt::{Slice, symbols};
13
14use crate::CodegenError;
15
16/// What a pointer in data points at.
17#[derive(Clone, Copy, Debug)]
18pub enum Target {
19    Data(DataId),
20    Function(FuncId),
21}
22
23/// The bytes of one piece of data, and the pointers in it, which the linker fills in.
24#[derive(Debug)]
25pub struct Blob {
26    bytes: Vec<u8>,
27    pointers: Vec<(usize, Target)>,
28}
29
30impl Blob {
31    /// `size` zero bytes, which read as empty slices, null pointers and zeros.
32    #[must_use]
33    pub fn zeroed(size: usize) -> Self {
34        Self {
35            bytes: vec![0; size],
36            pointers: Vec::new(),
37        }
38    }
39
40    #[must_use]
41    pub fn bytes(bytes: &[u8]) -> Self {
42        Self {
43            bytes: bytes.to_vec(),
44            pointers: Vec::new(),
45        }
46    }
47
48    pub fn put_u8(&mut self, offset: usize, value: u8) {
49        self.put(offset, &[value]);
50    }
51
52    pub fn put_u32(&mut self, offset: usize, value: u32) {
53        self.put(offset, &value.to_ne_bytes());
54    }
55
56    pub fn put_i64(&mut self, offset: usize, value: i64) {
57        self.put(offset, &value.to_ne_bytes());
58    }
59
60    pub fn put_f32(&mut self, offset: usize, value: f32) {
61        self.put(offset, &value.to_ne_bytes());
62    }
63
64    pub fn put_usize(&mut self, offset: usize, value: usize) {
65        self.put(offset, &value.to_ne_bytes());
66    }
67
68    pub fn put_pointer(&mut self, offset: usize, target: Target) {
69        self.pointers.push((offset, target));
70    }
71
72    /// A `nifty_rt::Slice` or `Str` at `offset`: `len` items starting at `items`, or an
73    /// empty one when there are none.
74    pub fn put_slice(&mut self, offset: usize, items: Option<Target>, len: usize) {
75        if let Some(items) = items {
76            self.put_pointer(offset + Slice::<u8>::PTR_OFFSET, items);
77        }
78        self.put_usize(offset + Slice::<u8>::LEN_OFFSET, len);
79    }
80
81    /// # Panics
82    /// When the value doesn't fit inside the blob, which is a bug in the caller's layout.
83    fn put(&mut self, offset: usize, value: &[u8]) {
84        self.bytes[offset..offset + value.len()].copy_from_slice(value);
85    }
86}
87
88/// The instruction set of the system `nf` runs on. Code loaded in-process must not be
89/// position-independent; executables on macOS and Windows must be, since they load at a
90/// random address.
91///
92/// # Errors
93/// When Cranelift doesn't support this system.
94pub fn host_isa(position_independent: bool) -> Result<OwnedTargetIsa, CodegenError> {
95    let mut flags = settings::builder();
96    flags.set(
97        "is_pic",
98        if position_independent {
99            "true"
100        } else {
101            "false"
102        },
103    )?;
104    flags.set("opt_level", "speed")?;
105    Ok(cranelift_native::builder()
106        .map_err(CodegenError::new)?
107        .finish(settings::Flags::new(flags))?)
108}
109
110/// A module being written, with the contexts Cranelift reuses between functions.
111pub struct Emitter<M: Module> {
112    module: M,
113    context: Context,
114    builder_context: FunctionBuilderContext,
115}
116
117impl<M: Module> Emitter<M> {
118    pub fn new(module: M) -> Self {
119        Self {
120            module,
121            context: Context::new(),
122            builder_context: FunctionBuilderContext::new(),
123        }
124    }
125
126    pub fn into_module(self) -> M {
127        self.module
128    }
129
130    pub(crate) fn module_mut(&mut self) -> &mut M {
131        &mut self.module
132    }
133
134    #[must_use]
135    pub fn pointer_type(&self) -> Type {
136        self.module.target_config().pointer_type()
137    }
138
139    /// A signature in the system's C calling convention.
140    #[must_use]
141    pub fn signature(&self, params: &[Type], returns: &[Type]) -> Signature {
142        let mut signature = self.module.make_signature();
143        signature
144            .params
145            .extend(params.iter().copied().map(AbiParam::new));
146        signature
147            .returns
148            .extend(returns.iter().copied().map(AbiParam::new));
149        signature
150    }
151
152    /// A runtime function, called by name.
153    ///
154    /// # Errors
155    /// When `name` was already declared with another signature.
156    pub fn import_function(
157        &mut self,
158        name: &str,
159        signature: &Signature,
160    ) -> Result<FuncId, CodegenError> {
161        Ok(self
162            .module
163            .declare_function(name, Linkage::Import, signature)?)
164    }
165
166    /// Runtime data, such as a built-in type, referred to by name.
167    ///
168    /// # Errors
169    /// When `name` was already declared as something else.
170    pub fn import_data(&mut self, name: &str) -> Result<DataId, CodegenError> {
171        Ok(self
172            .module
173            .declare_data(name, Linkage::Import, false, false)?)
174    }
175
176    /// Read-only data private to this module, defined later with [`Emitter::define_data`],
177    /// so other data can point at it first.
178    ///
179    /// # Errors
180    /// When Cranelift can't declare it.
181    pub fn declare_data(&mut self) -> Result<DataId, CodegenError> {
182        Ok(self.module.declare_anonymous_data(false, false)?)
183    }
184
185    /// # Errors
186    /// When Cranelift can't define it.
187    pub fn define_data(&mut self, id: DataId, blob: Blob) -> Result<(), CodegenError> {
188        let mut description = DataDescription::new();
189        description.set_align(8);
190        for (offset, target) in &blob.pointers {
191            let offset = u32::try_from(*offset).map_err(|_| {
192                CodegenError::new(format!("data is too large to point at offset {offset}"))
193            })?;
194            match *target {
195                Target::Data(data) => {
196                    let global = self.module.declare_data_in_data(data, &mut description);
197                    description.write_data_addr(offset, global, 0);
198                }
199                Target::Function(function) => {
200                    let function = self.module.declare_func_in_data(function, &mut description);
201                    description.write_function_addr(offset, function);
202                }
203            }
204        }
205        description.define(blob.bytes.into_boxed_slice());
206        self.module.define_data(id, &description)?;
207        Ok(())
208    }
209
210    /// Declares and defines read-only data private to this module.
211    ///
212    /// # Errors
213    /// When Cranelift can't define it.
214    pub fn data(&mut self, blob: Blob) -> Result<DataId, CodegenError> {
215        let id = self.declare_data()?;
216        self.define_data(id, blob)?;
217        Ok(id)
218    }
219
220    /// A function private to this module, compiled later with
221    /// [`Emitter::define_function`], so code can call it first.
222    ///
223    /// # Errors
224    /// When Cranelift can't declare it.
225    pub fn declare_function(&mut self, signature: &Signature) -> Result<FuncId, CodegenError> {
226        Ok(self.module.declare_anonymous_function(signature)?)
227    }
228
229    /// Compiles `id`, whose body `build` emits from its entry block, which has the
230    /// signature's parameters and is current when `build` starts. `build` also gets the
231    /// module, to refer to other functions and data.
232    ///
233    /// # Errors
234    /// The error `build` returns, or Cranelift's when it can't compile the body.
235    pub fn define_function(
236        &mut self,
237        id: FuncId,
238        signature: Signature,
239        build: impl FnOnce(&mut FunctionBuilder<'_>, &mut M) -> Result<(), CodegenError>,
240    ) -> Result<(), CodegenError> {
241        self.context.func.signature = signature;
242        let mut builder = FunctionBuilder::new(&mut self.context.func, &mut self.builder_context);
243        let entry = builder.create_block();
244        builder.append_block_params_for_function_params(entry);
245        builder.switch_to_block(entry);
246        let body = build(&mut builder, &mut self.module);
247        builder.seal_all_blocks();
248        builder.finalize();
249        let defined = body.and_then(|()| Ok(self.module.define_function(id, &mut self.context)?));
250        self.module.clear_context(&mut self.context);
251        defined
252    }
253
254    /// Declares and compiles a function private to this module.
255    ///
256    /// # Errors
257    /// As [`Emitter::define_function`].
258    pub fn function(
259        &mut self,
260        signature: Signature,
261        build: impl FnOnce(&mut FunctionBuilder<'_>, &mut M) -> Result<(), CodegenError>,
262    ) -> Result<FuncId, CodegenError> {
263        let id = self.declare_function(&signature)?;
264        self.define_function(id, signature, build)?;
265        Ok(id)
266    }
267
268    /// Emits `main`, which hands `program`, a `nifty_rt::ProgramDesc`, to the runtime and
269    /// returns the exit status it gives back.
270    ///
271    /// # Errors
272    /// When Cranelift can't compile it.
273    pub fn define_main(&mut self, program: DataId) -> Result<(), CodegenError> {
274        let pointer = self.pointer_type();
275        let run =
276            self.import_function(symbols::MAIN, &self.signature(&[pointer], &[types::I32]))?;
277        let signature = self.signature(&[types::I32, pointer], &[types::I32]);
278        let main = self
279            .module
280            .declare_function("main", Linkage::Export, &signature)?;
281        self.define_function(main, signature, |builder, module| {
282            let global = module.declare_data_in_func(program, builder.func);
283            let program = builder.ins().global_value(pointer, global);
284            let run = module.declare_func_in_func(run, builder.func);
285            let call = builder.ins().call(run, &[program]);
286            let status = builder.inst_results(call).to_vec();
287            builder.ins().return_(&status);
288            Ok(())
289        })
290    }
291}
292
293impl<M: Module> fmt::Debug for Emitter<M> {
294    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
295        f.debug_struct("Emitter").finish_non_exhaustive()
296    }
297}