Skip to main content

nifty_codegen/
object.rs

1//! Object files for `nf build`: a compiled program and the `main` that runs it.
2
3use cranelift_object::{ObjectBuilder, ObjectModule};
4use nifty_check::hir::Program;
5use nifty_rt::SourceLocation;
6use nifty_syntax::Span;
7
8use crate::CodegenError;
9use crate::compile::compile;
10use crate::emit::{Emitter, host_isa};
11
12/// Compiles `program` to an object file whose `main` runs it, with `locate` saying where
13/// each span is, for the errors the app may report.
14///
15/// # Errors
16/// When the program has errors, or Cranelift can't compile it for this system.
17pub fn compile_app(
18    name: &str,
19    program: &Program,
20    locate: &dyn Fn(Span) -> SourceLocation,
21) -> Result<Vec<u8>, CodegenError> {
22    let mut emitter = object_emitter(name)?;
23    let descriptor = compile(&mut emitter, program, locate)?;
24    emitter.define_main(descriptor)?;
25    finish(emitter)
26}
27
28/// An empty object file for the system `nf` runs on, named `name` inside.
29///
30/// # Errors
31/// When Cranelift doesn't support this system.
32pub fn object_emitter(name: &str) -> Result<Emitter<ObjectModule>, CodegenError> {
33    let builder = ObjectBuilder::new(
34        host_isa(true)?,
35        name,
36        cranelift_module::default_libcall_names(),
37    )?;
38    Ok(Emitter::new(ObjectModule::new(builder)))
39}
40
41/// The object file's bytes.
42///
43/// # Errors
44/// When the object can't be written.
45pub fn finish(emitter: Emitter<ObjectModule>) -> Result<Vec<u8>, CodegenError> {
46    let product = emitter.into_module().finish();
47    #[cfg(target_os = "macos")]
48    let product = {
49        let mut product = product;
50        product
51            .object
52            .set_macho_build_version(macos_build_version());
53        product
54    };
55    product
56        .emit()
57        .map_err(|error| CodegenError::new(error.to_string()))
58}
59
60/// The oldest macOS compiled apps run on: the oldest Apple-silicon Macs run, which Rust,
61/// and so the runtime library, also targets.
62#[cfg(target_os = "macos")]
63const MINIMUM_MACOS: (u32, u32) = (11, 0);
64
65/// Says which macOS the object is for, which the linker otherwise warns it has to assume.
66#[cfg(target_os = "macos")]
67fn macos_build_version() -> object::write::MachOBuildVersion {
68    let (major, minor) = MINIMUM_MACOS;
69    let mut version = object::write::MachOBuildVersion::default();
70    version.platform = object::macho::PLATFORM_MACOS;
71    // Versions are encoded in nibbles as xxxx.yy.zz.
72    version.minos = (major << 16) | (minor << 8);
73    version
74}