mirror of
https://github.com/roc-lang/roc.git
synced 2025-08-04 20:28:02 +00:00
give the new exposed symbols to the surgical linker
This commit is contained in:
parent
fee5e978f7
commit
454f3c603e
11 changed files with 163 additions and 75 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -3744,6 +3744,7 @@ dependencies = [
|
|||
"roc_collections",
|
||||
"roc_error_macros",
|
||||
"roc_load",
|
||||
"roc_module",
|
||||
"roc_mono",
|
||||
"roc_packaging",
|
||||
"roc_reporting",
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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" }
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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<_>>(),
|
||||
|
|
|
@ -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![],
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue