mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-26 13:29:12 +00:00
generate callers based on layout traversal
This commit is contained in:
parent
8a443ba8bb
commit
68524ef07e
4 changed files with 562 additions and 44 deletions
|
@ -188,20 +188,28 @@ where
|
||||||
let func_name = FuncName(&bytes);
|
let func_name = FuncName(&bytes);
|
||||||
|
|
||||||
if let HostExposedLayouts::HostExposed { aliases, .. } = &proc.host_exposed_layouts {
|
if let HostExposedLayouts::HostExposed { aliases, .. } = &proc.host_exposed_layouts {
|
||||||
for (_, (symbol, top_level, layout)) in aliases {
|
for (_, hels) in aliases {
|
||||||
match layout {
|
match hels.raw_function_layout {
|
||||||
RawFunctionLayout::Function(_, _, _) => {
|
RawFunctionLayout::Function(_, _, _) => {
|
||||||
let it = top_level.arguments.iter().copied();
|
let it = hels.proc_layout.arguments.iter().copied();
|
||||||
let bytes =
|
let bytes = func_name_bytes_help(
|
||||||
func_name_bytes_help(*symbol, it, Niche::NONE, top_level.result);
|
hels.symbol,
|
||||||
|
it,
|
||||||
|
Niche::NONE,
|
||||||
|
hels.proc_layout.result,
|
||||||
|
);
|
||||||
|
|
||||||
host_exposed_functions.push((bytes, top_level.arguments));
|
host_exposed_functions.push((bytes, hels.proc_layout.arguments));
|
||||||
}
|
}
|
||||||
RawFunctionLayout::ZeroArgumentThunk(_) => {
|
RawFunctionLayout::ZeroArgumentThunk(_) => {
|
||||||
let bytes =
|
let bytes = func_name_bytes_help(
|
||||||
func_name_bytes_help(*symbol, [], Niche::NONE, top_level.result);
|
hels.symbol,
|
||||||
|
[],
|
||||||
|
Niche::NONE,
|
||||||
|
hels.proc_layout.result,
|
||||||
|
);
|
||||||
|
|
||||||
host_exposed_functions.push((bytes, top_level.arguments));
|
host_exposed_functions.push((bytes, hels.proc_layout.arguments));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,8 +39,8 @@ use roc_debug_flags::dbg_do;
|
||||||
use roc_debug_flags::ROC_PRINT_LLVM_FN_VERIFICATION;
|
use roc_debug_flags::ROC_PRINT_LLVM_FN_VERIFICATION;
|
||||||
use roc_module::symbol::{Interns, ModuleId, Symbol};
|
use roc_module::symbol::{Interns, ModuleId, Symbol};
|
||||||
use roc_mono::ir::{
|
use roc_mono::ir::{
|
||||||
BranchInfo, CallType, CrashTag, EntryPoint, JoinPointId, ListLiteralElement, ModifyRc,
|
BranchInfo, CallType, CrashTag, EntryPoint, HostExposedLambdaSet, JoinPointId,
|
||||||
OptLevel, ProcLayout, SingleEntryPoint,
|
ListLiteralElement, ModifyRc, OptLevel, ProcLayout, SingleEntryPoint,
|
||||||
};
|
};
|
||||||
use roc_mono::layout::{
|
use roc_mono::layout::{
|
||||||
Builtin, InLayout, LambdaName, LambdaSet, Layout, LayoutIds, LayoutInterner, Niche,
|
Builtin, InLayout, LambdaName, LambdaSet, Layout, LayoutIds, LayoutInterner, Niche,
|
||||||
|
@ -3977,7 +3977,8 @@ fn expose_function_to_host_help_c_abi_v2<'a, 'ctx, 'env>(
|
||||||
// In C, this is modelled as a function returning void
|
// In C, this is modelled as a function returning void
|
||||||
(
|
(
|
||||||
¶ms[..],
|
¶ms[..],
|
||||||
¶m_types[..param_types.len().saturating_sub(1)],
|
// ¶m_types[..param_types.len().saturating_sub(1)],
|
||||||
|
¶m_types[..],
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
_ => (¶ms[..], ¶m_types[..]),
|
_ => (¶ms[..], ¶m_types[..]),
|
||||||
|
@ -4938,26 +4939,24 @@ fn expose_alias_to_host<'a, 'ctx, 'env>(
|
||||||
mod_solutions: &'a ModSolutions,
|
mod_solutions: &'a ModSolutions,
|
||||||
proc_name: LambdaName,
|
proc_name: LambdaName,
|
||||||
alias_symbol: Symbol,
|
alias_symbol: Symbol,
|
||||||
exposed_function_symbol: Symbol,
|
hels: &HostExposedLambdaSet<'a>,
|
||||||
top_level: ProcLayout<'a>,
|
|
||||||
layout: RawFunctionLayout<'a>,
|
|
||||||
) {
|
) {
|
||||||
let ident_string = proc_name.name().as_str(&env.interns);
|
let ident_string = proc_name.name().as_str(&env.interns);
|
||||||
let fn_name: String = format!("{}_1", ident_string);
|
let fn_name: String = format!("{}_1", ident_string);
|
||||||
|
|
||||||
match layout {
|
match hels.raw_function_layout {
|
||||||
RawFunctionLayout::Function(arguments, closure, result) => {
|
RawFunctionLayout::Function(arguments, closure, result) => {
|
||||||
// define closure size and return value size, e.g.
|
// define closure size and return value size, e.g.
|
||||||
//
|
//
|
||||||
// * roc__mainForHost_1_Update_size() -> i64
|
// * roc__mainForHost_1_Update_size() -> i64
|
||||||
// * roc__mainForHost_1_Update_result_size() -> i64
|
// * roc__mainForHost_1_Update_result_size() -> i64
|
||||||
|
|
||||||
let it = top_level.arguments.iter().copied();
|
let it = hels.proc_layout.arguments.iter().copied();
|
||||||
let bytes = roc_alias_analysis::func_name_bytes_help(
|
let bytes = roc_alias_analysis::func_name_bytes_help(
|
||||||
exposed_function_symbol,
|
hels.symbol,
|
||||||
it,
|
it,
|
||||||
Niche::NONE,
|
Niche::NONE,
|
||||||
top_level.result,
|
hels.proc_layout.result,
|
||||||
);
|
);
|
||||||
let func_name = FuncName(&bytes);
|
let func_name = FuncName(&bytes);
|
||||||
let func_solutions = mod_solutions.func_solutions(func_name).unwrap();
|
let func_solutions = mod_solutions.func_solutions(func_name).unwrap();
|
||||||
|
@ -4973,17 +4972,17 @@ fn expose_alias_to_host<'a, 'ctx, 'env>(
|
||||||
function_value_by_func_spec(
|
function_value_by_func_spec(
|
||||||
env,
|
env,
|
||||||
*func_spec,
|
*func_spec,
|
||||||
exposed_function_symbol,
|
hels.symbol,
|
||||||
top_level.arguments,
|
hels.proc_layout.arguments,
|
||||||
Niche::NONE,
|
Niche::NONE,
|
||||||
top_level.result,
|
hels.proc_layout.result,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
// morphic did not generate a specialization for this function,
|
// morphic did not generate a specialization for this function,
|
||||||
// therefore it must actually be unused.
|
// therefore it must actually be unused.
|
||||||
// An example is our closure callers
|
// An example is our closure callers
|
||||||
panic!("morphic did not specialize {:?}", exposed_function_symbol);
|
panic!("morphic did not specialize {:?}", hels.symbol);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -5237,16 +5236,14 @@ pub fn build_proc<'a, 'ctx, 'env>(
|
||||||
/* no host, or exposing types is not supported */
|
/* no host, or exposing types is not supported */
|
||||||
}
|
}
|
||||||
Binary | BinaryDev => {
|
Binary | BinaryDev => {
|
||||||
for (alias_name, (generated_function, top_level, layout)) in aliases.iter() {
|
for (alias_name, hels) in aliases.iter() {
|
||||||
expose_alias_to_host(
|
expose_alias_to_host(
|
||||||
env,
|
env,
|
||||||
layout_interner,
|
layout_interner,
|
||||||
mod_solutions,
|
mod_solutions,
|
||||||
proc.name,
|
proc.name,
|
||||||
*alias_name,
|
*alias_name,
|
||||||
*generated_function,
|
hels,
|
||||||
*top_level,
|
|
||||||
*layout,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,7 @@ use roc_std::RocDec;
|
||||||
use roc_target::TargetInfo;
|
use roc_target::TargetInfo;
|
||||||
use roc_types::subs::{
|
use roc_types::subs::{
|
||||||
instantiate_rigids, storage_copy_var_to, Content, ExhaustiveMark, FlatType, RedundantMark,
|
instantiate_rigids, storage_copy_var_to, Content, ExhaustiveMark, FlatType, RedundantMark,
|
||||||
StorageSubs, Subs, Variable, VariableSubsSlice,
|
StorageSubs, Subs, Variable, VariableSubsSlice,
|
||||||
};
|
};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use ven_pretty::{BoxAllocator, DocAllocator, DocBuilder};
|
use ven_pretty::{BoxAllocator, DocAllocator, DocBuilder};
|
||||||
|
@ -305,12 +305,21 @@ pub struct Proc<'a> {
|
||||||
pub host_exposed_layouts: HostExposedLayouts<'a>,
|
pub host_exposed_layouts: HostExposedLayouts<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub struct HostExposedLambdaSet<'a> {
|
||||||
|
pub id: LambdaSetId,
|
||||||
|
/// Symbol of the exposed function
|
||||||
|
pub symbol: Symbol,
|
||||||
|
pub proc_layout: ProcLayout<'a>,
|
||||||
|
pub raw_function_layout: RawFunctionLayout<'a>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
pub enum HostExposedLayouts<'a> {
|
pub enum HostExposedLayouts<'a> {
|
||||||
NotHostExposed,
|
NotHostExposed,
|
||||||
HostExposed {
|
HostExposed {
|
||||||
rigids: BumpMap<Lowercase, InLayout<'a>>,
|
rigids: BumpMap<Lowercase, InLayout<'a>>,
|
||||||
aliases: BumpMap<Symbol, (Symbol, ProcLayout<'a>, RawFunctionLayout<'a>)>,
|
aliases: BumpMap<Symbol, HostExposedLambdaSet<'a>>,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3068,25 +3077,126 @@ fn specialize_external_help<'a>(
|
||||||
);
|
);
|
||||||
|
|
||||||
match specialization_result {
|
match specialization_result {
|
||||||
Ok((proc, layout)) => {
|
Ok((mut proc, layout)) => {
|
||||||
let top_level = ProcLayout::from_raw_named(env.arena, name, layout);
|
let top_level = ProcLayout::from_raw_named(env.arena, name, layout);
|
||||||
|
|
||||||
if procs.is_module_thunk(name.name()) {
|
if procs.is_module_thunk(name.name()) {
|
||||||
debug_assert!(top_level.arguments.is_empty());
|
debug_assert!(top_level.arguments.is_empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
// layouts that are (transitively) used in the type of `mainForHost`.
|
if !host_exposed_aliases.is_empty() {
|
||||||
let host_exposed_layouts = top_level
|
// layouts that are (transitively) used in the type of `mainForHost`.
|
||||||
.arguments
|
let mut host_exposed_layouts: Vec<_> = top_level
|
||||||
.iter()
|
.arguments
|
||||||
.copied()
|
.iter()
|
||||||
.chain([top_level.result]);
|
.copied()
|
||||||
|
.chain([top_level.result])
|
||||||
|
.collect_in(env.arena);
|
||||||
|
|
||||||
// TODO: In the future, we will generate glue procs here
|
// it is very likely we see the same types across functions, or in multiple arguments
|
||||||
for in_layout in host_exposed_layouts {
|
host_exposed_layouts.sort();
|
||||||
let layout = layout_cache.interner.get(in_layout);
|
host_exposed_layouts.dedup();
|
||||||
|
|
||||||
let _ = layout;
|
// TODO: In the future, we will generate glue procs here
|
||||||
|
for in_layout in host_exposed_layouts {
|
||||||
|
let layout = layout_cache.interner.get(in_layout);
|
||||||
|
|
||||||
|
let all_glue_procs = generate_glue_procs(
|
||||||
|
env.home,
|
||||||
|
env.ident_ids,
|
||||||
|
env.arena,
|
||||||
|
&mut layout_cache.interner,
|
||||||
|
env.arena.alloc(layout),
|
||||||
|
);
|
||||||
|
|
||||||
|
// for now, getters are not processed here
|
||||||
|
let GlueProcs {
|
||||||
|
getters: _,
|
||||||
|
extern_names,
|
||||||
|
} = all_glue_procs;
|
||||||
|
|
||||||
|
let mut aliases = BumpMap::default();
|
||||||
|
|
||||||
|
for (id, mut raw_function_layout) in extern_names {
|
||||||
|
let symbol = env.unique_symbol();
|
||||||
|
let lambda_name = LambdaName::no_niche(symbol);
|
||||||
|
|
||||||
|
if false {
|
||||||
|
raw_function_layout = match raw_function_layout {
|
||||||
|
RawFunctionLayout::Function(a, mut lambda_set, _) => {
|
||||||
|
lambda_set.ret = in_layout;
|
||||||
|
RawFunctionLayout::Function(a, lambda_set, in_layout)
|
||||||
|
}
|
||||||
|
RawFunctionLayout::ZeroArgumentThunk(x) => {
|
||||||
|
RawFunctionLayout::ZeroArgumentThunk(x)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let (key, (_name, top_level, proc)) = generate_host_exposed_function(
|
||||||
|
env,
|
||||||
|
procs,
|
||||||
|
layout_cache,
|
||||||
|
lambda_name,
|
||||||
|
raw_function_layout,
|
||||||
|
);
|
||||||
|
|
||||||
|
procs
|
||||||
|
.specialized
|
||||||
|
.insert_specialized(symbol, top_level, proc);
|
||||||
|
|
||||||
|
let hels = HostExposedLambdaSet {
|
||||||
|
id,
|
||||||
|
symbol,
|
||||||
|
proc_layout: top_level,
|
||||||
|
raw_function_layout,
|
||||||
|
};
|
||||||
|
|
||||||
|
aliases.insert(key, hels);
|
||||||
|
}
|
||||||
|
|
||||||
|
// pre-glue: generate named callers for as-exposed aliases
|
||||||
|
for (alias_name, variable) in host_exposed_aliases {
|
||||||
|
let raw_function_layout = layout_cache
|
||||||
|
.raw_from_var(env.arena, *variable, env.subs)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let symbol = env.unique_symbol();
|
||||||
|
let lambda_name = LambdaName::no_niche(symbol);
|
||||||
|
|
||||||
|
let (_lambda_name, (proc_name, proc_layout, proc)) =
|
||||||
|
generate_host_exposed_function(
|
||||||
|
env,
|
||||||
|
procs,
|
||||||
|
layout_cache,
|
||||||
|
lambda_name,
|
||||||
|
raw_function_layout,
|
||||||
|
);
|
||||||
|
|
||||||
|
procs
|
||||||
|
.specialized
|
||||||
|
.insert_specialized(proc_name, proc_layout, proc);
|
||||||
|
|
||||||
|
let hels = HostExposedLambdaSet {
|
||||||
|
id: LambdaSetId::default(),
|
||||||
|
symbol: proc_name,
|
||||||
|
proc_layout,
|
||||||
|
raw_function_layout,
|
||||||
|
};
|
||||||
|
|
||||||
|
aliases.insert(*alias_name, hels);
|
||||||
|
}
|
||||||
|
|
||||||
|
match &mut proc.host_exposed_layouts {
|
||||||
|
HostExposedLayouts::HostExposed { aliases: old, .. } => old.extend(aliases),
|
||||||
|
hep @ HostExposedLayouts::NotHostExposed => {
|
||||||
|
*hep = HostExposedLayouts::HostExposed {
|
||||||
|
aliases,
|
||||||
|
rigids: Default::default(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
procs
|
procs
|
||||||
|
@ -3221,7 +3331,7 @@ fn generate_host_exposed_function<'a>(
|
||||||
layout: RawFunctionLayout<'a>,
|
layout: RawFunctionLayout<'a>,
|
||||||
) -> (Symbol, (Symbol, ProcLayout<'a>, Proc<'a>)) {
|
) -> (Symbol, (Symbol, ProcLayout<'a>, Proc<'a>)) {
|
||||||
let symbol = lambda_name.name();
|
let symbol = lambda_name.name();
|
||||||
let function_name = env.unique_symbol();
|
let function_name = symbol;
|
||||||
|
|
||||||
match layout {
|
match layout {
|
||||||
RawFunctionLayout::Function(_, lambda_set, _) => {
|
RawFunctionLayout::Function(_, lambda_set, _) => {
|
||||||
|
@ -3379,7 +3489,7 @@ fn specialize_proc_help<'a>(
|
||||||
let body_var = partial_proc.body_var;
|
let body_var = partial_proc.body_var;
|
||||||
|
|
||||||
// determine the layout of aliases/rigids exposed to the host
|
// determine the layout of aliases/rigids exposed to the host
|
||||||
let host_exposed_layouts = if host_exposed_variables.is_empty() {
|
let host_exposed_layouts = if true || host_exposed_variables.is_empty() {
|
||||||
HostExposedLayouts::NotHostExposed
|
HostExposedLayouts::NotHostExposed
|
||||||
} else {
|
} else {
|
||||||
let mut aliases = BumpMap::new_in(env.arena);
|
let mut aliases = BumpMap::new_in(env.arena);
|
||||||
|
@ -3400,12 +3510,20 @@ fn specialize_proc_help<'a>(
|
||||||
procs
|
procs
|
||||||
.specialized
|
.specialized
|
||||||
.insert_specialized(proc_name, proc_layout, proc);
|
.insert_specialized(proc_name, proc_layout, proc);
|
||||||
aliases.insert(*alias_name, (proc_name, proc_layout, raw_function_layout));
|
|
||||||
|
let hels = HostExposedLambdaSet {
|
||||||
|
id: LambdaSetId::default(),
|
||||||
|
symbol: proc_name,
|
||||||
|
proc_layout,
|
||||||
|
raw_function_layout,
|
||||||
|
};
|
||||||
|
|
||||||
|
aliases.insert(*alias_name, hels);
|
||||||
}
|
}
|
||||||
|
|
||||||
HostExposedLayouts::HostExposed {
|
HostExposedLayouts::HostExposed {
|
||||||
rigids: BumpMap::new_in(env.arena),
|
rigids: BumpMap::new_in(env.arena),
|
||||||
aliases,
|
aliases: Default::default(),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -11129,3 +11247,344 @@ where
|
||||||
remainder: env.arena.alloc(switch),
|
remainder: env.arena.alloc(switch),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct GlueLayouts<'a> {
|
||||||
|
pub getters: std::vec::Vec<(Symbol, ProcLayout<'a>)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
type GlueProcId = u16;
|
||||||
|
|
||||||
|
pub struct GlueProc<'a> {
|
||||||
|
pub name: Symbol,
|
||||||
|
pub proc_layout: ProcLayout<'a>,
|
||||||
|
pub proc: Proc<'a>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct GlueProcs<'a> {
|
||||||
|
pub getters: Vec<'a, (Layout<'a>, Vec<'a, GlueProc<'a>>)>,
|
||||||
|
pub extern_names: Vec<'a, (LambdaSetId, RawFunctionLayout<'a>)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
|
||||||
|
pub struct LambdaSetId(pub u32);
|
||||||
|
|
||||||
|
impl LambdaSetId {
|
||||||
|
#[must_use]
|
||||||
|
pub fn next(self) -> Self {
|
||||||
|
debug_assert!(self.0 < u32::MAX);
|
||||||
|
Self(self.0 + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn invalid() -> Self {
|
||||||
|
Self(u32::MAX)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn generate_glue_procs<'a, 'i, I>(
|
||||||
|
home: ModuleId,
|
||||||
|
ident_ids: &mut IdentIds,
|
||||||
|
arena: &'a Bump,
|
||||||
|
layout_interner: &'i mut I,
|
||||||
|
layout: &'a Layout<'a>,
|
||||||
|
) -> GlueProcs<'a>
|
||||||
|
where
|
||||||
|
I: LayoutInterner<'a>,
|
||||||
|
{
|
||||||
|
let mut answer = GlueProcs {
|
||||||
|
getters: Vec::new_in(arena),
|
||||||
|
extern_names: Vec::new_in(arena),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut lambda_set_id = LambdaSetId(0);
|
||||||
|
|
||||||
|
let mut stack: Vec<'a, Layout<'a>> = Vec::from_iter_in([*layout], arena);
|
||||||
|
let mut next_unique_id = 0;
|
||||||
|
|
||||||
|
macro_rules! handle_struct_field_layouts {
|
||||||
|
($field_layouts: expr) => {{
|
||||||
|
if $field_layouts.iter().any(|l| {
|
||||||
|
layout_interner
|
||||||
|
.get(*l)
|
||||||
|
.has_varying_stack_size(layout_interner, arena)
|
||||||
|
}) {
|
||||||
|
let procs = generate_glue_procs_for_struct_fields(
|
||||||
|
layout_interner,
|
||||||
|
home,
|
||||||
|
&mut next_unique_id,
|
||||||
|
ident_ids,
|
||||||
|
arena,
|
||||||
|
layout,
|
||||||
|
$field_layouts,
|
||||||
|
);
|
||||||
|
|
||||||
|
answer.getters.push((*layout, procs));
|
||||||
|
}
|
||||||
|
|
||||||
|
for in_layout in $field_layouts.iter().rev() {
|
||||||
|
stack.push(layout_interner.get(*in_layout));
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! handle_tag_field_layouts {
|
||||||
|
($tag_id:expr, $union_layout:expr, $field_layouts: expr) => {{
|
||||||
|
if $field_layouts.iter().any(|l| {
|
||||||
|
layout_interner
|
||||||
|
.get(*l)
|
||||||
|
.has_varying_stack_size(layout_interner, arena)
|
||||||
|
}) {
|
||||||
|
let procs = generate_glue_procs_for_tag_fields(
|
||||||
|
layout_interner,
|
||||||
|
home,
|
||||||
|
&mut next_unique_id,
|
||||||
|
ident_ids,
|
||||||
|
arena,
|
||||||
|
$tag_id,
|
||||||
|
layout,
|
||||||
|
$union_layout,
|
||||||
|
$field_layouts,
|
||||||
|
);
|
||||||
|
|
||||||
|
answer.getters.push((*layout, procs));
|
||||||
|
}
|
||||||
|
|
||||||
|
for in_layout in $field_layouts.iter().rev() {
|
||||||
|
stack.push(layout_interner.get(*in_layout));
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
while let Some(layout) = stack.pop() {
|
||||||
|
match layout {
|
||||||
|
Layout::Builtin(builtin) => match builtin {
|
||||||
|
Builtin::Int(_)
|
||||||
|
| Builtin::Float(_)
|
||||||
|
| Builtin::Bool
|
||||||
|
| Builtin::Decimal
|
||||||
|
| Builtin::Str => { /* do nothing */ }
|
||||||
|
Builtin::List(element) => stack.push(layout_interner.get(element)),
|
||||||
|
},
|
||||||
|
Layout::Struct { field_layouts, .. } => {
|
||||||
|
handle_struct_field_layouts!(field_layouts);
|
||||||
|
}
|
||||||
|
Layout::Boxed(boxed) => {
|
||||||
|
stack.push(layout_interner.get(boxed));
|
||||||
|
}
|
||||||
|
Layout::Union(union_layout) => match union_layout {
|
||||||
|
UnionLayout::NonRecursive(tags) => {
|
||||||
|
for in_layout in tags.iter().flat_map(|e| e.iter()) {
|
||||||
|
stack.push(layout_interner.get(*in_layout));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
UnionLayout::Recursive(tags) => {
|
||||||
|
for in_layout in tags.iter().flat_map(|e| e.iter()) {
|
||||||
|
stack.push(layout_interner.get(*in_layout));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
UnionLayout::NonNullableUnwrapped(field_layouts) => {
|
||||||
|
handle_tag_field_layouts!(0, union_layout, field_layouts);
|
||||||
|
}
|
||||||
|
UnionLayout::NullableWrapped {
|
||||||
|
other_tags,
|
||||||
|
nullable_id,
|
||||||
|
} => {
|
||||||
|
let tag_ids =
|
||||||
|
(0..nullable_id).chain(nullable_id + 1..other_tags.len() as u16 + 1);
|
||||||
|
for (i, field_layouts) in tag_ids.zip(other_tags) {
|
||||||
|
handle_tag_field_layouts!(i, union_layout, *field_layouts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
UnionLayout::NullableUnwrapped { other_fields, .. } => {
|
||||||
|
for in_layout in other_fields.iter().rev() {
|
||||||
|
stack.push(layout_interner.get(*in_layout));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Layout::LambdaSet(lambda_set) => {
|
||||||
|
let raw_function_layout =
|
||||||
|
RawFunctionLayout::Function(*lambda_set.args, lambda_set, lambda_set.ret);
|
||||||
|
|
||||||
|
let key = (lambda_set_id, raw_function_layout);
|
||||||
|
answer.extern_names.push(key);
|
||||||
|
|
||||||
|
// this id is used, increment for the next one
|
||||||
|
lambda_set_id = lambda_set_id.next();
|
||||||
|
|
||||||
|
stack.push(layout_interner.get(lambda_set.runtime_representation()));
|
||||||
|
}
|
||||||
|
Layout::RecursivePointer(_) => {
|
||||||
|
/* do nothing, we've already generated for this type through the Union(_) */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
answer
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_glue_procs_for_struct_fields<'a, 'i, I>(
|
||||||
|
layout_interner: &'i mut I,
|
||||||
|
home: ModuleId,
|
||||||
|
next_unique_id: &mut GlueProcId,
|
||||||
|
ident_ids: &mut IdentIds,
|
||||||
|
arena: &'a Bump,
|
||||||
|
unboxed_struct_layout: &'a Layout<'a>,
|
||||||
|
field_layouts: &'a [InLayout<'a>],
|
||||||
|
) -> Vec<'a, GlueProc<'a>>
|
||||||
|
where
|
||||||
|
I: LayoutInterner<'a>,
|
||||||
|
{
|
||||||
|
let interned_unboxed_struct_layout = layout_interner.insert(*unboxed_struct_layout);
|
||||||
|
let boxed_struct_layout = Layout::Boxed(interned_unboxed_struct_layout);
|
||||||
|
let boxed_struct_layout = layout_interner.insert(boxed_struct_layout);
|
||||||
|
let mut answer = bumpalo::collections::Vec::with_capacity_in(field_layouts.len(), arena);
|
||||||
|
|
||||||
|
for (index, field) in field_layouts.iter().enumerate() {
|
||||||
|
let proc_layout = ProcLayout {
|
||||||
|
arguments: arena.alloc([boxed_struct_layout]),
|
||||||
|
result: *field,
|
||||||
|
niche: Niche::NONE,
|
||||||
|
};
|
||||||
|
|
||||||
|
let symbol = unique_glue_symbol(arena, next_unique_id, home, ident_ids);
|
||||||
|
|
||||||
|
let argument = Symbol::new(home, ident_ids.gen_unique());
|
||||||
|
let unboxed = Symbol::new(home, ident_ids.gen_unique());
|
||||||
|
let result = Symbol::new(home, ident_ids.gen_unique());
|
||||||
|
|
||||||
|
home.register_debug_idents(ident_ids);
|
||||||
|
|
||||||
|
let ret_stmt = arena.alloc(Stmt::Ret(result));
|
||||||
|
|
||||||
|
let field_get_expr = Expr::StructAtIndex {
|
||||||
|
index: index as u64,
|
||||||
|
field_layouts,
|
||||||
|
structure: unboxed,
|
||||||
|
};
|
||||||
|
|
||||||
|
let field_get_stmt = Stmt::Let(result, field_get_expr, *field, ret_stmt);
|
||||||
|
|
||||||
|
let unbox_expr = Expr::ExprUnbox { symbol: argument };
|
||||||
|
|
||||||
|
let unbox_stmt = Stmt::Let(
|
||||||
|
unboxed,
|
||||||
|
unbox_expr,
|
||||||
|
interned_unboxed_struct_layout,
|
||||||
|
arena.alloc(field_get_stmt),
|
||||||
|
);
|
||||||
|
|
||||||
|
let proc = Proc {
|
||||||
|
name: LambdaName::no_niche(symbol),
|
||||||
|
args: arena.alloc([(boxed_struct_layout, argument)]),
|
||||||
|
body: unbox_stmt,
|
||||||
|
closure_data_layout: None,
|
||||||
|
ret_layout: *field,
|
||||||
|
is_self_recursive: SelfRecursive::NotSelfRecursive,
|
||||||
|
must_own_arguments: false,
|
||||||
|
host_exposed_layouts: HostExposedLayouts::NotHostExposed,
|
||||||
|
};
|
||||||
|
|
||||||
|
answer.push(GlueProc {
|
||||||
|
name: symbol,
|
||||||
|
proc_layout,
|
||||||
|
proc,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
answer
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unique_glue_symbol(
|
||||||
|
arena: &Bump,
|
||||||
|
next_unique_id: &mut GlueProcId,
|
||||||
|
home: ModuleId,
|
||||||
|
ident_ids: &mut IdentIds,
|
||||||
|
) -> Symbol {
|
||||||
|
let unique_id = *next_unique_id;
|
||||||
|
|
||||||
|
*next_unique_id = unique_id + 1;
|
||||||
|
|
||||||
|
// Turn unique_id into a Symbol without doing a heap allocation.
|
||||||
|
use std::fmt::Write;
|
||||||
|
let mut string = bumpalo::collections::String::with_capacity_in(32, arena);
|
||||||
|
let _result = write!(
|
||||||
|
&mut string,
|
||||||
|
"roc__getter_{}_{}",
|
||||||
|
"", // then name of the platform `main.roc` is the empty string
|
||||||
|
unique_id
|
||||||
|
);
|
||||||
|
debug_assert_eq!(_result, Ok(())); // This should never fail, but doesn't hurt to debug-check!
|
||||||
|
|
||||||
|
let ident_id = ident_ids.get_or_insert(string.into_bump_str());
|
||||||
|
|
||||||
|
Symbol::new(home, ident_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
fn generate_glue_procs_for_tag_fields<'a, 'i, I>(
|
||||||
|
layout_interner: &'i mut I,
|
||||||
|
home: ModuleId,
|
||||||
|
next_unique_id: &mut GlueProcId,
|
||||||
|
ident_ids: &mut IdentIds,
|
||||||
|
arena: &'a Bump,
|
||||||
|
tag_id: TagIdIntType,
|
||||||
|
unboxed_struct_layout: &'a Layout<'a>,
|
||||||
|
union_layout: UnionLayout<'a>,
|
||||||
|
field_layouts: &'a [InLayout<'a>],
|
||||||
|
) -> Vec<'a, GlueProc<'a>>
|
||||||
|
where
|
||||||
|
I: LayoutInterner<'a>,
|
||||||
|
{
|
||||||
|
let interned = layout_interner.insert(*unboxed_struct_layout);
|
||||||
|
let boxed_struct_layout = Layout::Boxed(interned);
|
||||||
|
let boxed_struct_layout = layout_interner.insert(boxed_struct_layout);
|
||||||
|
let mut answer = bumpalo::collections::Vec::with_capacity_in(field_layouts.len(), arena);
|
||||||
|
|
||||||
|
for (index, field) in field_layouts.iter().enumerate() {
|
||||||
|
let proc_layout = ProcLayout {
|
||||||
|
arguments: arena.alloc([boxed_struct_layout]),
|
||||||
|
result: *field,
|
||||||
|
niche: Niche::NONE,
|
||||||
|
};
|
||||||
|
let symbol = unique_glue_symbol(arena, next_unique_id, home, ident_ids);
|
||||||
|
|
||||||
|
let argument = Symbol::new(home, ident_ids.gen_unique());
|
||||||
|
let unboxed = Symbol::new(home, ident_ids.gen_unique());
|
||||||
|
let result = Symbol::new(home, ident_ids.gen_unique());
|
||||||
|
|
||||||
|
home.register_debug_idents(ident_ids);
|
||||||
|
|
||||||
|
let ret_stmt = arena.alloc(Stmt::Ret(result));
|
||||||
|
|
||||||
|
let field_get_expr = Expr::UnionAtIndex {
|
||||||
|
structure: unboxed,
|
||||||
|
tag_id,
|
||||||
|
union_layout,
|
||||||
|
index: index as u64,
|
||||||
|
};
|
||||||
|
|
||||||
|
let field_get_stmt = Stmt::Let(result, field_get_expr, *field, ret_stmt);
|
||||||
|
|
||||||
|
let unbox_expr = Expr::ExprUnbox { symbol: argument };
|
||||||
|
|
||||||
|
let unbox_stmt = Stmt::Let(unboxed, unbox_expr, interned, arena.alloc(field_get_stmt));
|
||||||
|
|
||||||
|
let proc = Proc {
|
||||||
|
name: LambdaName::no_niche(symbol),
|
||||||
|
args: arena.alloc([(boxed_struct_layout, argument)]),
|
||||||
|
body: unbox_stmt,
|
||||||
|
closure_data_layout: None,
|
||||||
|
ret_layout: *field,
|
||||||
|
is_self_recursive: SelfRecursive::NotSelfRecursive,
|
||||||
|
must_own_arguments: false,
|
||||||
|
host_exposed_layouts: HostExposedLayouts::NotHostExposed,
|
||||||
|
};
|
||||||
|
|
||||||
|
answer.push(GlueProc {
|
||||||
|
name: symbol,
|
||||||
|
proc_layout,
|
||||||
|
proc,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
answer
|
||||||
|
}
|
||||||
|
|
|
@ -2717,6 +2717,60 @@ impl<'a> Layout<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn has_varying_stack_size<I>(self, interner: &I, arena: &bumpalo::Bump) -> bool
|
||||||
|
where
|
||||||
|
I: LayoutInterner<'a>,
|
||||||
|
{
|
||||||
|
let mut stack: Vec<Layout> = bumpalo::collections::Vec::new_in(arena);
|
||||||
|
|
||||||
|
stack.push(self);
|
||||||
|
|
||||||
|
while let Some(layout) = stack.pop() {
|
||||||
|
match layout {
|
||||||
|
Layout::Builtin(builtin) => match builtin {
|
||||||
|
Builtin::Int(_)
|
||||||
|
| Builtin::Float(_)
|
||||||
|
| Builtin::Bool
|
||||||
|
| Builtin::Decimal
|
||||||
|
| Builtin::Str
|
||||||
|
// If there's any layer of indirection (behind a pointer), then it doesn't vary!
|
||||||
|
| Builtin::List(_) => { /* do nothing */ }
|
||||||
|
},
|
||||||
|
// If there's any layer of indirection (behind a pointer), then it doesn't vary!
|
||||||
|
Layout::Struct { field_layouts, .. } => {
|
||||||
|
stack.extend(field_layouts.iter().map(|interned| interner.get(*interned)))
|
||||||
|
}
|
||||||
|
Layout::Union(tag_union) => match tag_union {
|
||||||
|
UnionLayout::NonRecursive(tags) | UnionLayout::Recursive(tags) => {
|
||||||
|
for tag in tags {
|
||||||
|
stack.extend(tag.iter().map(|interned| interner.get(*interned)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
UnionLayout::NonNullableUnwrapped(fields) => {
|
||||||
|
stack.extend(fields.iter().map(|interned| interner.get(*interned)));
|
||||||
|
}
|
||||||
|
UnionLayout::NullableWrapped { other_tags, .. } => {
|
||||||
|
for tag in other_tags {
|
||||||
|
stack.extend(tag.iter().map(|interned| interner.get(*interned)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
UnionLayout::NullableUnwrapped { other_fields, .. } => {
|
||||||
|
stack.extend(other_fields.iter().map(|interned| interner.get(*interned)));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Layout::LambdaSet(_) => return true,
|
||||||
|
Layout::Boxed(_) => {
|
||||||
|
// If there's any layer of indirection (behind a pointer), then it doesn't vary!
|
||||||
|
}
|
||||||
|
Layout::RecursivePointer(_) => {
|
||||||
|
/* do nothing, we've already generated for this type through the Union(_) */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
/// Used to build a `Layout::Struct` where the field name order is irrelevant.
|
/// Used to build a `Layout::Struct` where the field name order is irrelevant.
|
||||||
pub fn struct_no_name_order(field_layouts: &'a [InLayout]) -> Self {
|
pub fn struct_no_name_order(field_layouts: &'a [InLayout]) -> Self {
|
||||||
if field_layouts.is_empty() {
|
if field_layouts.is_empty() {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue