From 18e6fb66effc3657a8c301c3c2aae10e7f5ccad6 Mon Sep 17 00:00:00 2001 From: treemcgee42 Date: Tue, 16 Jan 2024 11:16:52 -0500 Subject: [PATCH 01/24] Add basic driver tests Driver tests test the compilation of an entire file or collection of files, from generating executables to linking libraries. These are particularly helpful for testing compatibility on different platforms. --- cobalt-cli/src/lib.rs | 1 + cobalt-cli/src/tests/hello_world.rs | 104 ++++++++++++++++++ cobalt-cli/src/tests/inputs/hello_world.co | 4 + .../tests/inputs/hello_world_linked_lib.co | 5 + .../tests/inputs/hello_world_linked_main.co | 0 cobalt-cli/src/tests/mod.rs | 3 + cobalt-cli/src/tests/outputs/.gitignore | 2 + 7 files changed, 119 insertions(+) create mode 100644 cobalt-cli/src/tests/hello_world.rs create mode 100644 cobalt-cli/src/tests/inputs/hello_world.co create mode 100644 cobalt-cli/src/tests/inputs/hello_world_linked_lib.co create mode 100644 cobalt-cli/src/tests/inputs/hello_world_linked_main.co create mode 100644 cobalt-cli/src/tests/mod.rs create mode 100644 cobalt-cli/src/tests/outputs/.gitignore diff --git a/cobalt-cli/src/lib.rs b/cobalt-cli/src/lib.rs index abcd2b8c..355b2575 100644 --- a/cobalt-cli/src/lib.rs +++ b/cobalt-cli/src/lib.rs @@ -19,6 +19,7 @@ use std::io::{prelude::*, BufReader}; use std::path::{Path, PathBuf}; use std::process::Command; use std::time::{Duration, Instant}; +mod tests; #[derive(Default, Debug, Clone, Copy, PartialEq, Eq, ValueEnum)] pub enum OutputType { diff --git a/cobalt-cli/src/tests/hello_world.rs b/cobalt-cli/src/tests/hello_world.rs new file mode 100644 index 00000000..598a473f --- /dev/null +++ b/cobalt-cli/src/tests/hello_world.rs @@ -0,0 +1,104 @@ +use super::*; + +#[test] +fn test_hello_world_aot() { + let input_path_str = "src/tests/inputs/hello_world.co"; + let output_path_str = "src/tests/outputs/hello_world"; + + let input = clio::Input::new(input_path_str); + assert!(input.is_ok(), "(clio) failed to load input: {:?}", &input.err()); + let output = clio::OutputPath::new(output_path_str); + assert!(output.is_ok(), "(clio) failed to load output: {:?}", &output.err()); + + let cli = Cli::Aot { + input: input.unwrap(), + output: Some(output.unwrap()), + linked: vec![], + link_dirs: vec![], + headers: vec![], + triple: None, + emit: OutputType::Executable, + profile: None, + continue_if_err: true, + debug_mangle: false, + no_default_link: false, + timings: false, + }; + + let result = driver(cli); + assert!(result.is_ok(), "failure while compiling file: {:?}", &result.err()); + + let command_output = Command::new(output_path_str) + .output() + .expect("Failed to execute command"); + let output_str = std::str::from_utf8(&command_output.stdout) + .expect("Output is not valid UTF-8"); + assert_eq!(output_str.trim(), "Hello, world!"); +} + +#[test] +fn test_hello_world_aot_linked() { + let input_main_path_str = "src/tests/inputs/hello_world_linked_main.co"; + let input_lib_path_str = "src/tests/inputs/hello_world_linked_lib.co"; + let output_lib_path_str = "src/tests/outputs/libhello_world_linked.o"; + let output_main_path_str = "src/tests/outputs/hello_world_linked"; + + // --- + + let input_lib = clio::Input::new(input_lib_path_str); + assert!(input_lib.is_ok(), "(clio) failed to load input: {:?}", &input_lib.err()); + let output_lib = clio::OutputPath::new(output_lib_path_str); + assert!(output_lib.is_ok(), "(clio) failed to load output: {:?}", &output_lib.err()); + + let cli_lib = Cli::Aot { + input: input_lib.unwrap(), + output: Some(output_lib.unwrap()), + linked: vec![], + link_dirs: vec![], + headers: vec![], + triple: None, + emit: OutputType::Library, + profile: None, + continue_if_err: true, + debug_mangle: false, + no_default_link: false, + timings: false, + }; + + let result_lib = driver(cli_lib); + assert!(result_lib.is_ok(), "failure while compiling library file: {:?}", &result_lib.err()); + + // --- + + let input_main = clio::Input::new(input_main_path_str); + assert!(input_main.is_ok(), "(clio) failed to load input: {:?}", &input_main.err()); + let output_main = clio::OutputPath::new(output_main_path_str); + assert!(output_main.is_ok(), "(clio) failed to load output: {:?}", &output_main.err()); + + let cli_main = Cli::Aot { + input: input_main.unwrap(), + output: Some(output_main.unwrap()), + linked: vec!["hello_world_linked".to_string()], + link_dirs: vec!["src/tests/outputs".into()], + headers: vec![], + triple: None, + emit: OutputType::Executable, + profile: None, + continue_if_err: true, + debug_mangle: false, + no_default_link: false, + timings: false, + }; + + let result_main = driver(cli_main); + assert!(result_main.is_ok(), "failure while compiling main file: {:?}", &result_main.err()); + + // --- + + let command_output = Command::new(output_main_path_str) + .output() + .expect("Failed to execute command"); + let output_str = std::str::from_utf8(&command_output.stdout) + .expect("Output is not valid UTF-8"); + assert_eq!(output_str.trim(), "Hello, world!"); +} \ No newline at end of file diff --git a/cobalt-cli/src/tests/inputs/hello_world.co b/cobalt-cli/src/tests/inputs/hello_world.co new file mode 100644 index 00000000..666c680b --- /dev/null +++ b/cobalt-cli/src/tests/inputs/hello_world.co @@ -0,0 +1,4 @@ +fn main(): i32 = { + hello_world(); + 0 +}; diff --git a/cobalt-cli/src/tests/inputs/hello_world_linked_lib.co b/cobalt-cli/src/tests/inputs/hello_world_linked_lib.co new file mode 100644 index 00000000..4207228b --- /dev/null +++ b/cobalt-cli/src/tests/inputs/hello_world_linked_lib.co @@ -0,0 +1,5 @@ +@C(extern) fn puts(str: *u8); + +fn hello_world() = { + puts("Hello, world!"c); +}; \ No newline at end of file diff --git a/cobalt-cli/src/tests/inputs/hello_world_linked_main.co b/cobalt-cli/src/tests/inputs/hello_world_linked_main.co new file mode 100644 index 00000000..e69de29b diff --git a/cobalt-cli/src/tests/mod.rs b/cobalt-cli/src/tests/mod.rs new file mode 100644 index 00000000..edf4d618 --- /dev/null +++ b/cobalt-cli/src/tests/mod.rs @@ -0,0 +1,3 @@ +use super::*; + +mod hello_world; \ No newline at end of file diff --git a/cobalt-cli/src/tests/outputs/.gitignore b/cobalt-cli/src/tests/outputs/.gitignore new file mode 100644 index 00000000..d6b7ef32 --- /dev/null +++ b/cobalt-cli/src/tests/outputs/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore From 9ac66b0d172438832bf8a877730a3edb9e40e941 Mon Sep 17 00:00:00 2001 From: treemcgee42 Date: Tue, 16 Jan 2024 11:16:52 -0500 Subject: [PATCH 02/24] Add basic driver tests Driver tests test the compilation of an entire file or collection of files, from generating executables to linking libraries. These are particularly helpful for testing compatibility on different platforms. --- cobalt-cli/src/tests/inputs/hello_world.co | 4 +++- cobalt-cli/src/tests/inputs/hello_world_linked_main.co | 4 ++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/cobalt-cli/src/tests/inputs/hello_world.co b/cobalt-cli/src/tests/inputs/hello_world.co index 666c680b..6d79b191 100644 --- a/cobalt-cli/src/tests/inputs/hello_world.co +++ b/cobalt-cli/src/tests/inputs/hello_world.co @@ -1,4 +1,6 @@ +@C(extern) fn puts(str: *u8); + fn main(): i32 = { - hello_world(); + puts("Hello, world!"c); 0 }; diff --git a/cobalt-cli/src/tests/inputs/hello_world_linked_main.co b/cobalt-cli/src/tests/inputs/hello_world_linked_main.co index e69de29b..85d35799 100644 --- a/cobalt-cli/src/tests/inputs/hello_world_linked_main.co +++ b/cobalt-cli/src/tests/inputs/hello_world_linked_main.co @@ -0,0 +1,4 @@ +fn main(): i32 = { + hello_world(); + 0 +}; \ No newline at end of file From ea28b7eecc403d4ce1a0a05d5176899e835dd55e Mon Sep 17 00:00:00 2001 From: treemcgee42 Date: Tue, 16 Jan 2024 11:16:52 -0500 Subject: [PATCH 03/24] Add basic driver tests Driver tests test the compilation of an entire file or collection of files, from generating executables to linking libraries. These are particularly helpful for testing compatibility on different platforms. --- cobalt-cli/src/lib.rs | 1 + cobalt-cli/src/tests/hello_world.rs | 158 +++++++++++++++++----------- 2 files changed, 98 insertions(+), 61 deletions(-) diff --git a/cobalt-cli/src/lib.rs b/cobalt-cli/src/lib.rs index 355b2575..3f587664 100644 --- a/cobalt-cli/src/lib.rs +++ b/cobalt-cli/src/lib.rs @@ -19,6 +19,7 @@ use std::io::{prelude::*, BufReader}; use std::path::{Path, PathBuf}; use std::process::Command; use std::time::{Duration, Instant}; +#[cfg(test)] mod tests; #[derive(Default, Debug, Clone, Copy, PartialEq, Eq, ValueEnum)] diff --git a/cobalt-cli/src/tests/hello_world.rs b/cobalt-cli/src/tests/hello_world.rs index 598a473f..a1f72ad7 100644 --- a/cobalt-cli/src/tests/hello_world.rs +++ b/cobalt-cli/src/tests/hello_world.rs @@ -6,99 +6,135 @@ fn test_hello_world_aot() { let output_path_str = "src/tests/outputs/hello_world"; let input = clio::Input::new(input_path_str); - assert!(input.is_ok(), "(clio) failed to load input: {:?}", &input.err()); + assert!( + input.is_ok(), + "(clio) failed to load input: {:?}", + &input.err() + ); let output = clio::OutputPath::new(output_path_str); - assert!(output.is_ok(), "(clio) failed to load output: {:?}", &output.err()); - - let cli = Cli::Aot { - input: input.unwrap(), - output: Some(output.unwrap()), - linked: vec![], - link_dirs: vec![], - headers: vec![], - triple: None, - emit: OutputType::Executable, - profile: None, - continue_if_err: true, - debug_mangle: false, - no_default_link: false, + assert!( + output.is_ok(), + "(clio) failed to load output: {:?}", + &output.err() + ); + + let cli = Cli::Aot { + input: input.unwrap(), + output: Some(output.unwrap()), + linked: vec![], + link_dirs: vec![], + headers: vec![], + triple: None, + emit: OutputType::Executable, + profile: None, + continue_if_err: true, + debug_mangle: false, + no_default_link: false, timings: false, }; let result = driver(cli); - assert!(result.is_ok(), "failure while compiling file: {:?}", &result.err()); - + assert!( + result.is_ok(), + "failure while compiling file: {:?}", + &result.err() + ); + let command_output = Command::new(output_path_str) .output() .expect("Failed to execute command"); - let output_str = std::str::from_utf8(&command_output.stdout) - .expect("Output is not valid UTF-8"); + let output_str = + std::str::from_utf8(&command_output.stdout).expect("Output is not valid UTF-8"); assert_eq!(output_str.trim(), "Hello, world!"); } #[test] fn test_hello_world_aot_linked() { - let input_main_path_str = "src/tests/inputs/hello_world_linked_main.co"; + let input_main_path_str = "src/tests/inputs/hello_world_linked_main.co"; let input_lib_path_str = "src/tests/inputs/hello_world_linked_lib.co"; let output_lib_path_str = "src/tests/outputs/libhello_world_linked.o"; let output_main_path_str = "src/tests/outputs/hello_world_linked"; - - // --- - + + // --- + let input_lib = clio::Input::new(input_lib_path_str); - assert!(input_lib.is_ok(), "(clio) failed to load input: {:?}", &input_lib.err()); + assert!( + input_lib.is_ok(), + "(clio) failed to load input: {:?}", + &input_lib.err() + ); let output_lib = clio::OutputPath::new(output_lib_path_str); - assert!(output_lib.is_ok(), "(clio) failed to load output: {:?}", &output_lib.err()); - - let cli_lib = Cli::Aot { - input: input_lib.unwrap(), - output: Some(output_lib.unwrap()), - linked: vec![], - link_dirs: vec![], - headers: vec![], - triple: None, - emit: OutputType::Library, - profile: None, - continue_if_err: true, - debug_mangle: false, - no_default_link: false, + assert!( + output_lib.is_ok(), + "(clio) failed to load output: {:?}", + &output_lib.err() + ); + + let cli_lib = Cli::Aot { + input: input_lib.unwrap(), + output: Some(output_lib.unwrap()), + linked: vec![], + link_dirs: vec![], + headers: vec![], + triple: None, + emit: OutputType::Library, + profile: None, + continue_if_err: true, + debug_mangle: false, + no_default_link: false, timings: false, }; let result_lib = driver(cli_lib); - assert!(result_lib.is_ok(), "failure while compiling library file: {:?}", &result_lib.err()); + assert!( + result_lib.is_ok(), + "failure while compiling library file: {:?}", + &result_lib.err() + ); // --- let input_main = clio::Input::new(input_main_path_str); - assert!(input_main.is_ok(), "(clio) failed to load input: {:?}", &input_main.err()); + assert!( + input_main.is_ok(), + "(clio) failed to load input: {:?}", + &input_main.err() + ); let output_main = clio::OutputPath::new(output_main_path_str); - assert!(output_main.is_ok(), "(clio) failed to load output: {:?}", &output_main.err()); - - let cli_main = Cli::Aot { - input: input_main.unwrap(), - output: Some(output_main.unwrap()), - linked: vec!["hello_world_linked".to_string()], - link_dirs: vec!["src/tests/outputs".into()], - headers: vec![], - triple: None, - emit: OutputType::Executable, - profile: None, - continue_if_err: true, - debug_mangle: false, - no_default_link: false, + assert!( + output_main.is_ok(), + "(clio) failed to load output: {:?}", + &output_main.err() + ); + + let cli_main = Cli::Aot { + input: input_main.unwrap(), + output: Some(output_main.unwrap()), + linked: vec!["hello_world_linked".to_string()], + link_dirs: vec!["src/tests/outputs".into()], + headers: vec![], + triple: None, + emit: OutputType::Executable, + profile: None, + continue_if_err: true, + debug_mangle: false, + no_default_link: false, timings: false, }; - + let result_main = driver(cli_main); - assert!(result_main.is_ok(), "failure while compiling main file: {:?}", &result_main.err()); - + assert!( + result_main.is_ok(), + "failure while compiling main file: {:?}", + &result_main.err() + ); + // --- - + let command_output = Command::new(output_main_path_str) .output() .expect("Failed to execute command"); - let output_str = std::str::from_utf8(&command_output.stdout) - .expect("Output is not valid UTF-8"); + let output_str = + std::str::from_utf8(&command_output.stdout).expect("Output is not valid UTF-8"); assert_eq!(output_str.trim(), "Hello, world!"); -} \ No newline at end of file +} From ea1e24e8c62c298f312e4c8f8e8f7bbf44254dbb Mon Sep 17 00:00:00 2001 From: Matthew Cornell Date: Wed, 17 Jan 2024 13:00:48 -0500 Subject: [PATCH 04/24] Fix build issues --- cobalt-build/src/lib.rs | 2 ++ cobalt-cli/src/tests/hello_world.rs | 11 ++++++----- cobalt-cli/src/tests/inputs/hello_world_linked_lib.co | 4 ++-- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/cobalt-build/src/lib.rs b/cobalt-build/src/lib.rs index de671cb7..4bf233bc 100644 --- a/cobalt-build/src/lib.rs +++ b/cobalt-build/src/lib.rs @@ -18,6 +18,8 @@ use std::io::{self, prelude::*, ErrorKind}; use std::path::{Path, PathBuf}; use thiserror::Error; +pub const HOST_TRIPLE: &str = env!("HOST"); + #[derive(Debug, Clone, Copy, Error)] #[error("build failed because of {0} compiler error{}", if *.0 == 1 {""} else {"s"})] pub struct CompileErrors(pub usize); diff --git a/cobalt-cli/src/tests/hello_world.rs b/cobalt-cli/src/tests/hello_world.rs index a1f72ad7..8854f395 100644 --- a/cobalt-cli/src/tests/hello_world.rs +++ b/cobalt-cli/src/tests/hello_world.rs @@ -52,22 +52,23 @@ fn test_hello_world_aot() { fn test_hello_world_aot_linked() { let input_main_path_str = "src/tests/inputs/hello_world_linked_main.co"; let input_lib_path_str = "src/tests/inputs/hello_world_linked_lib.co"; - let output_lib_path_str = "src/tests/outputs/libhello_world_linked.o"; let output_main_path_str = "src/tests/outputs/hello_world_linked"; - + let output_lib_path_str = Path::new("src/tests/outputs").join(cobalt_build::libs::format_lib("hello_world_linked", cobalt_build::HOST_TRIPLE, true)); // --- let input_lib = clio::Input::new(input_lib_path_str); assert!( input_lib.is_ok(), "(clio) failed to load input: {:?}", - &input_lib.err() + &input_lib.unwrap_err() ); - let output_lib = clio::OutputPath::new(output_lib_path_str); + + + let output_lib = clio::OutputPath::new(&output_lib_path_str); assert!( output_lib.is_ok(), "(clio) failed to load output: {:?}", - &output_lib.err() + &output_lib.unwrap_err() ); let cli_lib = Cli::Aot { diff --git a/cobalt-cli/src/tests/inputs/hello_world_linked_lib.co b/cobalt-cli/src/tests/inputs/hello_world_linked_lib.co index 4207228b..1a66d93f 100644 --- a/cobalt-cli/src/tests/inputs/hello_world_linked_lib.co +++ b/cobalt-cli/src/tests/inputs/hello_world_linked_lib.co @@ -1,5 +1,5 @@ @C(extern) fn puts(str: *u8); -fn hello_world() = { +@export fn hello_world() = { puts("Hello, world!"c); -}; \ No newline at end of file +}; From 008f7004101b713cd84d9de704bf344c48f35eb4 Mon Sep 17 00:00:00 2001 From: Matthew Cornell Date: Wed, 17 Jan 2024 13:08:24 -0500 Subject: [PATCH 05/24] Fix formatting --- cobalt-cli/src/tests/hello_world.rs | 7 +++++-- cobalt-cli/src/tests/mod.rs | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/cobalt-cli/src/tests/hello_world.rs b/cobalt-cli/src/tests/hello_world.rs index 8854f395..98741dc5 100644 --- a/cobalt-cli/src/tests/hello_world.rs +++ b/cobalt-cli/src/tests/hello_world.rs @@ -53,7 +53,11 @@ fn test_hello_world_aot_linked() { let input_main_path_str = "src/tests/inputs/hello_world_linked_main.co"; let input_lib_path_str = "src/tests/inputs/hello_world_linked_lib.co"; let output_main_path_str = "src/tests/outputs/hello_world_linked"; - let output_lib_path_str = Path::new("src/tests/outputs").join(cobalt_build::libs::format_lib("hello_world_linked", cobalt_build::HOST_TRIPLE, true)); + let output_lib_path_str = Path::new("src/tests/outputs").join(cobalt_build::libs::format_lib( + "hello_world_linked", + cobalt_build::HOST_TRIPLE, + true, + )); // --- let input_lib = clio::Input::new(input_lib_path_str); @@ -63,7 +67,6 @@ fn test_hello_world_aot_linked() { &input_lib.unwrap_err() ); - let output_lib = clio::OutputPath::new(&output_lib_path_str); assert!( output_lib.is_ok(), diff --git a/cobalt-cli/src/tests/mod.rs b/cobalt-cli/src/tests/mod.rs index edf4d618..e23a00d5 100644 --- a/cobalt-cli/src/tests/mod.rs +++ b/cobalt-cli/src/tests/mod.rs @@ -1,3 +1,3 @@ use super::*; -mod hello_world; \ No newline at end of file +mod hello_world; From 1ef2185edef9fac8c2766ab3cf3d6c3123b066ec Mon Sep 17 00:00:00 2001 From: Matthew Cornell Date: Wed, 17 Jan 2024 13:16:38 -0500 Subject: [PATCH 06/24] Use more idiomatic Rust for tests --- cobalt-cli/src/tests/hello_world.rs | 100 +++++++++++----------------- 1 file changed, 39 insertions(+), 61 deletions(-) diff --git a/cobalt-cli/src/tests/hello_world.rs b/cobalt-cli/src/tests/hello_world.rs index 98741dc5..40ffc85f 100644 --- a/cobalt-cli/src/tests/hello_world.rs +++ b/cobalt-cli/src/tests/hello_world.rs @@ -5,22 +5,18 @@ fn test_hello_world_aot() { let input_path_str = "src/tests/inputs/hello_world.co"; let output_path_str = "src/tests/outputs/hello_world"; - let input = clio::Input::new(input_path_str); - assert!( - input.is_ok(), - "(clio) failed to load input: {:?}", - &input.err() - ); - let output = clio::OutputPath::new(output_path_str); - assert!( - output.is_ok(), - "(clio) failed to load output: {:?}", - &output.err() - ); + let input = match clio::Input::new(input_path_str) { + Ok(p) => p, + Err(err) => panic!("(clio) failed to load input: {err}"), + }; + let output = match clio::OutputPath::new(output_path_str) { + Ok(p) => p, + Err(err) => panic!("(clio) failed to load output: {err}"), + }; let cli = Cli::Aot { - input: input.unwrap(), - output: Some(output.unwrap()), + input: input, + output: Some(output), linked: vec![], link_dirs: vec![], headers: vec![], @@ -33,12 +29,9 @@ fn test_hello_world_aot() { timings: false, }; - let result = driver(cli); - assert!( - result.is_ok(), - "failure while compiling file: {:?}", - &result.err() - ); + if let Err(err) = driver(cli) { + panic!("failure while compiling file: {err}") + }; let command_output = Command::new(output_path_str) .output() @@ -60,23 +53,18 @@ fn test_hello_world_aot_linked() { )); // --- - let input_lib = clio::Input::new(input_lib_path_str); - assert!( - input_lib.is_ok(), - "(clio) failed to load input: {:?}", - &input_lib.unwrap_err() - ); - - let output_lib = clio::OutputPath::new(&output_lib_path_str); - assert!( - output_lib.is_ok(), - "(clio) failed to load output: {:?}", - &output_lib.unwrap_err() - ); + let input_lib = match clio::Input::new(input_lib_path_str) { + Ok(p) => p, + Err(err) => panic!("(clio) failed to load input: {err}"), + }; + let output_lib = match clio::OutputPath::new(&output_lib_path_str) { + Ok(p) => p, + Err(err) => panic!("(clio) failed to load output: {err}"), + }; let cli_lib = Cli::Aot { - input: input_lib.unwrap(), - output: Some(output_lib.unwrap()), + input: input_lib, + output: Some(output_lib), linked: vec![], link_dirs: vec![], headers: vec![], @@ -89,31 +77,24 @@ fn test_hello_world_aot_linked() { timings: false, }; - let result_lib = driver(cli_lib); - assert!( - result_lib.is_ok(), - "failure while compiling library file: {:?}", - &result_lib.err() - ); + if let Err(err) = driver(cli_lib) { + panic!("failure while compiling file: {err}") + }; // --- - let input_main = clio::Input::new(input_main_path_str); - assert!( - input_main.is_ok(), - "(clio) failed to load input: {:?}", - &input_main.err() - ); - let output_main = clio::OutputPath::new(output_main_path_str); - assert!( - output_main.is_ok(), - "(clio) failed to load output: {:?}", - &output_main.err() - ); + let input_main = match clio::Input::new(input_main_path_str) { + Ok(p) => p, + Err(err) => panic!("(clio) failed to load input: {err}"), + }; + let output_main = match clio::OutputPath::new(output_main_path_str) { + Ok(p) => p, + Err(err) => panic!("(clio) failed to load output: {err}"), + }; let cli_main = Cli::Aot { - input: input_main.unwrap(), - output: Some(output_main.unwrap()), + input: input_main, + output: Some(output_main), linked: vec!["hello_world_linked".to_string()], link_dirs: vec!["src/tests/outputs".into()], headers: vec![], @@ -126,12 +107,9 @@ fn test_hello_world_aot_linked() { timings: false, }; - let result_main = driver(cli_main); - assert!( - result_main.is_ok(), - "failure while compiling main file: {:?}", - &result_main.err() - ); + if let Err(err) = driver(cli_main) { + panic!("failure while compiling file: {err}") + }; // --- From b5ebf452aba3f12695c2a7b8bda4a5421856546f Mon Sep 17 00:00:00 2001 From: Matthew Cornell Date: Wed, 17 Jan 2024 19:54:16 -0500 Subject: [PATCH 07/24] NOMERGE: add `dbg!`s to figure out what's going on in unit tests --- cobalt-ast/src/types/agg.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/cobalt-ast/src/types/agg.rs b/cobalt-ast/src/types/agg.rs index 779af177..8eeeee1a 100644 --- a/cobalt-ast/src/types/agg.rs +++ b/cobalt-ast/src/types/agg.rs @@ -690,9 +690,9 @@ impl Type for SizedArray { target: (TypeRef, Option), ctx: &CompCtx<'src, 'ctx>, ) -> Result, CobaltError<'src>> { - if target + if dbg!(target .0 - .is_and::(|r| r.base() == self.elem()) + .is_and::(|r| r.base() == self.elem())) { Ok(Value { data_type: target.0, @@ -766,7 +766,8 @@ impl Type for SizedArray { } } fn _can_ref_iconv(&'static self, target: TypeRef, ctx: &CompCtx) -> bool { - target.is_and::(|r| r.base() == self.elem()) + eprintln!("self: {self}, target: {target}"); + dbg!(target.is_and::(|r| r.base() == self.elem())) } fn _can_refmut_iconv(&'static self, target: TypeRef, ctx: &CompCtx) -> bool { target.is_and::(|r| { From 3b963a24fc0b3bce33e673a681c86a271daef1c6 Mon Sep 17 00:00:00 2001 From: Matthew Cornell Date: Wed, 17 Jan 2024 20:08:01 -0500 Subject: [PATCH 08/24] Fix overeager caching --- .github/workflows/test.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 29e31186..ea86d68b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -31,6 +31,7 @@ jobs: path: | Cargo.lock target/ + !target/*/cobalt-* - name: Run Clippy run: cargo clippy env: From a5ddfaedb6fa02b7d3d27a399ca528d3c7438215 Mon Sep 17 00:00:00 2001 From: Matthew Cornell Date: Wed, 17 Jan 2024 20:18:21 -0500 Subject: [PATCH 09/24] Gimme more details --- cobalt-ast/src/types/agg.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cobalt-ast/src/types/agg.rs b/cobalt-ast/src/types/agg.rs index 8eeeee1a..ca8c6b90 100644 --- a/cobalt-ast/src/types/agg.rs +++ b/cobalt-ast/src/types/agg.rs @@ -766,7 +766,8 @@ impl Type for SizedArray { } } fn _can_ref_iconv(&'static self, target: TypeRef, ctx: &CompCtx) -> bool { - eprintln!("self: {self}, target: {target}"); + eprintln!("self: {self:?}, target: {target:?}"); + eprintln!("target kind: {}, ptr kind: {}", target.kind(), types::Pointer::KIND); dbg!(target.is_and::(|r| r.base() == self.elem())) } fn _can_refmut_iconv(&'static self, target: TypeRef, ctx: &CompCtx) -> bool { From 5c7beec1849282522512dc4fe296a5ce60e2ed3d Mon Sep 17 00:00:00 2001 From: Matthew Cornell Date: Wed, 17 Jan 2024 20:24:04 -0500 Subject: [PATCH 10/24] Moooooooore --- cobalt-ast/src/types/agg.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cobalt-ast/src/types/agg.rs b/cobalt-ast/src/types/agg.rs index ca8c6b90..ae32a95d 100644 --- a/cobalt-ast/src/types/agg.rs +++ b/cobalt-ast/src/types/agg.rs @@ -768,6 +768,10 @@ impl Type for SizedArray { fn _can_ref_iconv(&'static self, target: TypeRef, ctx: &CompCtx) -> bool { eprintln!("self: {self:?}, target: {target:?}"); eprintln!("target kind: {}, ptr kind: {}", target.kind(), types::Pointer::KIND); + let base = dbg!(target.downcast::()); + if let Some(base) = base { + eprintln!("base kind: {}, our int kind: {}, real int kind: {}", base.kind(), self.elem().kind(), types::Int::KIND); + } dbg!(target.is_and::(|r| r.base() == self.elem())) } fn _can_refmut_iconv(&'static self, target: TypeRef, ctx: &CompCtx) -> bool { From 78c8634dd33e0d29437a263fbbf8b0cfe6a7e4ca Mon Sep 17 00:00:00 2001 From: Matthew Cornell Date: Wed, 17 Jan 2024 20:35:32 -0500 Subject: [PATCH 11/24] Mooooooooore --- cobalt-ast/src/types/agg.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cobalt-ast/src/types/agg.rs b/cobalt-ast/src/types/agg.rs index ae32a95d..35187e29 100644 --- a/cobalt-ast/src/types/agg.rs +++ b/cobalt-ast/src/types/agg.rs @@ -768,9 +768,10 @@ impl Type for SizedArray { fn _can_ref_iconv(&'static self, target: TypeRef, ctx: &CompCtx) -> bool { eprintln!("self: {self:?}, target: {target:?}"); eprintln!("target kind: {}, ptr kind: {}", target.kind(), types::Pointer::KIND); - let base = dbg!(target.downcast::()); + let base = dbg!(target.downcast::().map(types::Pointer::base)); if let Some(base) = base { eprintln!("base kind: {}, our int kind: {}, real int kind: {}", base.kind(), self.elem().kind(), types::Int::KIND); + dbg!(base == self.elem()); } dbg!(target.is_and::(|r| r.base() == self.elem())) } From 00735cfaf86a365590b937695e464ec9aec4d6cd Mon Sep 17 00:00:00 2001 From: Matthew Cornell Date: Wed, 17 Jan 2024 20:45:55 -0500 Subject: [PATCH 12/24] Moooooooooore --- cobalt-ast/src/types/int.rs | 1 + cobalt-utils/src/intern.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/cobalt-ast/src/types/int.rs b/cobalt-ast/src/types/int.rs index bf4c96cd..9480c3b8 100644 --- a/cobalt-ast/src/types/int.rs +++ b/cobalt-ast/src/types/int.rs @@ -8,6 +8,7 @@ impl Int { #[ref_cast_custom] fn from_ref(val: &(u16, bool)) -> &Self; pub fn new(bits: u16, unsigned: bool) -> &'static Self { + eprintln!("new int: {bits}, {unsigned}"); static INTERN: Interner<(u16, bool)> = Interner::new(); Self::from_ref(INTERN.intern((bits, unsigned))) } diff --git a/cobalt-utils/src/intern.rs b/cobalt-utils/src/intern.rs index 51d25f88..e8bc857f 100644 --- a/cobalt-utils/src/intern.rs +++ b/cobalt-utils/src/intern.rs @@ -25,6 +25,7 @@ impl<'a, K: PartialEq + Eq + Hash> Interner<'a, K> { if let Some(k) = lock.get(hashed, |v| v.0 == &key).map(|x| x.1) { &self.vec[k] } else { + eprintln!("creating new element!"); std::mem::drop(lock); let mut lock = self.map.write().unwrap(); let idx = self.vec.push(key); From 430dd2ec1e23fed94bcfdb54f5c8ac77a17be465 Mon Sep 17 00:00:00 2001 From: Matthew Cornell Date: Wed, 17 Jan 2024 20:50:28 -0500 Subject: [PATCH 13/24] Mooooooooooore --- cobalt-ast/src/types/agg.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/cobalt-ast/src/types/agg.rs b/cobalt-ast/src/types/agg.rs index 35187e29..1eb1d2b1 100644 --- a/cobalt-ast/src/types/agg.rs +++ b/cobalt-ast/src/types/agg.rs @@ -772,6 +772,7 @@ impl Type for SizedArray { if let Some(base) = base { eprintln!("base kind: {}, our int kind: {}, real int kind: {}", base.kind(), self.elem().kind(), types::Int::KIND); dbg!(base == self.elem()); + eprintln!("base: {base:p}, elem: {:p}", self.elem()); } dbg!(target.is_and::(|r| r.base() == self.elem())) } From 539ab8a432332098dafe64778a329acf342de128 Mon Sep 17 00:00:00 2001 From: Matthew Cornell Date: Wed, 17 Jan 2024 20:56:00 -0500 Subject: [PATCH 14/24] Moooooooooooore --- cobalt-ast/src/types/int.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cobalt-ast/src/types/int.rs b/cobalt-ast/src/types/int.rs index 9480c3b8..9fcaaa19 100644 --- a/cobalt-ast/src/types/int.rs +++ b/cobalt-ast/src/types/int.rs @@ -10,7 +10,9 @@ impl Int { pub fn new(bits: u16, unsigned: bool) -> &'static Self { eprintln!("new int: {bits}, {unsigned}"); static INTERN: Interner<(u16, bool)> = Interner::new(); - Self::from_ref(INTERN.intern((bits, unsigned))) + let ret = Self::from_ref(INTERN.intern((bits, unsigned))); + eprintln!("finished making int"); + ret } pub fn signed(bits: u16) -> &'static Self { Self::new(bits, false) From a3afe214c8681a7992d5fe71d7c01375811e708b Mon Sep 17 00:00:00 2001 From: Matthew Cornell Date: Wed, 17 Jan 2024 21:28:49 -0500 Subject: [PATCH 15/24] Debug interner struct --- cobalt-ast/src/types/agg.rs | 1 + cobalt-ast/src/types/int.rs | 2 +- cobalt-utils/src/intern.rs | 20 +++++++++++++------- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/cobalt-ast/src/types/agg.rs b/cobalt-ast/src/types/agg.rs index 1eb1d2b1..fda2c037 100644 --- a/cobalt-ast/src/types/agg.rs +++ b/cobalt-ast/src/types/agg.rs @@ -773,6 +773,7 @@ impl Type for SizedArray { eprintln!("base kind: {}, our int kind: {}, real int kind: {}", base.kind(), self.elem().kind(), types::Int::KIND); dbg!(base == self.elem()); eprintln!("base: {base:p}, elem: {:p}", self.elem()); + eprintln!("{:?}", types::int::INTERN); } dbg!(target.is_and::(|r| r.base() == self.elem())) } diff --git a/cobalt-ast/src/types/int.rs b/cobalt-ast/src/types/int.rs index 9fcaaa19..fd0593e1 100644 --- a/cobalt-ast/src/types/int.rs +++ b/cobalt-ast/src/types/int.rs @@ -1,5 +1,6 @@ use super::*; use inkwell::IntPredicate::*; +pub(crate) static INTERN: Interner<(u16, bool)> = Interner::new(); #[derive(Debug, ConstIdentify, PartialEq, Eq, Hash, Display, RefCastCustom)] #[display(fmt = "{}{}", r#"if _0.1 {"u"} else {"i"}"#, "_0.0")] #[repr(transparent)] @@ -9,7 +10,6 @@ impl Int { fn from_ref(val: &(u16, bool)) -> &Self; pub fn new(bits: u16, unsigned: bool) -> &'static Self { eprintln!("new int: {bits}, {unsigned}"); - static INTERN: Interner<(u16, bool)> = Interner::new(); let ret = Self::from_ref(INTERN.intern((bits, unsigned))); eprintln!("finished making int"); ret diff --git a/cobalt-utils/src/intern.rs b/cobalt-utils/src/intern.rs index e8bc857f..87842081 100644 --- a/cobalt-utils/src/intern.rs +++ b/cobalt-utils/src/intern.rs @@ -1,7 +1,8 @@ -use hashbrown::raw::*; +use hashbrown::HashTable; use once_cell::sync::Lazy; use std::hash::{Hash, Hasher}; use std::sync::RwLock; +use std::fmt::{self, Debug, Formatter}; #[inline] fn hash(val: &impl Hash) -> u64 { let mut state = std::collections::hash_map::DefaultHasher::default(); @@ -10,19 +11,24 @@ fn hash(val: &impl Hash) -> u64 { } pub struct Interner<'a, T: PartialEq + Eq + Hash> { vec: Lazy>, - map: RwLock>, + map: RwLock>, +} +impl Debug for Interner<'_, T> { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("Interner").field("map", &self.map.read().unwrap()).finish_non_exhaustive() + } } impl<'a, K: PartialEq + Eq + Hash> Interner<'a, K> { pub const fn new() -> Self { Self { vec: Lazy::new(|| aovec::Aovec::new(16)), - map: RwLock::new(RawTable::new()), + map: RwLock::new(HashTable::new()), } } pub fn intern(&'a self, key: K) -> &K { let hashed = hash(&key); let lock = self.map.read().unwrap(); - if let Some(k) = lock.get(hashed, |v| v.0 == &key).map(|x| x.1) { + if let Some(k) = lock.find(hashed, |v| v.0 == &key).map(|x| x.1) { &self.vec[k] } else { eprintln!("creating new element!"); @@ -30,7 +36,7 @@ impl<'a, K: PartialEq + Eq + Hash> Interner<'a, K> { let mut lock = self.map.write().unwrap(); let idx = self.vec.push(key); let val = &self.vec[idx]; - lock.insert(hashed, (val, idx), |v| hash(&v.0)); + lock.insert_unique(hashed, (val, idx), |v| hash(&v.0)); val } } @@ -45,7 +51,7 @@ impl<'a, K: PartialEq + Eq + Hash> Interner<'a, K> { let hashed = hash(&key); let lock = self.map.read().unwrap(); if let Some(k) = lock - .get(hashed, |v| key.as_ref() == v.0.as_ref()) + .find(hashed, |v| key.as_ref() == v.0.as_ref()) .map(|x| x.1) { &self.vec[k] @@ -54,7 +60,7 @@ impl<'a, K: PartialEq + Eq + Hash> Interner<'a, K> { let mut lock = self.map.write().unwrap(); let idx = self.vec.push(key.into()); let val = &self.vec[idx]; - lock.insert(hashed, (val, idx), |v| hash(&v.0)); + lock.insert_unique(hashed, (val, idx), |v| hash(&v.0)); val } } From 561dbd6530606ca3bd4e30cb3f861162a330b1ca Mon Sep 17 00:00:00 2001 From: Matthew Cornell Date: Wed, 17 Jan 2024 21:34:59 -0500 Subject: [PATCH 16/24] Oh no the interner's broken --- cobalt-utils/src/intern.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cobalt-utils/src/intern.rs b/cobalt-utils/src/intern.rs index 87842081..7975cf54 100644 --- a/cobalt-utils/src/intern.rs +++ b/cobalt-utils/src/intern.rs @@ -29,9 +29,10 @@ impl<'a, K: PartialEq + Eq + Hash> Interner<'a, K> { let hashed = hash(&key); let lock = self.map.read().unwrap(); if let Some(k) = lock.find(hashed, |v| v.0 == &key).map(|x| x.1) { + eprintln!("reusing old element @ {k}"); &self.vec[k] } else { - eprintln!("creating new element!"); + eprintln!("creating new element @ {}", self.vec.len()); std::mem::drop(lock); let mut lock = self.map.write().unwrap(); let idx = self.vec.push(key); From 3b4039a9233eff2c08f7099d9fba5b3e15574f03 Mon Sep 17 00:00:00 2001 From: Matthew Cornell Date: Wed, 17 Jan 2024 23:07:36 -0500 Subject: [PATCH 17/24] kill me --- cobalt-ast/src/types/agg.rs | 1 - cobalt-ast/src/types/int.rs | 1 + cobalt-utils/Cargo.toml | 2 +- cobalt-utils/src/intern.rs | 13 ++++--------- 4 files changed, 6 insertions(+), 11 deletions(-) diff --git a/cobalt-ast/src/types/agg.rs b/cobalt-ast/src/types/agg.rs index fda2c037..1eb1d2b1 100644 --- a/cobalt-ast/src/types/agg.rs +++ b/cobalt-ast/src/types/agg.rs @@ -773,7 +773,6 @@ impl Type for SizedArray { eprintln!("base kind: {}, our int kind: {}, real int kind: {}", base.kind(), self.elem().kind(), types::Int::KIND); dbg!(base == self.elem()); eprintln!("base: {base:p}, elem: {:p}", self.elem()); - eprintln!("{:?}", types::int::INTERN); } dbg!(target.is_and::(|r| r.base() == self.elem())) } diff --git a/cobalt-ast/src/types/int.rs b/cobalt-ast/src/types/int.rs index fd0593e1..b497e344 100644 --- a/cobalt-ast/src/types/int.rs +++ b/cobalt-ast/src/types/int.rs @@ -10,6 +10,7 @@ impl Int { fn from_ref(val: &(u16, bool)) -> &Self; pub fn new(bits: u16, unsigned: bool) -> &'static Self { eprintln!("new int: {bits}, {unsigned}"); + eprintln!("INTERN: {INTERN:#?}"); let ret = Self::from_ref(INTERN.intern((bits, unsigned))); eprintln!("finished making int"); ret diff --git a/cobalt-utils/Cargo.toml b/cobalt-utils/Cargo.toml index 5752a9c8..3957e9be 100644 --- a/cobalt-utils/Cargo.toml +++ b/cobalt-utils/Cargo.toml @@ -9,6 +9,6 @@ description.workspace = true documentation.workspace = true [dependencies] -aovec = "1.1.0" +boxcar = "0.2.4" hashbrown = { version = "0.14.2", features = ["raw"] } once_cell = "1.18.0" diff --git a/cobalt-utils/src/intern.rs b/cobalt-utils/src/intern.rs index 7975cf54..210a2fe3 100644 --- a/cobalt-utils/src/intern.rs +++ b/cobalt-utils/src/intern.rs @@ -2,26 +2,21 @@ use hashbrown::HashTable; use once_cell::sync::Lazy; use std::hash::{Hash, Hasher}; use std::sync::RwLock; -use std::fmt::{self, Debug, Formatter}; #[inline] fn hash(val: &impl Hash) -> u64 { let mut state = std::collections::hash_map::DefaultHasher::default(); val.hash(&mut state); state.finish() } +#[derive(Debug)] pub struct Interner<'a, T: PartialEq + Eq + Hash> { - vec: Lazy>, + vec: Lazy>, map: RwLock>, } -impl Debug for Interner<'_, T> { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - f.debug_struct("Interner").field("map", &self.map.read().unwrap()).finish_non_exhaustive() - } -} impl<'a, K: PartialEq + Eq + Hash> Interner<'a, K> { pub const fn new() -> Self { Self { - vec: Lazy::new(|| aovec::Aovec::new(16)), + vec: Lazy::new(boxcar::Vec::new), map: RwLock::new(HashTable::new()), } } @@ -32,7 +27,7 @@ impl<'a, K: PartialEq + Eq + Hash> Interner<'a, K> { eprintln!("reusing old element @ {k}"); &self.vec[k] } else { - eprintln!("creating new element @ {}", self.vec.len()); + eprintln!("creating new element @ {}", self.vec.count()); std::mem::drop(lock); let mut lock = self.map.write().unwrap(); let idx = self.vec.push(key); From c6f7cff31b74cb23837cf7564485c8e6548ba7c8 Mon Sep 17 00:00:00 2001 From: Matthew Cornell Date: Wed, 17 Jan 2024 23:57:21 -0500 Subject: [PATCH 18/24] Add tests for interner --- cobalt-ast/src/types/agg.rs | 14 +++----------- cobalt-utils/src/lib.rs | 3 +++ 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/cobalt-ast/src/types/agg.rs b/cobalt-ast/src/types/agg.rs index 1eb1d2b1..779af177 100644 --- a/cobalt-ast/src/types/agg.rs +++ b/cobalt-ast/src/types/agg.rs @@ -690,9 +690,9 @@ impl Type for SizedArray { target: (TypeRef, Option), ctx: &CompCtx<'src, 'ctx>, ) -> Result, CobaltError<'src>> { - if dbg!(target + if target .0 - .is_and::(|r| r.base() == self.elem())) + .is_and::(|r| r.base() == self.elem()) { Ok(Value { data_type: target.0, @@ -766,15 +766,7 @@ impl Type for SizedArray { } } fn _can_ref_iconv(&'static self, target: TypeRef, ctx: &CompCtx) -> bool { - eprintln!("self: {self:?}, target: {target:?}"); - eprintln!("target kind: {}, ptr kind: {}", target.kind(), types::Pointer::KIND); - let base = dbg!(target.downcast::().map(types::Pointer::base)); - if let Some(base) = base { - eprintln!("base kind: {}, our int kind: {}, real int kind: {}", base.kind(), self.elem().kind(), types::Int::KIND); - dbg!(base == self.elem()); - eprintln!("base: {base:p}, elem: {:p}", self.elem()); - } - dbg!(target.is_and::(|r| r.base() == self.elem())) + target.is_and::(|r| r.base() == self.elem()) } fn _can_refmut_iconv(&'static self, target: TypeRef, ctx: &CompCtx) -> bool { target.is_and::(|r| { diff --git a/cobalt-utils/src/lib.rs b/cobalt-utils/src/lib.rs index a7327bdf..9441c000 100644 --- a/cobalt-utils/src/lib.rs +++ b/cobalt-utils/src/lib.rs @@ -5,3 +5,6 @@ pub mod misc; pub use cell::*; pub use intern::*; pub use misc::*; + +#[cfg(test)] +mod tests; From 21ecd097fc255a051b1cd5a469ecf8a667c7d18f Mon Sep 17 00:00:00 2001 From: Matthew Cornell Date: Wed, 17 Jan 2024 23:59:25 -0500 Subject: [PATCH 19/24] Forgot to add tests --- cobalt-utils/src/tests.rs | 1 + cobalt-utils/src/tests/intern.rs | 12 ++++++++++++ 2 files changed, 13 insertions(+) create mode 100644 cobalt-utils/src/tests.rs create mode 100644 cobalt-utils/src/tests/intern.rs diff --git a/cobalt-utils/src/tests.rs b/cobalt-utils/src/tests.rs new file mode 100644 index 00000000..d9193dc7 --- /dev/null +++ b/cobalt-utils/src/tests.rs @@ -0,0 +1 @@ +mod intern; diff --git a/cobalt-utils/src/tests/intern.rs b/cobalt-utils/src/tests/intern.rs new file mode 100644 index 00000000..fbe35ca4 --- /dev/null +++ b/cobalt-utils/src/tests/intern.rs @@ -0,0 +1,12 @@ +#[test] +fn test_int_bool_tuple() { + static INTERN: crate::Interner<(i32, bool)> = crate::Interner::new(); + { + let p1 = INTERN.intern((16, true)); + let p2 = INTERN.intern((64, false)); + let p3 = INTERN.intern((16, true)); + assert!(!std::ptr::eq(p1, p2)); + assert!(!std::ptr::eq(p2, p3)); + assert!(std::ptr::eq(p1, p3)); + } +} From 932a04e893fa4acb7c3b03766b1ac111cc71d879 Mon Sep 17 00:00:00 2001 From: Matthew Cornell Date: Thu, 18 Jan 2024 00:02:40 -0500 Subject: [PATCH 20/24] Remove debug printing --- cobalt-ast/src/types/int.rs | 6 +----- cobalt-utils/src/intern.rs | 2 -- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/cobalt-ast/src/types/int.rs b/cobalt-ast/src/types/int.rs index b497e344..5b64def8 100644 --- a/cobalt-ast/src/types/int.rs +++ b/cobalt-ast/src/types/int.rs @@ -9,11 +9,7 @@ impl Int { #[ref_cast_custom] fn from_ref(val: &(u16, bool)) -> &Self; pub fn new(bits: u16, unsigned: bool) -> &'static Self { - eprintln!("new int: {bits}, {unsigned}"); - eprintln!("INTERN: {INTERN:#?}"); - let ret = Self::from_ref(INTERN.intern((bits, unsigned))); - eprintln!("finished making int"); - ret + Self::from_ref(INTERN.intern((bits, unsigned))) } pub fn signed(bits: u16) -> &'static Self { Self::new(bits, false) diff --git a/cobalt-utils/src/intern.rs b/cobalt-utils/src/intern.rs index 210a2fe3..3d01cc69 100644 --- a/cobalt-utils/src/intern.rs +++ b/cobalt-utils/src/intern.rs @@ -24,10 +24,8 @@ impl<'a, K: PartialEq + Eq + Hash> Interner<'a, K> { let hashed = hash(&key); let lock = self.map.read().unwrap(); if let Some(k) = lock.find(hashed, |v| v.0 == &key).map(|x| x.1) { - eprintln!("reusing old element @ {k}"); &self.vec[k] } else { - eprintln!("creating new element @ {}", self.vec.count()); std::mem::drop(lock); let mut lock = self.map.write().unwrap(); let idx = self.vec.push(key); From 4cc48071cac07b84dd42b5df56abad337bddcde8 Mon Sep 17 00:00:00 2001 From: Matthew Cornell Date: Fri, 19 Jan 2024 17:01:20 -0500 Subject: [PATCH 21/24] Rewrite the interner --- cobalt-ast/src/types/custom.rs | 2 +- cobalt-utils/Cargo.toml | 9 ++ cobalt-utils/src/intern.rs | 159 +++++++++++++++++++++---------- cobalt-utils/src/tests/intern.rs | 8 +- 4 files changed, 122 insertions(+), 56 deletions(-) diff --git a/cobalt-ast/src/types/custom.rs b/cobalt-ast/src/types/custom.rs index a21a1401..8e820671 100644 --- a/cobalt-ast/src/types/custom.rs +++ b/cobalt-ast/src/types/custom.rs @@ -226,7 +226,7 @@ impl<'de> Deserialize<'de> for CustomHeader { where A: MapAccess<'de>, { - while let Some(k) = map.next_key()? { + while let Some(k) = map.next_key::>()? { let v = map.next_value_seed(CIProxySeed(self.0))?; assert!( CUSTOM_DATA diff --git a/cobalt-utils/Cargo.toml b/cobalt-utils/Cargo.toml index 3957e9be..3bbf1687 100644 --- a/cobalt-utils/Cargo.toml +++ b/cobalt-utils/Cargo.toml @@ -10,5 +10,14 @@ documentation.workspace = true [dependencies] boxcar = "0.2.4" +bumpalo = { version = "3.14.0", features = ["boxed", "std"] } +bumpalo-herd = "0.1.2" +dashmap = "5.5.3" +flurry = "0.4.0" hashbrown = { version = "0.14.2", features = ["raw"] } once_cell = "1.18.0" +ouroboros = "0.18.2" +parking_lot = "0.12.1" +scc = "2.0.9" +self_cell = "1.0.3" +thread_local = "1.1.7" diff --git a/cobalt-utils/src/intern.rs b/cobalt-utils/src/intern.rs index 3d01cc69..8d68a9dd 100644 --- a/cobalt-utils/src/intern.rs +++ b/cobalt-utils/src/intern.rs @@ -1,66 +1,123 @@ -use hashbrown::HashTable; +use std::hash::*; +use thread_local::ThreadLocal; +use bumpalo_herd::*; +use std::fmt; use once_cell::sync::Lazy; -use std::hash::{Hash, Hasher}; -use std::sync::RwLock; -#[inline] -fn hash(val: &impl Hash) -> u64 { - let mut state = std::collections::hash_map::DefaultHasher::default(); - val.hash(&mut state); - state.finish() +use hashbrown::HashTable; +use parking_lot::RwLock; +use std::collections::hash_map::RandomState; +use std::borrow::*; + +enum MaybeLazy { + Eager(T), + Lazy(Lazy) +} +impl std::ops::Deref for MaybeLazy { + type Target = T; + fn deref(&self) -> &T { + match self { + Self::Eager(val) => val, + Self::Lazy(val) => &val + } + } +} + +#[ouroboros::self_referencing] +struct InternerInner { + owner: Herd, + #[borrows(owner)] + #[not_covariant] + member: ThreadLocal>, + #[borrows(owner)] + #[not_covariant] + set: RwLock>, +} + +pub struct Interner(Lazy>, MaybeLazy); + +impl fmt::Debug for Interner { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.with_set(|set| { + fmt::Debug::fmt(set, f) + }) + } } -#[derive(Debug)] -pub struct Interner<'a, T: PartialEq + Eq + Hash> { - vec: Lazy>, - map: RwLock>, +impl Interner { + pub const fn with_hasher(state: S) -> Self { + Self(Lazy::new(|| InternerInner::new(Herd::new(), |_| ThreadLocal::new(), |_| RwLock::default())), MaybeLazy::Eager(state)) + } } -impl<'a, K: PartialEq + Eq + Hash> Interner<'a, K> { +impl Interner { pub const fn new() -> Self { - Self { - vec: Lazy::new(boxcar::Vec::new), - map: RwLock::new(HashTable::new()), - } + Self(Lazy::new(|| InternerInner::new(Herd::new(), |_| ThreadLocal::new(), |_| RwLock::default())), MaybeLazy::Lazy(Lazy::new(S::default))) } - pub fn intern(&'a self, key: K) -> &K { - let hashed = hash(&key); - let lock = self.map.read().unwrap(); - if let Some(k) = lock.find(hashed, |v| v.0 == &key).map(|x| x.1) { - &self.vec[k] - } else { - std::mem::drop(lock); - let mut lock = self.map.write().unwrap(); - let idx = self.vec.push(key); - let val = &self.vec[idx]; - lock.insert_unique(hashed, (val, idx), |v| hash(&v.0)); - val - } +} +impl Interner { + pub fn intern<'a, Q: Eq + Hash + Into>( + &'a self, + key: Q, + ) -> &'a T + where + T: Borrow + { + let builder = &*self.1; + let hash = builder.hash_one(&key); + self.0.with_set(|set| { + let lock = set.read(); + if let Some(r) = lock.find(hash, |m| >::borrow(m) == &key) { + // safety: lifetime can't be invalidated as long as self is not moved + unsafe { + crate::new_lifetime(&*r) + } + } else { + std::mem::drop(lock); + let ret = self.0.with_member(|m| unsafe { + crate::new_lifetime(m.get_or(|| crate::new_lifetime(self.0.borrow_owner()).get()).alloc(key.into())) + }); + set.write().insert_unique(hash, ret, |v| builder.hash_one(&v)); + &*ret + } + }) } - pub fn intern_ref + ?Sized + 'a>( + pub fn intern_ref<'a, 'b, Q: Eq + Hash + ToOwned + ?Sized>( &'a self, - key: &Q, - ) -> &K + key: &'b Q, + ) -> &'a T where - K: std::borrow::Borrow + AsRef, - for<'b> &'b Q: Into, + T: Borrow, + Q::Owned: Into { - let hashed = hash(&key); - let lock = self.map.read().unwrap(); - if let Some(k) = lock - .find(hashed, |v| key.as_ref() == v.0.as_ref()) - .map(|x| x.1) - { - &self.vec[k] - } else { - std::mem::drop(lock); - let mut lock = self.map.write().unwrap(); - let idx = self.vec.push(key.into()); - let val = &self.vec[idx]; - lock.insert_unique(hashed, (val, idx), |v| hash(&v.0)); - val - } + let builder = &*self.1; + let hash = builder.hash_one(&key); + self.0.with_set(|set| { + let lock = set.read(); + if let Some(r) = lock.find(hash, |m| >::borrow(m) == key) { + // safety: lifetime can't be invalidated as long as self is not moved + unsafe { + crate::new_lifetime(&*r) + } + } else { + std::mem::drop(lock); + let ret = self.0.with_member(|m| unsafe { + crate::new_lifetime(m.get_or(|| crate::new_lifetime(self.0.borrow_owner()).get()).alloc(key.to_owned().into())) + }); + set.write().insert_unique(hash, ret, |v| builder.hash_one(&v)); + &*ret + } + }) + } +} +impl Drop for Interner { + fn drop(&mut self) { + self.0.with_set_mut(|set| { + set.get_mut().iter_mut().for_each(|p| unsafe { + std::ptr::drop_in_place::(*p as *const T as *mut T) + }) + }) } } -impl Default for Interner<'_, T> { +impl Default for Interner { fn default() -> Self { Self::new() } diff --git a/cobalt-utils/src/tests/intern.rs b/cobalt-utils/src/tests/intern.rs index fbe35ca4..13efc11e 100644 --- a/cobalt-utils/src/tests/intern.rs +++ b/cobalt-utils/src/tests/intern.rs @@ -1,10 +1,10 @@ #[test] fn test_int_bool_tuple() { - static INTERN: crate::Interner<(i32, bool)> = crate::Interner::new(); + let intern = crate::Interner::<(i32, bool)>::new(); { - let p1 = INTERN.intern((16, true)); - let p2 = INTERN.intern((64, false)); - let p3 = INTERN.intern((16, true)); + let p1 = intern.intern((16, true)); + let p2 = intern.intern((64, false)); + let p3 = intern.intern((16, true)); assert!(!std::ptr::eq(p1, p2)); assert!(!std::ptr::eq(p2, p3)); assert!(std::ptr::eq(p1, p3)); From 94b34894d529927127a11dab407b48059d239567 Mon Sep 17 00:00:00 2001 From: Matthew Cornell Date: Fri, 19 Jan 2024 18:24:54 -0500 Subject: [PATCH 22/24] Fix formatting and clippy lints --- cobalt-cli/src/tests/hello_world.rs | 2 +- cobalt-utils/src/intern.rs | 87 ++++++++++++++++------------- 2 files changed, 49 insertions(+), 40 deletions(-) diff --git a/cobalt-cli/src/tests/hello_world.rs b/cobalt-cli/src/tests/hello_world.rs index 40ffc85f..fb4e5ff0 100644 --- a/cobalt-cli/src/tests/hello_world.rs +++ b/cobalt-cli/src/tests/hello_world.rs @@ -15,7 +15,7 @@ fn test_hello_world_aot() { }; let cli = Cli::Aot { - input: input, + input, output: Some(output), linked: vec![], link_dirs: vec![], diff --git a/cobalt-utils/src/intern.rs b/cobalt-utils/src/intern.rs index 8d68a9dd..e92281f2 100644 --- a/cobalt-utils/src/intern.rs +++ b/cobalt-utils/src/intern.rs @@ -1,23 +1,23 @@ -use std::hash::*; -use thread_local::ThreadLocal; use bumpalo_herd::*; -use std::fmt; -use once_cell::sync::Lazy; use hashbrown::HashTable; +use once_cell::sync::Lazy; use parking_lot::RwLock; -use std::collections::hash_map::RandomState; use std::borrow::*; +use std::collections::hash_map::RandomState; +use std::fmt; +use std::hash::*; +use thread_local::ThreadLocal; enum MaybeLazy { Eager(T), - Lazy(Lazy) + Lazy(Lazy), } impl std::ops::Deref for MaybeLazy { type Target = T; fn deref(&self) -> &T { match self { Self::Eager(val) => val, - Self::Lazy(val) => &val + Self::Lazy(val) => val, } } } @@ -33,32 +33,40 @@ struct InternerInner { set: RwLock>, } -pub struct Interner(Lazy>, MaybeLazy); +pub struct Interner( + Lazy>, + MaybeLazy, +); impl fmt::Debug for Interner { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0.with_set(|set| { - fmt::Debug::fmt(set, f) - }) + self.0.with_set(|set| fmt::Debug::fmt(set, f)) } } impl Interner { pub const fn with_hasher(state: S) -> Self { - Self(Lazy::new(|| InternerInner::new(Herd::new(), |_| ThreadLocal::new(), |_| RwLock::default())), MaybeLazy::Eager(state)) + Self( + Lazy::new(|| { + InternerInner::new(Herd::new(), |_| ThreadLocal::new(), |_| RwLock::default()) + }), + MaybeLazy::Eager(state), + ) } } impl Interner { pub const fn new() -> Self { - Self(Lazy::new(|| InternerInner::new(Herd::new(), |_| ThreadLocal::new(), |_| RwLock::default())), MaybeLazy::Lazy(Lazy::new(S::default))) + Self( + Lazy::new(|| { + InternerInner::new(Herd::new(), |_| ThreadLocal::new(), |_| RwLock::default()) + }), + MaybeLazy::Lazy(Lazy::new(S::default)), + ) } } impl Interner { - pub fn intern<'a, Q: Eq + Hash + Into>( - &'a self, - key: Q, - ) -> &'a T + pub fn intern>(&self, key: Q) -> & T where - T: Borrow + T: Borrow, { let builder = &*self.1; let hash = builder.hash_one(&key); @@ -66,43 +74,44 @@ impl Interner { let lock = set.read(); if let Some(r) = lock.find(hash, |m| >::borrow(m) == &key) { // safety: lifetime can't be invalidated as long as self is not moved - unsafe { - crate::new_lifetime(&*r) - } + unsafe { crate::new_lifetime(r) } } else { std::mem::drop(lock); let ret = self.0.with_member(|m| unsafe { - crate::new_lifetime(m.get_or(|| crate::new_lifetime(self.0.borrow_owner()).get()).alloc(key.into())) + crate::new_lifetime( + m.get_or(|| crate::new_lifetime(self.0.borrow_owner()).get()) + .alloc(key.into()), + ) }); - set.write().insert_unique(hash, ret, |v| builder.hash_one(&v)); - &*ret + set.write() + .insert_unique(hash, ret, |v| builder.hash_one(v)); + ret } }) } - pub fn intern_ref<'a, 'b, Q: Eq + Hash + ToOwned + ?Sized>( - &'a self, - key: &'b Q, - ) -> &'a T + pub fn intern_ref<'a, Q: Eq + Hash + ToOwned + ?Sized>(&'a self, key: &Q) -> &'a T where T: Borrow, - Q::Owned: Into + Q::Owned: Into, { let builder = &*self.1; - let hash = builder.hash_one(&key); + let hash = builder.hash_one(key); self.0.with_set(|set| { let lock = set.read(); if let Some(r) = lock.find(hash, |m| >::borrow(m) == key) { // safety: lifetime can't be invalidated as long as self is not moved - unsafe { - crate::new_lifetime(&*r) - } + unsafe { crate::new_lifetime(r) } } else { std::mem::drop(lock); let ret = self.0.with_member(|m| unsafe { - crate::new_lifetime(m.get_or(|| crate::new_lifetime(self.0.borrow_owner()).get()).alloc(key.to_owned().into())) + crate::new_lifetime( + m.get_or(|| crate::new_lifetime(self.0.borrow_owner()).get()) + .alloc(key.to_owned().into()), + ) }); - set.write().insert_unique(hash, ret, |v| builder.hash_one(&v)); - &*ret + set.write() + .insert_unique(hash, ret, |v| builder.hash_one(v)); + ret } }) } @@ -110,9 +119,9 @@ impl Interner { impl Drop for Interner { fn drop(&mut self) { self.0.with_set_mut(|set| { - set.get_mut().iter_mut().for_each(|p| unsafe { - std::ptr::drop_in_place::(*p as *const T as *mut T) - }) + set.get_mut() + .iter_mut() + .for_each(|p| unsafe { std::ptr::drop_in_place::(*p as *const T as *mut T) }) }) } } From 5697dec78463b82243c1ea7bffc8f5e540a45c34 Mon Sep 17 00:00:00 2001 From: Matthew Cornell Date: Fri, 19 Jan 2024 18:26:04 -0500 Subject: [PATCH 23/24] Clippy messed up the formatting --- cobalt-utils/src/intern.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cobalt-utils/src/intern.rs b/cobalt-utils/src/intern.rs index e92281f2..736ce9d1 100644 --- a/cobalt-utils/src/intern.rs +++ b/cobalt-utils/src/intern.rs @@ -64,7 +64,7 @@ impl Interner { } } impl Interner { - pub fn intern>(&self, key: Q) -> & T + pub fn intern>(&self, key: Q) -> &T where T: Borrow, { From 039f1b48f83c33411f30815818a384457a4c1fc7 Mon Sep 17 00:00:00 2001 From: Matthew Cornell Date: Thu, 25 Jan 2024 15:31:02 -0500 Subject: [PATCH 24/24] Remove unused libraries --- cobalt-utils/Cargo.toml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/cobalt-utils/Cargo.toml b/cobalt-utils/Cargo.toml index 3bbf1687..a16b480e 100644 --- a/cobalt-utils/Cargo.toml +++ b/cobalt-utils/Cargo.toml @@ -12,12 +12,8 @@ documentation.workspace = true boxcar = "0.2.4" bumpalo = { version = "3.14.0", features = ["boxed", "std"] } bumpalo-herd = "0.1.2" -dashmap = "5.5.3" -flurry = "0.4.0" -hashbrown = { version = "0.14.2", features = ["raw"] } +hashbrown = "0.14.2" once_cell = "1.18.0" ouroboros = "0.18.2" parking_lot = "0.12.1" -scc = "2.0.9" -self_cell = "1.0.3" thread_local = "1.1.7"