give the new exposed symbols to the surgical linker

This commit is contained in:
Folkert 2023-02-25 19:35:46 +01:00
parent fee5e978f7
commit 454f3c603e
No known key found for this signature in database
GPG key ID: 1F17F6FFD112B97C
11 changed files with 163 additions and 75 deletions

1
Cargo.lock generated
View file

@ -3744,6 +3744,7 @@ dependencies = [
"roc_collections",
"roc_error_macros",
"roc_load",
"roc_module",
"roc_mono",
"roc_packaging",
"roc_reporting",

View file

@ -7,6 +7,7 @@ use roc_build::{
program::{self, CodeGenBackend, CodeGenOptions},
};
use roc_builtins::bitcode;
use roc_linker::ExposedSymbols;
use roc_load::{
EntryPoint, ExecutionMode, ExpectMetadata, LoadConfig, LoadMonomorphizedError, LoadedModule,
LoadingProblem, Threading,
@ -200,25 +201,8 @@ fn build_loaded_file<'a>(
// Also, we should no longer need to do this once we have platforms on
// a package repository, as we can then get prebuilt platforms from there.
let exposed_values = loaded
.exposed_to_host
.values
.keys()
.map(|x| x.as_str(&loaded.interns).to_string())
.collect();
let exposed_closure_types = loaded
.exposed_to_host
.closure_types
.iter()
.map(|x| {
format!(
"{}_{}",
x.module_string(&loaded.interns),
x.as_str(&loaded.interns)
)
})
.collect();
let dll_stub_symbols =
ExposedSymbols::from_exposed_to_host(&loaded.interns, &loaded.exposed_to_host);
let join_handle = spawn_rebuild_thread(
code_gen_options.opt_level,
@ -227,8 +211,7 @@ fn build_loaded_file<'a>(
preprocessed_host_path.clone(),
output_exe_path.clone(),
target,
exposed_values,
exposed_closure_types,
dll_stub_symbols,
);
Some(join_handle)
@ -447,8 +430,7 @@ fn spawn_rebuild_thread(
preprocessed_host_path: PathBuf,
output_exe_path: PathBuf,
target: &Triple,
exported_symbols: Vec<String>,
exported_closure_types: Vec<String>,
dll_stub_symbols: Vec<String>,
) -> std::thread::JoinHandle<u128> {
let thread_local_target = target.clone();
std::thread::spawn(move || {
@ -471,13 +453,12 @@ fn spawn_rebuild_thread(
preprocess_host_wasm32(host_dest.as_path(), &preprocessed_host_path);
}
LinkingStrategy::Surgical => {
roc_linker::build_and_preprocess_host(
roc_linker::build_and_preprocess_host_lowlevel(
opt_level,
&thread_local_target,
platform_main_roc.as_path(),
preprocessed_host_path.as_path(),
exported_symbols,
exported_closure_types,
&dll_stub_symbols,
);
// Copy preprocessed host to executable location.

View file

@ -180,7 +180,12 @@ fn gen_from_mono_module_llvm<'a>(
OptLevel::Normal | OptLevel::Size | OptLevel::Optimize => LlvmBackendMode::Binary,
},
exposed_to_host: loaded.exposed_to_host.values.keys().copied().collect(),
exposed_to_host: loaded
.exposed_to_host
.top_level_values
.keys()
.copied()
.collect(),
};
// does not add any externs for this mode (we have a host) but cleans up some functions around
@ -485,7 +490,7 @@ fn gen_from_mono_module_dev_wasm32<'a>(
let exposed_to_host = loaded
.exposed_to_host
.values
.top_level_values
.keys()
.copied()
.collect::<MutSet<_>>();
@ -556,7 +561,7 @@ fn gen_from_mono_module_dev_assembly<'a>(
let env = roc_gen_dev::Env {
arena,
module_id,
exposed_to_host: exposed_to_host.values.keys().copied().collect(),
exposed_to_host: exposed_to_host.top_level_values.keys().copied().collect(),
lazy_literals,
generate_allocators,
};

View file

@ -18,8 +18,8 @@ const SKIP_SUBS_CACHE: bool = {
pub use roc_load_internal::docs;
pub use roc_load_internal::file::{
EntryPoint, ExecutionMode, ExpectMetadata, Expectations, LoadConfig, LoadResult, LoadStart,
LoadedModule, LoadingProblem, MonomorphizedModule, Phase, Threading,
EntryPoint, ExecutionMode, ExpectMetadata, Expectations, ExposedToHost, LoadConfig, LoadResult,
LoadStart, LoadedModule, LoadingProblem, MonomorphizedModule, Phase, Threading,
};
#[allow(clippy::too_many_arguments)]

View file

@ -31,8 +31,8 @@ use roc_module::symbol::{
PackageQualified, Symbol,
};
use roc_mono::ir::{
CapturedSymbols, ExternalSpecializations, GlueLayouts, PartialProc, Proc, ProcLayout, Procs,
ProcsBase, UpdateModeIds,
CapturedSymbols, ExternalSpecializations, GlueLayouts, LambdaSetId, PartialProc, Proc,
ProcLayout, Procs, ProcsBase, UpdateModeIds,
};
use roc_mono::layout::LayoutInterner;
use roc_mono::layout::{
@ -797,9 +797,12 @@ pub struct Expectations {
#[derive(Clone, Debug, Default)]
pub struct ExposedToHost {
/// usually `mainForHost`
pub values: MutMap<Symbol, Variable>,
pub top_level_values: MutMap<Symbol, Variable>,
/// exposed closure types, typically `Fx`
pub closure_types: Vec<Symbol>,
/// lambda_sets
pub lambda_sets: Vec<(Symbol, LambdaSetId)>,
pub getters: Vec<Symbol>,
}
impl<'a> MonomorphizedModule<'a> {
@ -2777,7 +2780,7 @@ fn update<'a>(
!matches!(state.exec_mode, ExecutionMode::Test);
if add_to_host_exposed {
state.exposed_to_host.values.extend(
state.exposed_to_host.top_level_values.extend(
solved_module
.exposed_vars_by_symbol
.iter()
@ -3104,7 +3107,7 @@ fn update<'a>(
debug_print_ir!(state, &layout_interner, ROC_PRINT_IR_AFTER_RESET_REUSE);
let host_exposed_procs = bumpalo::collections::Vec::from_iter_in(
state.exposed_to_host.values.keys().copied(),
state.exposed_to_host.top_level_values.keys().copied(),
arena,
);
@ -3275,7 +3278,7 @@ fn finish_specialization<'a>(
state: State<'a>,
subs: Subs,
mut layout_interner: STLayoutInterner<'a>,
exposed_to_host: ExposedToHost,
mut exposed_to_host: ExposedToHost,
module_expectations: VecMap<ModuleId, Expectations>,
) -> Result<MonomorphizedModule<'a>, LoadingProblem<'a>> {
if false {
@ -3328,7 +3331,7 @@ fn finish_specialization<'a>(
let exposed_symbols_and_layouts = match state.platform_data {
None => {
let src = &state.exposed_to_host.values;
let src = &state.exposed_to_host.top_level_values;
let mut buf = bumpalo::collections::Vec::with_capacity_in(src.len(), arena);
for &symbol in src.keys() {
@ -3396,13 +3399,14 @@ fn finish_specialization<'a>(
let mut glue_getters = Vec::new();
if let EntryPoint::Executable {
exposed_to_host, ..
exposed_to_host: exposed_top_levels,
..
} = &entry_point
{
// Expose glue for the platform, not for the app module!
let module_id = platform_data.as_ref().unwrap().module_id;
for (_name, proc_layout) in exposed_to_host.iter() {
for (_name, proc_layout) in exposed_top_levels.iter() {
let ret = &proc_layout.result;
for in_layout in proc_layout.arguments.iter().chain([ret]) {
let layout = layout_interner.get(*in_layout);
@ -3415,6 +3419,18 @@ fn finish_specialization<'a>(
arena.alloc(layout),
);
let getter_names = all_glue_procs
.getters
.iter()
.flat_map(|(_, glue_procs)| glue_procs.iter().map(|glue_proc| glue_proc.name));
exposed_to_host.getters.extend(getter_names);
let lambda_set_names = all_glue_procs
.extern_names
.iter()
.map(|(lambda_set_id, _)| (*_name, *lambda_set_id));
exposed_to_host.lambda_sets.extend(lambda_set_names);
glue_getters.extend(all_glue_procs.getters.iter().flat_map(|(_, glue_procs)| {
glue_procs
.iter()
@ -5787,7 +5803,7 @@ fn build_pending_specializations<'a>(
let symbol = declarations.symbols[index].value;
let expr_var = declarations.variables[index];
let is_host_exposed = exposed_to_host.values.contains_key(&symbol);
let is_host_exposed = exposed_to_host.top_level_values.contains_key(&symbol);
// TODO remove clones (with drain)
let annotation = declarations.annotations[index].clone();

View file

@ -148,7 +148,7 @@ fn compiles_to_ir(test_name: &str, src: &str, mode: &str, no_check: bool) {
assert!(type_problems.is_empty());
let main_fn_symbol = exposed_to_host.values.keys().copied().next();
let main_fn_symbol = exposed_to_host.top_level_values.keys().copied().next();
if !no_check {
check_procedures(arena, &interns, &mut layout_interner, &procedures);

View file

@ -16,6 +16,7 @@ roc_mono = { path = "../compiler/mono" }
roc_build = { path = "../compiler/build" }
roc_collections = { path = "../compiler/collections" }
roc_error_macros = { path = "../error_macros" }
roc_module = { path = "../compiler/module" }
roc_load = { path = "../compiler/load" }
roc_packaging = { path = "../packaging" }
roc_reporting = { path = "../reporting" }

View file

@ -7,7 +7,8 @@ use memmap2::{Mmap, MmapMut};
use object::Object;
use roc_build::link::{get_target_triple_str, rebuild_host, LinkType};
use roc_error_macros::internal_error;
use roc_load::{EntryPoint, ExecutionMode, LoadConfig, Threading};
use roc_load::{EntryPoint, ExecutionMode, ExposedToHost, LoadConfig, Threading};
use roc_module::symbol::Interns;
use roc_mono::ir::OptLevel;
use roc_packaging::cache::RocCacheDir;
use roc_reporting::report::{RenderTarget, DEFAULT_PALETTE};
@ -59,8 +60,25 @@ pub fn build_and_preprocess_host(
target: &Triple,
platform_main_roc: &Path,
preprocessed_host_path: &Path,
exposed_to_host: Vec<String>,
exported_closure_types: Vec<String>,
exposed_symbols: ExposedSymbols,
) {
let stub_dll_symbols = exposed_symbols.stub_dll_symbols();
build_and_preprocess_host_lowlevel(
opt_level,
target,
platform_main_roc,
preprocessed_host_path,
&stub_dll_symbols,
)
}
pub fn build_and_preprocess_host_lowlevel(
opt_level: OptLevel,
target: &Triple,
platform_main_roc: &Path,
preprocessed_host_path: &Path,
stub_dll_symbols: &[String],
) {
let stub_lib = if let target_lexicon::OperatingSystem::Windows = target.operating_system {
platform_main_roc.with_file_name("libapp.dll")
@ -74,7 +92,6 @@ pub fn build_and_preprocess_host(
platform_main_roc.with_file_name("dynhost")
};
let stub_dll_symbols = make_stub_dll_symbols(exposed_to_host, exported_closure_types);
generate_dynamic_lib(target, &stub_dll_symbols, &stub_lib);
rebuild_host(opt_level, target, platform_main_roc, Some(&stub_lib));
@ -137,7 +154,7 @@ pub fn generate_stub_lib(
let exposed_to_host = loaded
.exposed_to_host
.values
.top_level_values
.keys()
.map(|x| x.as_str(&loaded.interns).to_string())
.collect();
@ -155,6 +172,11 @@ pub fn generate_stub_lib(
})
.collect();
let exposed_symbols = ExposedSymbols {
top_level_values: exposed_to_host,
exported_closure_types,
};
if let EntryPoint::Executable { platform_path, .. } = &loaded.entry_point {
let stub_lib = if let target_lexicon::OperatingSystem::Windows = triple.operating_system {
platform_path.with_file_name("libapp.obj")
@ -162,7 +184,7 @@ pub fn generate_stub_lib(
platform_path.with_file_name("libapp.so")
};
let stub_dll_symbols = make_stub_dll_symbols(exposed_to_host, exported_closure_types);
let stub_dll_symbols = exposed_symbols.stub_dll_symbols();
generate_dynamic_lib(triple, &stub_dll_symbols, &stub_lib);
} else {
unreachable!();
@ -170,33 +192,88 @@ pub fn generate_stub_lib(
Ok(0)
}
fn make_stub_dll_symbols(
exposed_to_host: Vec<String>,
exported_closure_types: Vec<String>,
) -> Vec<String> {
let mut custom_names = Vec::new();
pub struct ExposedSymbols {
// usually just `mainForhost`
pub top_level_values: Vec<String>,
for sym in exposed_to_host {
custom_names.extend([
format!("roc__{}_1_exposed", sym),
format!("roc__{}_1_exposed_generic", sym),
format!("roc__{}_size", sym),
]);
// old type exposing mechanism
pub exported_closure_types: Vec<String>,
}
impl ExposedSymbols {
pub fn from_exposed_to_host(interns: &Interns, exposed_to_host: &ExposedToHost) -> Vec<String> {
let mut custom_names = Vec::new();
for x in exposed_to_host.top_level_values.keys() {
let sym = x.as_str(interns);
for closure_type in &exported_closure_types {
custom_names.extend([
format!("roc__{}_1_{}_caller", sym, closure_type),
format!("roc__{}_1_{}_size", sym, closure_type),
format!("roc__{}_1_{}_result_size", sym, closure_type),
format!("roc__{}_1_exposed", sym),
format!("roc__{}_1_exposed_generic", sym),
format!("roc__{}_size", sym),
]);
let exported_closure_types = exposed_to_host
.closure_types
.iter()
.map(|x| format!("{}_{}", x.module_string(interns), x.as_str(interns)));
for closure_type in exported_closure_types {
custom_names.extend([
format!("roc__{}_1_{}_caller", sym, closure_type),
format!("roc__{}_1_{}_size", sym, closure_type),
format!("roc__{}_1_{}_result_size", sym, closure_type),
]);
}
}
for x in &exposed_to_host.getters {
let sym = x.as_str(interns);
custom_names.extend([
format!("{sym}"),
format!("{sym}_generic"),
format!("roc__{sym}_size"),
]);
}
for (top_level_value, lambda_set_id) in &exposed_to_host.lambda_sets {
let sym = top_level_value.as_str(interns);
let id = lambda_set_id.0;
custom_names.extend([format!("roc__{sym}_{id}_caller")]);
}
// on windows (PE) binary search is used on the symbols,
// so they must be in alphabetical order
custom_names.sort_unstable();
custom_names
}
// on windows (PE) binary search is used on the symbols,
// so they must be in alphabetical order
custom_names.sort_unstable();
fn stub_dll_symbols(&self) -> Vec<String> {
let mut custom_names = Vec::new();
custom_names
for sym in &self.top_level_values {
custom_names.extend([
format!("roc__{}_1_exposed", sym),
format!("roc__{}_1_exposed_generic", sym),
format!("roc__{}_size", sym),
]);
for closure_type in &self.exported_closure_types {
custom_names.extend([
format!("roc__{}_1_{}_caller", sym, closure_type),
format!("roc__{}_1_{}_size", sym, closure_type),
format!("roc__{}_1_{}_result_size", sym, closure_type),
]);
}
}
// on windows (PE) binary search is used on the symbols,
// so they must be in alphabetical order
custom_names.sort_unstable();
custom_names
}
}
fn generate_dynamic_lib(target: &Triple, stub_dll_symbols: &[String], stub_lib_path: &Path) {

View file

@ -42,8 +42,13 @@ pub fn gen_and_eval_llvm<'a, I: Iterator<Item = &'a str>>(
}
};
debug_assert_eq!(loaded.exposed_to_host.values.len(), 1);
let (main_fn_symbol, main_fn_var) = loaded.exposed_to_host.values.iter().next().unwrap();
debug_assert_eq!(loaded.exposed_to_host.top_level_values.len(), 1);
let (main_fn_symbol, main_fn_var) = loaded
.exposed_to_host
.top_level_values
.iter()
.next()
.unwrap();
let main_fn_symbol = *main_fn_symbol;
let main_fn_var = *main_fn_var;

View file

@ -217,8 +217,8 @@ pub async fn entrypoint_from_js(src: String) -> Result<String, String> {
..
} = mono;
debug_assert_eq!(exposed_to_host.values.len(), 1);
let (main_fn_symbol, main_fn_var) = exposed_to_host.values.iter().next().unwrap();
debug_assert_eq!(exposed_to_host.top_level_values.len(), 1);
let (main_fn_symbol, main_fn_var) = exposed_to_host.top_level_values.iter().next().unwrap();
let main_fn_symbol = *main_fn_symbol;
let main_fn_var = *main_fn_var;
@ -242,7 +242,7 @@ pub async fn entrypoint_from_js(src: String) -> Result<String, String> {
module_id,
stack_bytes: roc_gen_wasm::Env::DEFAULT_STACK_BYTES,
exposed_to_host: exposed_to_host
.values
.top_level_values
.keys()
.copied()
.collect::<MutSet<_>>(),

View file

@ -8,7 +8,7 @@ static BUILD_ONCE: std::sync::Once = std::sync::Once::new();
#[cfg(all(target_os = "linux"))]
fn build_host() {
use roc_build::link::preprocessed_host_filename;
use roc_linker::build_and_preprocess_host;
use roc_linker::{build_and_preprocess_host, ExposedSymbols};
let platform_main_roc = std::env::current_dir()
.unwrap()
@ -26,8 +26,10 @@ fn build_host() {
&target,
&platform_main_roc,
&preprocessed_host_path,
vec![String::from("mainForHost")],
vec![],
ExposedSymbols {
top_level_values: vec![String::from("mainForHost")],
exported_closure_types: vec![],
},
);
}