From ffe88b8800dd39d801f8be3eb130e2f9b4e8652b Mon Sep 17 00:00:00 2001 From: Simon Larsson Date: Tue, 1 Oct 2024 23:20:35 +0200 Subject: [PATCH] demonstrate and test C-repr structs in FFI example --- examples/ffi/config.lm | 3 +++ examples/ffi/src/main.c | 28 +++++++++++++++++++++++++ examples/ffi/src/main.lm | 33 +++++++++++++++++++++++++----- examples/ffi/src/types.h | 16 +++++++++++++++ lumina-compiler/src/ast/config.rs | 16 ++++++++++----- lumina-compiler/src/backend/mod.rs | 14 +++++++++++-- lumina-compiler/src/hir/mod.rs | 8 +++++++- lumina/src/build.rs | 13 ++++++------ lumina/tests/examples.rs | 6 ++++++ 9 files changed, 118 insertions(+), 19 deletions(-) create mode 100644 examples/ffi/src/main.c create mode 100644 examples/ffi/src/types.h diff --git a/examples/ffi/config.lm b/examples/ffi/config.lm index cd09691..f18ffdb 100644 --- a/examples/ffi/config.lm +++ b/examples/ffi/config.lm @@ -3,3 +3,6 @@ val version = "1.0" val authors = [] val dependencies = [] + +val linker_args = ["-flto"] +val linker_libs = ["src/main.c"] diff --git a/examples/ffi/src/main.c b/examples/ffi/src/main.c new file mode 100644 index 0000000..1c3437a --- /dev/null +++ b/examples/ffi/src/main.c @@ -0,0 +1,28 @@ +#include "types.h" + +struct Outer returns() { + struct Outer outer; + outer.a = 1; + + struct Inner inner; + inner.a = 2; + inner.b = 3; + inner.c = 4; + inner.d = 5; + outer.b = inner; + + outer.c = 6; + + return outer; +} + +unsigned int takes(struct Outer outer) { + if (outer.a != 1) return 10; + if (outer.b.a != 2) return 20; + if (outer.b.b != 3) return 30; + if (outer.b.c != 4) return 40; + if (outer.b.d != 5) return 50; + if (outer.c != 6) return 60; + + return 100; +} diff --git a/examples/ffi/src/main.lm b/examples/ffi/src/main.lm index 2a8dae9..83eeb81 100644 --- a/examples/ffi/src/main.lm +++ b/examples/ffi/src/main.lm @@ -1,8 +1,31 @@ -use std:io - @[extern "exit", platform ["linux-gnu", "linux-musl"]] -fn my_libc_exit_call as i64 -> () +fn libc_exit as i32 -> () fn main = - let exit_code = 5 - in my_libc_exit_call exit_code + let outer = returns in + let n = takes outer in + if n == 100 then + libc_exit 0 + else + libc_exit n + +@[repr "C"] +type Inner { + a u32 + b u8 + c u64 + d u16 +} + +@[repr "C"] +type Outer { + a u16 + b Inner + c u8 +} + +@[extern "takes"] +fn takes as Outer -> i32 + +@[extern "returns"] +fn returns as Outer diff --git a/examples/ffi/src/types.h b/examples/ffi/src/types.h new file mode 100644 index 0000000..9d4cb21 --- /dev/null +++ b/examples/ffi/src/types.h @@ -0,0 +1,16 @@ +struct Inner { + unsigned int a; + unsigned char b; + unsigned long long c; + unsigned short d; +}; + +struct Outer { + unsigned short a; + struct Inner b; + unsigned char c; +}; + + +struct Outer returns(); +unsigned int takes(struct Outer outer); diff --git a/lumina-compiler/src/ast/config.rs b/lumina-compiler/src/ast/config.rs index 1242e23..516810e 100644 --- a/lumina-compiler/src/ast/config.rs +++ b/lumina-compiler/src/ast/config.rs @@ -12,6 +12,8 @@ pub struct ProjectConfig { pub epanic: bool, pub prelude: String, pub dependencies: Vec, + pub linker_args: Vec, + pub linker_libs: Vec, } #[derive(Debug)] @@ -61,15 +63,19 @@ impl ProjectConfig { self.epanic = bool(val.value)?; Ok(()) } - "authors" => { - let authors = self.parse_str_list(val.value)?; - self.authors.extend(authors); - Ok(()) - } + "authors" => self + .parse_str_list(val.value) + .map(|authors| self.authors.extend(authors)), "prelude" => { self.prelude = name(val.value)?; Ok(()) } + "linker_args" => self + .parse_str_list(val.value) + .map(|args| self.linker_args.extend(args)), + "linker_libs" => self + .parse_str_list(val.value) + .map(|args| self.linker_libs.extend(args)), _ => Err(Error::InvalidVal(val.span)), } } diff --git a/lumina-compiler/src/backend/mod.rs b/lumina-compiler/src/backend/mod.rs index 8e1a930..478256e 100644 --- a/lumina-compiler/src/backend/mod.rs +++ b/lumina-compiler/src/backend/mod.rs @@ -1,6 +1,6 @@ pub mod cranelift; -use super::{target::LinuxPlatform, target::Platform, Target}; +use super::{ast, target::LinuxPlatform, target::Platform, Target}; use std::ffi::OsStr; use std::fs::File; use std::io::Write; @@ -10,12 +10,14 @@ use std::process::ExitCode; use tracing::info; pub fn link_native_binary( + config: ast::ProjectConfig, target: Target, - project_name: String, output: &Path, + projectpath: PathBuf, luminapath: PathBuf, object: Vec, ) -> Result<(), ExitCode> { + let project_name = config.name.clone(); let workdir = create_workdir(&luminapath, &project_name); let objectfile = { @@ -45,12 +47,20 @@ pub fn link_native_binary( Command::new(bindir.join("ld.lld")) }; + for arg in config.linker_args { + linker.arg(arg); + } + linker.arg("-o").arg(output).arg(&objectfile); iter_objects(&sublinuxdir, &["o", "a"], |path| { linker.arg(path); }); + for lib in config.linker_libs { + linker.arg(projectpath.join(lib)); + } + linker.arg(linuxdir.join("syscall.o")); linker diff --git a/lumina-compiler/src/hir/mod.rs b/lumina-compiler/src/hir/mod.rs index f5b478f..0184876 100644 --- a/lumina-compiler/src/hir/mod.rs +++ b/lumina-compiler/src/hir/mod.rs @@ -97,7 +97,12 @@ pub fn run<'a, 's>( info: ProjectInfo, target: Target, ast: AST<'s>, -) -> (HIR<'s>, MMap>, ImplIndex) { +) -> ( + ast::ProjectConfig, + HIR<'s>, + MMap>, + ImplIndex, +) { let mut tenvs = ast.entities.fheaders.secondary(); let mut funcs = ast.entities.fheaders.secondary(); @@ -197,6 +202,7 @@ pub fn run<'a, 's>( .map(|_, assoc| assoc.values().map(|a| a.name.tr(a.span)).collect()); ( + ast.config, HIR { fnames: ast.entities.field_names, val_initializers: ast.entities.vals, diff --git a/lumina/src/build.rs b/lumina/src/build.rs index 018eff6..1a49940 100644 --- a/lumina/src/build.rs +++ b/lumina/src/build.rs @@ -20,6 +20,8 @@ pub fn build_project( settings: cli::BuildFlags, ) -> Result { let mut project_path = env.current_directory.clone(); + let lumina_dir = env.lumina_directory.clone(); + if let Some(path) = settings.project { if path.is_absolute() { project_path = path; @@ -35,7 +37,7 @@ pub fn build_project( let ast = match compiler::ast::parse( project_path.clone(), - env.lumina_directory.clone(), + lumina_dir.clone(), settings.epanic, target.clone(), ) { @@ -54,8 +56,7 @@ pub fn build_project( Ok(pinfo) => pinfo, }; - let project_name = ast.config.name.clone(); - let (hir, tenvs, mut iquery) = compiler::hir::run(pinfo, target, ast); + let (pconfig, hir, tenvs, mut iquery) = compiler::hir::run(pinfo, target, ast); let (mir, has_failed) = compiler::mir::run(pinfo, target, hir, tenvs, &mut iquery); if has_failed { @@ -71,13 +72,13 @@ pub fn build_project( Some(name) => { let mut path = std::path::PathBuf::from(name); while path.is_dir() { - path.push(&project_name); + path.push(&pconfig.name); } path } None if run => { let mut path = std::env::temp_dir(); - path.push(&project_name); + path.push(&pconfig.name); path.set_extension(target.executable_extension()); path } @@ -87,7 +88,7 @@ pub fn build_project( } }; - link_native_binary(target, project_name, &output, env.lumina_directory, object)?; + link_native_binary(pconfig, target, &output, project_path, lumina_dir, object)?; Ok(output) } diff --git a/lumina/tests/examples.rs b/lumina/tests/examples.rs index 6cc40d8..4c37814 100644 --- a/lumina/tests/examples.rs +++ b/lumina/tests/examples.rs @@ -86,3 +86,9 @@ fn example_using_ext_library() { fn example_modules() { run("examples/modules", "HelloWorld\nHelloWorld\n"); } + +#[cfg(unix)] +#[test] +fn example_ffi() { + run("examples/ffi", ""); +}