mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-03 08:34:33 +00:00
Start reuse similar layouts
This commit is contained in:
parent
b32cd5687b
commit
d82f3ee09d
6 changed files with 324 additions and 79 deletions
|
@ -3115,6 +3115,7 @@ fn update<'a>(
|
||||||
&layout_interner,
|
&layout_interner,
|
||||||
module_id,
|
module_id,
|
||||||
ident_ids,
|
ident_ids,
|
||||||
|
state.target_info,
|
||||||
&mut update_mode_ids,
|
&mut update_mode_ids,
|
||||||
&mut state.procedures,
|
&mut state.procedures,
|
||||||
);
|
);
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
// Implementation based of Reference Counting with Frame Limited Reuse
|
// Implementation based of Reference Counting with Frame Limited Reuse
|
||||||
// https://www.microsoft.com/en-us/research/uploads/prod/2021/11/flreuse-tr.pdf
|
// https://www.microsoft.com/en-us/research/uploads/prod/2021/11/flreuse-tr.pdf
|
||||||
|
|
||||||
|
use std::hash::Hash;
|
||||||
|
|
||||||
use crate::borrow::Ownership;
|
use crate::borrow::Ownership;
|
||||||
use crate::ir::{
|
use crate::ir::{
|
||||||
BranchInfo, Expr, JoinPointId, ModifyRc, Param, Proc, ProcLayout, Stmt, UpdateModeId,
|
BranchInfo, Expr, JoinPointId, ModifyRc, Param, Proc, ProcLayout, Stmt, UpdateModeId,
|
||||||
|
@ -18,6 +20,7 @@ use bumpalo::collections::vec::Vec;
|
||||||
use bumpalo::collections::CollectIn;
|
use bumpalo::collections::CollectIn;
|
||||||
use roc_collections::{MutMap, MutSet};
|
use roc_collections::{MutMap, MutSet};
|
||||||
use roc_module::symbol::{IdentIds, ModuleId, Symbol};
|
use roc_module::symbol::{IdentIds, ModuleId, Symbol};
|
||||||
|
use roc_target::TargetInfo;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Insert reset and reuse operations into the IR.
|
Insert reset and reuse operations into the IR.
|
||||||
|
@ -28,6 +31,7 @@ pub fn insert_reset_reuse_operations<'a, 'i>(
|
||||||
layout_interner: &'i STLayoutInterner<'a>,
|
layout_interner: &'i STLayoutInterner<'a>,
|
||||||
home: ModuleId,
|
home: ModuleId,
|
||||||
ident_ids: &'i mut IdentIds,
|
ident_ids: &'i mut IdentIds,
|
||||||
|
target_info: TargetInfo,
|
||||||
update_mode_ids: &'i mut UpdateModeIds,
|
update_mode_ids: &'i mut UpdateModeIds,
|
||||||
procs: &mut MutMap<(Symbol, ProcLayout<'a>), Proc<'a>>,
|
procs: &mut MutMap<(Symbol, ProcLayout<'a>), Proc<'a>>,
|
||||||
) {
|
) {
|
||||||
|
@ -42,6 +46,7 @@ pub fn insert_reset_reuse_operations<'a, 'i>(
|
||||||
layout_interner,
|
layout_interner,
|
||||||
home,
|
home,
|
||||||
ident_ids,
|
ident_ids,
|
||||||
|
target_info,
|
||||||
update_mode_ids,
|
update_mode_ids,
|
||||||
global_layouts.clone(),
|
global_layouts.clone(),
|
||||||
proc.clone(),
|
proc.clone(),
|
||||||
|
@ -55,6 +60,7 @@ fn insert_reset_reuse_operations_proc<'a, 'i>(
|
||||||
layout_interner: &'i STLayoutInterner<'a>,
|
layout_interner: &'i STLayoutInterner<'a>,
|
||||||
home: ModuleId,
|
home: ModuleId,
|
||||||
ident_ids: &'i mut IdentIds,
|
ident_ids: &'i mut IdentIds,
|
||||||
|
target_info: TargetInfo,
|
||||||
update_mode_ids: &'i mut UpdateModeIds,
|
update_mode_ids: &'i mut UpdateModeIds,
|
||||||
mut symbol_layout: SymbolLayout<'a>,
|
mut symbol_layout: SymbolLayout<'a>,
|
||||||
mut proc: Proc<'a>,
|
mut proc: Proc<'a>,
|
||||||
|
@ -76,6 +82,7 @@ fn insert_reset_reuse_operations_proc<'a, 'i>(
|
||||||
layout_interner,
|
layout_interner,
|
||||||
home,
|
home,
|
||||||
ident_ids,
|
ident_ids,
|
||||||
|
target_info,
|
||||||
update_mode_ids,
|
update_mode_ids,
|
||||||
&mut env,
|
&mut env,
|
||||||
arena.alloc(proc.body),
|
arena.alloc(proc.body),
|
||||||
|
@ -93,6 +100,7 @@ fn insert_reset_reuse_operations_stmt<'a, 'i>(
|
||||||
layout_interner: &'i STLayoutInterner<'a>,
|
layout_interner: &'i STLayoutInterner<'a>,
|
||||||
home: ModuleId,
|
home: ModuleId,
|
||||||
ident_ids: &'i mut IdentIds,
|
ident_ids: &'i mut IdentIds,
|
||||||
|
target_info: TargetInfo,
|
||||||
update_mode_ids: &'i mut UpdateModeIds,
|
update_mode_ids: &'i mut UpdateModeIds,
|
||||||
environment: &mut ReuseEnvironment<'a>,
|
environment: &mut ReuseEnvironment<'a>,
|
||||||
stmt: &'a Stmt<'a>,
|
stmt: &'a Stmt<'a>,
|
||||||
|
@ -131,33 +139,45 @@ fn insert_reset_reuse_operations_stmt<'a, 'i>(
|
||||||
environment.add_symbol_tag(*binding, *tag_id);
|
environment.add_symbol_tag(*binding, *tag_id);
|
||||||
|
|
||||||
// Check if the tag id for this layout can be reused at all.
|
// Check if the tag id for this layout can be reused at all.
|
||||||
match can_reuse_union_layout_tag(tag_layout, Option::Some(*tag_id)) {
|
match can_reuse_union_layout_tag(*tag_layout, Option::Some(*tag_id)) {
|
||||||
// The tag is reusable.
|
// The tag is reusable.
|
||||||
Reuse::Reusable => {
|
Reuse::Reusable(union_layout) => {
|
||||||
// See if we have a token.
|
// See if we have a token.
|
||||||
match environment.pop_reuse_token(layout) {
|
match environment.pop_reuse_token(&ReuseLayout {
|
||||||
|
layout_info: get_reuse_layout_info(
|
||||||
|
layout_interner,
|
||||||
|
target_info,
|
||||||
|
union_layout,
|
||||||
|
),
|
||||||
|
// placeholder layout
|
||||||
|
layout,
|
||||||
|
}) {
|
||||||
// We have a reuse token for this layout, use it.
|
// We have a reuse token for this layout, use it.
|
||||||
Some(reuse_token) => {
|
Some(reuse_token) => {
|
||||||
|
let new_symbol = Symbol::new(home, ident_ids.gen_unique());
|
||||||
|
(
|
||||||
|
Some((reuse_token.symbol, new_symbol, layout)),
|
||||||
Expr::Reuse {
|
Expr::Reuse {
|
||||||
symbol: reuse_token.symbol,
|
symbol: new_symbol,
|
||||||
update_mode: reuse_token.update_mode_id,
|
update_mode: reuse_token.update_mode_id,
|
||||||
// for now, always overwrite the tag ID just to be sure
|
// for now, always overwrite the tag ID just to be sure
|
||||||
update_tag_id: true,
|
update_tag_id: true,
|
||||||
tag_layout: *tag_layout,
|
tag_layout: *tag_layout,
|
||||||
tag_id: *tag_id,
|
tag_id: *tag_id,
|
||||||
arguments,
|
arguments,
|
||||||
}
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// We have no reuse token available, keep the old expression with a fresh allocation.
|
// We have no reuse token available, keep the old expression with a fresh allocation.
|
||||||
None => expr.clone(),
|
None => (None, expr.clone()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// We cannot reuse this tag id because it's a null pointer.
|
// We cannot reuse this tag id because it's a null pointer.
|
||||||
Reuse::Nonreusable => expr.clone(),
|
Reuse::Nonreusable => (None, expr.clone()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => expr.clone(),
|
_ => (None, expr.clone()),
|
||||||
};
|
};
|
||||||
|
|
||||||
environment.add_symbol_layout(*binding, layout);
|
environment.add_symbol_layout(*binding, layout);
|
||||||
|
@ -169,6 +189,7 @@ fn insert_reset_reuse_operations_stmt<'a, 'i>(
|
||||||
layout_interner,
|
layout_interner,
|
||||||
home,
|
home,
|
||||||
ident_ids,
|
ident_ids,
|
||||||
|
target_info,
|
||||||
update_mode_ids,
|
update_mode_ids,
|
||||||
environment,
|
environment,
|
||||||
current_stmt,
|
current_stmt,
|
||||||
|
@ -176,8 +197,25 @@ fn insert_reset_reuse_operations_stmt<'a, 'i>(
|
||||||
|
|
||||||
new_triplets.into_iter().rev().fold(
|
new_triplets.into_iter().rev().fold(
|
||||||
new_continuation,
|
new_continuation,
|
||||||
|new_continuation, (binding, new_expr, layout)| {
|
|new_continuation, (binding, (reused, new_expr), layout)| {
|
||||||
arena.alloc(Stmt::Let(*binding, new_expr, *layout, new_continuation))
|
let new_let =
|
||||||
|
arena.alloc(Stmt::Let(*binding, new_expr, *layout, new_continuation));
|
||||||
|
match reused {
|
||||||
|
// The layout for the reuse does not match that of the reset, use PtrCast to convert the layout.
|
||||||
|
Some((old_symbol, new_symbol, layout)) => arena.alloc(Stmt::Let(
|
||||||
|
new_symbol,
|
||||||
|
Expr::Call(crate::ir::Call {
|
||||||
|
call_type: crate::ir::CallType::LowLevel {
|
||||||
|
op: roc_module::low_level::LowLevel::PtrCast,
|
||||||
|
update_mode: UpdateModeId::BACKEND_DUMMY,
|
||||||
|
},
|
||||||
|
arguments: Vec::from_iter_in([old_symbol], arena).into_bump_slice(),
|
||||||
|
}),
|
||||||
|
*layout,
|
||||||
|
new_let,
|
||||||
|
)),
|
||||||
|
None => new_let,
|
||||||
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -206,6 +244,7 @@ fn insert_reset_reuse_operations_stmt<'a, 'i>(
|
||||||
layout_interner,
|
layout_interner,
|
||||||
home,
|
home,
|
||||||
ident_ids,
|
ident_ids,
|
||||||
|
target_info,
|
||||||
update_mode_ids,
|
update_mode_ids,
|
||||||
&mut branch_env,
|
&mut branch_env,
|
||||||
branch,
|
branch,
|
||||||
|
@ -233,6 +272,7 @@ fn insert_reset_reuse_operations_stmt<'a, 'i>(
|
||||||
layout_interner,
|
layout_interner,
|
||||||
home,
|
home,
|
||||||
ident_ids,
|
ident_ids,
|
||||||
|
target_info,
|
||||||
update_mode_ids,
|
update_mode_ids,
|
||||||
&mut branch_env,
|
&mut branch_env,
|
||||||
branch,
|
branch,
|
||||||
|
@ -353,17 +393,14 @@ fn insert_reset_reuse_operations_stmt<'a, 'i>(
|
||||||
|
|
||||||
// If the symbol is defined in the current proc, we can use the layout from the environment.
|
// If the symbol is defined in the current proc, we can use the layout from the environment.
|
||||||
match layout_option.clone() {
|
match layout_option.clone() {
|
||||||
LayoutOption::Layout(layout)
|
LayoutOption::Layout(layout) => {
|
||||||
if matches!(
|
match symbol_layout_reusability(
|
||||||
symbol_layout_reusability(
|
|
||||||
layout_interner,
|
layout_interner,
|
||||||
environment,
|
environment,
|
||||||
symbol,
|
symbol,
|
||||||
layout
|
layout,
|
||||||
),
|
) {
|
||||||
Reuse::Reusable
|
Reuse::Reusable(union_layout) => {
|
||||||
) =>
|
|
||||||
{
|
|
||||||
let reuse_token = ReuseToken {
|
let reuse_token = ReuseToken {
|
||||||
symbol: Symbol::new(home, ident_ids.gen_unique()),
|
symbol: Symbol::new(home, ident_ids.gen_unique()),
|
||||||
update_mode_id: update_mode_ids.next_id(),
|
update_mode_id: update_mode_ids.next_id(),
|
||||||
|
@ -375,8 +412,22 @@ fn insert_reset_reuse_operations_stmt<'a, 'i>(
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
environment.push_reuse_token(arena, layout, reuse_token);
|
environment.push_reuse_token(
|
||||||
Some((layout, *symbol, reuse_token, dec_ref))
|
arena,
|
||||||
|
ReuseLayout {
|
||||||
|
layout_info: get_reuse_layout_info(
|
||||||
|
layout_interner,
|
||||||
|
target_info,
|
||||||
|
union_layout,
|
||||||
|
),
|
||||||
|
layout,
|
||||||
|
},
|
||||||
|
reuse_token,
|
||||||
|
);
|
||||||
|
Some((layout, union_layout, *symbol, reuse_token, dec_ref))
|
||||||
|
}
|
||||||
|
Reuse::Nonreusable => None,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
|
@ -388,20 +439,33 @@ fn insert_reset_reuse_operations_stmt<'a, 'i>(
|
||||||
layout_interner,
|
layout_interner,
|
||||||
home,
|
home,
|
||||||
ident_ids,
|
ident_ids,
|
||||||
|
target_info,
|
||||||
update_mode_ids,
|
update_mode_ids,
|
||||||
environment,
|
environment,
|
||||||
continuation,
|
continuation,
|
||||||
);
|
);
|
||||||
|
|
||||||
// If we inserted a reuse token, we need to insert a reset reuse operation if the reuse token is consumed.
|
// If we inserted a reuse token, we need to insert a reset reuse operation if the reuse token is consumed.
|
||||||
if let Some((layout, symbol, reuse_token, dec_ref)) = reuse_pair {
|
if let Some((layout, union_layout, symbol, reuse_token, dec_ref)) = reuse_pair {
|
||||||
let stack_reuse_token = environment.peek_reuse_token(layout);
|
let stack_reuse_token = environment.peek_reuse_token(&ReuseLayout {
|
||||||
|
layout_info: get_reuse_layout_info(layout_interner, target_info, union_layout),
|
||||||
|
// placeholder
|
||||||
|
layout,
|
||||||
|
});
|
||||||
|
|
||||||
match stack_reuse_token {
|
match stack_reuse_token {
|
||||||
Some(reuse_symbol) if reuse_symbol == reuse_token => {
|
Some(reuse_symbol) if reuse_symbol == reuse_token => {
|
||||||
// The token we inserted is still on the stack, so we don't need to insert a reset operation.
|
// The token we inserted is still on the stack, so we don't need to insert a reset operation.
|
||||||
// We do need to remove the token from the environment. To prevent errors higher in the tree.
|
// We do need to remove the token from the environment. To prevent errors higher in the tree.
|
||||||
let _ = environment.pop_reuse_token(layout);
|
let _ = environment.pop_reuse_token(&ReuseLayout {
|
||||||
|
layout_info: get_reuse_layout_info(
|
||||||
|
layout_interner,
|
||||||
|
target_info,
|
||||||
|
union_layout,
|
||||||
|
),
|
||||||
|
// placeholder
|
||||||
|
layout,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
// The token we inserted is no longer on the stack, it must have been consumed.
|
// The token we inserted is no longer on the stack, it must have been consumed.
|
||||||
|
@ -453,6 +517,7 @@ fn insert_reset_reuse_operations_stmt<'a, 'i>(
|
||||||
layout_interner,
|
layout_interner,
|
||||||
home,
|
home,
|
||||||
ident_ids,
|
ident_ids,
|
||||||
|
target_info,
|
||||||
update_mode_ids,
|
update_mode_ids,
|
||||||
environment,
|
environment,
|
||||||
remainder,
|
remainder,
|
||||||
|
@ -478,6 +543,7 @@ fn insert_reset_reuse_operations_stmt<'a, 'i>(
|
||||||
layout_interner,
|
layout_interner,
|
||||||
home,
|
home,
|
||||||
ident_ids,
|
ident_ids,
|
||||||
|
target_info,
|
||||||
update_mode_ids,
|
update_mode_ids,
|
||||||
environment,
|
environment,
|
||||||
remainder,
|
remainder,
|
||||||
|
@ -501,6 +567,7 @@ fn insert_reset_reuse_operations_stmt<'a, 'i>(
|
||||||
layout_interner,
|
layout_interner,
|
||||||
home,
|
home,
|
||||||
ident_ids,
|
ident_ids,
|
||||||
|
target_info,
|
||||||
update_mode_ids,
|
update_mode_ids,
|
||||||
environment,
|
environment,
|
||||||
remainder,
|
remainder,
|
||||||
|
@ -536,6 +603,7 @@ fn insert_reset_reuse_operations_stmt<'a, 'i>(
|
||||||
layout_interner,
|
layout_interner,
|
||||||
home,
|
home,
|
||||||
ident_ids,
|
ident_ids,
|
||||||
|
target_info,
|
||||||
update_mode_ids,
|
update_mode_ids,
|
||||||
&mut first_pass_environment,
|
&mut first_pass_environment,
|
||||||
remainder,
|
remainder,
|
||||||
|
@ -616,6 +684,7 @@ fn insert_reset_reuse_operations_stmt<'a, 'i>(
|
||||||
layout_interner,
|
layout_interner,
|
||||||
home,
|
home,
|
||||||
ident_ids,
|
ident_ids,
|
||||||
|
target_info,
|
||||||
update_mode_ids,
|
update_mode_ids,
|
||||||
&mut first_pass_body_environment,
|
&mut first_pass_body_environment,
|
||||||
body,
|
body,
|
||||||
|
@ -706,6 +775,7 @@ fn insert_reset_reuse_operations_stmt<'a, 'i>(
|
||||||
layout_interner,
|
layout_interner,
|
||||||
home,
|
home,
|
||||||
ident_ids,
|
ident_ids,
|
||||||
|
target_info,
|
||||||
update_mode_ids,
|
update_mode_ids,
|
||||||
environment,
|
environment,
|
||||||
remainder,
|
remainder,
|
||||||
|
@ -733,10 +803,10 @@ fn insert_reset_reuse_operations_stmt<'a, 'i>(
|
||||||
let token_params =
|
let token_params =
|
||||||
layouts_for_reuse_with_token
|
layouts_for_reuse_with_token
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(layout, token)| Param {
|
.map(|(reuse_layout, token)| Param {
|
||||||
symbol: token.symbol,
|
symbol: token.symbol,
|
||||||
ownership: Ownership::Owned,
|
ownership: Ownership::Owned,
|
||||||
layout,
|
layout: *reuse_layout.layout,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Add the void tokens to the jump arguments to match the expected arguments of the join point.
|
// Add the void tokens to the jump arguments to match the expected arguments of the join point.
|
||||||
|
@ -792,6 +862,7 @@ fn insert_reset_reuse_operations_stmt<'a, 'i>(
|
||||||
layout_interner,
|
layout_interner,
|
||||||
home,
|
home,
|
||||||
ident_ids,
|
ident_ids,
|
||||||
|
target_info,
|
||||||
update_mode_ids,
|
update_mode_ids,
|
||||||
&mut body_environment,
|
&mut body_environment,
|
||||||
body,
|
body,
|
||||||
|
@ -858,7 +929,7 @@ fn insert_reset_reuse_operations_stmt<'a, 'i>(
|
||||||
void_pointer_layout_symbols.into_iter().fold(
|
void_pointer_layout_symbols.into_iter().fold(
|
||||||
arena.alloc(Stmt::Jump(*id, extended_arguments)),
|
arena.alloc(Stmt::Jump(*id, extended_arguments)),
|
||||||
|child, (layout, symbol)| {
|
|child, (layout, symbol)| {
|
||||||
arena.alloc(Stmt::Let(symbol, Expr::NullPointer, layout, child))
|
arena.alloc(Stmt::Let(symbol, Expr::NullPointer, *layout.layout, child))
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -870,7 +941,8 @@ fn insert_reset_reuse_operations_stmt<'a, 'i>(
|
||||||
|
|
||||||
// We currently don't pass any reuse tokens to recursive jumps.
|
// We currently don't pass any reuse tokens to recursive jumps.
|
||||||
// This is to avoid keeping reuse tokens alive for too long. But it could be changed.
|
// This is to avoid keeping reuse tokens alive for too long. But it could be changed.
|
||||||
let mut void_pointer_layout_symbols: std::vec::Vec<(InLayout, Symbol)> = vec![];
|
let mut void_pointer_layout_symbols: std::vec::Vec<(ReuseLayout, Symbol)> =
|
||||||
|
vec![];
|
||||||
|
|
||||||
let void_tokens =
|
let void_tokens =
|
||||||
token_layouts
|
token_layouts
|
||||||
|
@ -898,8 +970,13 @@ fn insert_reset_reuse_operations_stmt<'a, 'i>(
|
||||||
// Wrap the jump in a let statement for each void pointer token layout.
|
// Wrap the jump in a let statement for each void pointer token layout.
|
||||||
void_pointer_layout_symbols.into_iter().fold(
|
void_pointer_layout_symbols.into_iter().fold(
|
||||||
arena.alloc(Stmt::Jump(*id, extended_arguments)),
|
arena.alloc(Stmt::Jump(*id, extended_arguments)),
|
||||||
|child, (layout, symbol)| {
|
|child, (reuse_layout, symbol)| {
|
||||||
arena.alloc(Stmt::Let(symbol, Expr::NullPointer, layout, child))
|
arena.alloc(Stmt::Let(
|
||||||
|
symbol,
|
||||||
|
Expr::NullPointer,
|
||||||
|
*reuse_layout.layout,
|
||||||
|
child,
|
||||||
|
))
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -912,9 +989,9 @@ fn insert_reset_reuse_operations_stmt<'a, 'i>(
|
||||||
// TODO make sure all dup/drop operations are already inserted statically.
|
// TODO make sure all dup/drop operations are already inserted statically.
|
||||||
// (e.g. not as a side effect of another operation) To make sure we can actually reuse.
|
// (e.g. not as a side effect of another operation) To make sure we can actually reuse.
|
||||||
|
|
||||||
enum Reuse {
|
enum Reuse<'a> {
|
||||||
// Reuseable but the pointer *might* be null, which will cause a fresh allocation.
|
// Reuseable but the pointer *might* be null, which will cause a fresh allocation.
|
||||||
Reusable,
|
Reusable(UnionLayout<'a>),
|
||||||
Nonreusable,
|
Nonreusable,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -922,7 +999,51 @@ enum Reuse {
|
||||||
Map containing the reuse tokens of a layout.
|
Map containing the reuse tokens of a layout.
|
||||||
A vec is used as a stack as we want to use the latest reuse token available.
|
A vec is used as a stack as we want to use the latest reuse token available.
|
||||||
*/
|
*/
|
||||||
type ReuseTokens<'a> = MutMap<InLayout<'a>, Vec<'a, ReuseToken>>;
|
type ReuseTokens<'a> = MutMap<ReuseLayout<'a>, Vec<'a, ReuseToken>>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Struct to to check whether two reuse layouts are interchangeable.
|
||||||
|
*/
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||||
|
struct ReuseLayoutInfo {
|
||||||
|
has_tag: bool,
|
||||||
|
size: u32,
|
||||||
|
alignment: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Struct containing the layout of a reuse token, and the layout of the contained union.
|
||||||
|
Hashing/Equality is only done on the union layout. And the normal layout is kept for bookkeeping.
|
||||||
|
*/
|
||||||
|
#[derive(Clone, Copy, Eq)]
|
||||||
|
struct ReuseLayout<'a> {
|
||||||
|
layout_info: ReuseLayoutInfo,
|
||||||
|
layout: &'a InLayout<'a>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Hash for ReuseLayout<'a> {
|
||||||
|
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||||
|
self.layout_info.hash(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> PartialEq for ReuseLayout<'a> {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.layout_info == other.layout_info
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> PartialOrd for ReuseLayout<'a> {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||||
|
self.layout_info.partial_cmp(&other.layout_info)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Ord for ReuseLayout<'a> {
|
||||||
|
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||||
|
self.layout_info.cmp(&other.layout_info)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
A reuse token is a symbol that is used to reset a layout.
|
A reuse token is a symbol that is used to reset a layout.
|
||||||
|
@ -962,7 +1083,7 @@ enum JoinPointReuseTokens<'a> {
|
||||||
BodyFirst,
|
BodyFirst,
|
||||||
|
|
||||||
// Second body pass, to update any jump calls to pass void pointer parameters instead of no parameters.
|
// Second body pass, to update any jump calls to pass void pointer parameters instead of no parameters.
|
||||||
BodySecond(Vec<'a, InLayout<'a>>),
|
BodySecond(Vec<'a, ReuseLayout<'a>>),
|
||||||
|
|
||||||
// The first pass is used to determine the amount of reuse tokens a join point can expect.
|
// The first pass is used to determine the amount of reuse tokens a join point can expect.
|
||||||
// Therefore, we don't know the amount of reuse tokens yet.
|
// Therefore, we don't know the amount of reuse tokens yet.
|
||||||
|
@ -970,7 +1091,7 @@ enum JoinPointReuseTokens<'a> {
|
||||||
|
|
||||||
// In the second pass, we determined the amount of reuse tokens a join point can expect.
|
// In the second pass, we determined the amount of reuse tokens a join point can expect.
|
||||||
// Therefore, we know the amount of reuse tokens and can use.
|
// Therefore, we know the amount of reuse tokens and can use.
|
||||||
RemainderSecond(Vec<'a, InLayout<'a>>),
|
RemainderSecond(Vec<'a, ReuseLayout<'a>>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Clone)]
|
#[derive(Default, Clone)]
|
||||||
|
@ -1003,7 +1124,7 @@ impl<'a> ReuseEnvironment<'a> {
|
||||||
/**
|
/**
|
||||||
Retrieve a reuse token for a layout from the stack for said layout.
|
Retrieve a reuse token for a layout from the stack for said layout.
|
||||||
*/
|
*/
|
||||||
fn pop_reuse_token(&mut self, layout: &InLayout<'a>) -> Option<ReuseToken> {
|
fn pop_reuse_token(&mut self, layout: &ReuseLayout<'a>) -> Option<ReuseToken> {
|
||||||
let reuse_tokens = self.reuse_tokens.get_mut(layout)?;
|
let reuse_tokens = self.reuse_tokens.get_mut(layout)?;
|
||||||
// If the layout is in the map, pop the token from the stack.
|
// If the layout is in the map, pop the token from the stack.
|
||||||
let reuse_token = reuse_tokens.pop();
|
let reuse_token = reuse_tokens.pop();
|
||||||
|
@ -1018,7 +1139,7 @@ impl<'a> ReuseEnvironment<'a> {
|
||||||
Retrieve a reuse token for a layout from the stack for said layout.
|
Retrieve a reuse token for a layout from the stack for said layout.
|
||||||
Without consuming the token.
|
Without consuming the token.
|
||||||
*/
|
*/
|
||||||
fn peek_reuse_token(&mut self, layout: &InLayout<'a>) -> Option<ReuseToken> {
|
fn peek_reuse_token(&mut self, layout: &ReuseLayout<'a>) -> Option<ReuseToken> {
|
||||||
let reuse_tokens = self.reuse_tokens.get(layout)?;
|
let reuse_tokens = self.reuse_tokens.get(layout)?;
|
||||||
// If the layout is in the map, peek at the last element.
|
// If the layout is in the map, peek at the last element.
|
||||||
let reuse_token = reuse_tokens.last();
|
let reuse_token = reuse_tokens.last();
|
||||||
|
@ -1028,8 +1149,8 @@ impl<'a> ReuseEnvironment<'a> {
|
||||||
/**
|
/**
|
||||||
Push a reuse token for a layout on the stack for said layout.
|
Push a reuse token for a layout on the stack for said layout.
|
||||||
*/
|
*/
|
||||||
fn push_reuse_token(&mut self, arena: &'a Bump, layout: &InLayout<'a>, token: ReuseToken) {
|
fn push_reuse_token(&mut self, arena: &'a Bump, layout: ReuseLayout<'a>, token: ReuseToken) {
|
||||||
match self.reuse_tokens.get_mut(layout) {
|
match self.reuse_tokens.get_mut(&layout) {
|
||||||
Some(reuse_tokens) => {
|
Some(reuse_tokens) => {
|
||||||
// If the layout is already in the map, push the token on the stack.
|
// If the layout is already in the map, push the token on the stack.
|
||||||
reuse_tokens.push(token);
|
reuse_tokens.push(token);
|
||||||
|
@ -1037,7 +1158,7 @@ impl<'a> ReuseEnvironment<'a> {
|
||||||
None => {
|
None => {
|
||||||
// If the layout is not in the map, create a new stack with the token.
|
// If the layout is not in the map, create a new stack with the token.
|
||||||
self.reuse_tokens
|
self.reuse_tokens
|
||||||
.insert(*layout, Vec::from_iter_in([token], arena));
|
.insert(layout, Vec::from_iter_in([token], arena));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1132,10 +1253,10 @@ fn symbol_layout_reusability<'a>(
|
||||||
environment: &ReuseEnvironment<'a>,
|
environment: &ReuseEnvironment<'a>,
|
||||||
symbol: &Symbol,
|
symbol: &Symbol,
|
||||||
layout: &InLayout<'a>,
|
layout: &InLayout<'a>,
|
||||||
) -> Reuse {
|
) -> Reuse<'a> {
|
||||||
match layout_interner.get(*layout).repr {
|
match layout_interner.get(*layout).repr {
|
||||||
LayoutRepr::Union(union_layout) => {
|
LayoutRepr::Union(union_layout) => {
|
||||||
can_reuse_union_layout_tag(&union_layout, environment.get_symbol_tag(symbol))
|
can_reuse_union_layout_tag(union_layout, environment.get_symbol_tag(symbol))
|
||||||
}
|
}
|
||||||
// Strings literals are constants.
|
// Strings literals are constants.
|
||||||
// Arrays are probably given to functions and reused there. Little use to reuse them here.
|
// Arrays are probably given to functions and reused there. Little use to reuse them here.
|
||||||
|
@ -1146,13 +1267,16 @@ fn symbol_layout_reusability<'a>(
|
||||||
/**
|
/**
|
||||||
Check if a union layout can be reused. by verifying if the tag is not nullable.
|
Check if a union layout can be reused. by verifying if the tag is not nullable.
|
||||||
*/
|
*/
|
||||||
fn can_reuse_union_layout_tag(union_layout: &UnionLayout<'_>, tag_id_option: Option<Tag>) -> Reuse {
|
fn can_reuse_union_layout_tag<'a>(
|
||||||
|
union_layout: UnionLayout<'a>,
|
||||||
|
tag_id_option: Option<Tag>,
|
||||||
|
) -> Reuse<'a> {
|
||||||
match union_layout {
|
match union_layout {
|
||||||
UnionLayout::NonRecursive(_) => Reuse::Nonreusable,
|
UnionLayout::NonRecursive(_) => Reuse::Nonreusable,
|
||||||
// Non nullable union layouts
|
// Non nullable union layouts
|
||||||
UnionLayout::Recursive(_) | UnionLayout::NonNullableUnwrapped(_) => {
|
UnionLayout::Recursive(_) | UnionLayout::NonNullableUnwrapped(_) => {
|
||||||
// Non nullable union layouts can always be reused.
|
// Non nullable union layouts can always be reused.
|
||||||
Reuse::Reusable
|
Reuse::Reusable(union_layout)
|
||||||
}
|
}
|
||||||
// Nullable union layouts
|
// Nullable union layouts
|
||||||
UnionLayout::NullableWrapped { .. } | UnionLayout::NullableUnwrapped { .. } => {
|
UnionLayout::NullableWrapped { .. } | UnionLayout::NullableUnwrapped { .. } => {
|
||||||
|
@ -1163,13 +1287,13 @@ fn can_reuse_union_layout_tag(union_layout: &UnionLayout<'_>, tag_id_option: Opt
|
||||||
Reuse::Nonreusable
|
Reuse::Nonreusable
|
||||||
} else {
|
} else {
|
||||||
// Symbol of layout is not null, so it can be reused.
|
// Symbol of layout is not null, so it can be reused.
|
||||||
Reuse::Reusable
|
Reuse::Reusable(union_layout)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
// Symbol of layout might be null, so it might be reused.
|
// Symbol of layout might be null, so it might be reused.
|
||||||
// If null will cause null pointer and fresh allocation.
|
// If null will cause null pointer and fresh allocation.
|
||||||
Reuse::Reusable
|
Reuse::Reusable(union_layout)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1193,3 +1317,23 @@ fn drop_unused_reuse_tokens<'a>(
|
||||||
))
|
))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_reuse_layout_info<'a, 'i>(
|
||||||
|
layout_interner: &'i STLayoutInterner<'a>,
|
||||||
|
target_info: TargetInfo,
|
||||||
|
union_layout: UnionLayout<'a>,
|
||||||
|
) -> ReuseLayoutInfo {
|
||||||
|
let (size, alignment) = union_layout.data_size_and_alignment(layout_interner, target_info);
|
||||||
|
let has_tag = match union_layout {
|
||||||
|
UnionLayout::NonRecursive(_) => unreachable!("Non recursive unions should not be reused."),
|
||||||
|
// The memory for union layouts that has a tag_id can be reused for new allocations with tag_id.
|
||||||
|
UnionLayout::Recursive(_) | UnionLayout::NullableWrapped { .. } => true,
|
||||||
|
// The memory for union layouts that have no tag_id can be reused for new allocations without tag_id
|
||||||
|
UnionLayout::NonNullableUnwrapped(_) | UnionLayout::NullableUnwrapped { .. } => false,
|
||||||
|
};
|
||||||
|
ReuseLayoutInfo {
|
||||||
|
has_tag,
|
||||||
|
size,
|
||||||
|
alignment,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
63
crates/compiler/test_mono/generated/binary_tree_fbip.txt
Normal file
63
crates/compiler/test_mono/generated/binary_tree_fbip.txt
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
procedure Num.19 (#Attr.2, #Attr.3):
|
||||||
|
let Num.281 : I64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||||
|
ret Num.281;
|
||||||
|
|
||||||
|
procedure Test.4 (Test.27):
|
||||||
|
let Test.39 : [<rnu>C [<rnu><null>, C *self *self] *self, <null>] = TagId(0) ;
|
||||||
|
let Test.40 : I64 = 0i64;
|
||||||
|
let Test.38 : I64 = CallByName Test.5 Test.27 Test.39 Test.40;
|
||||||
|
ret Test.38;
|
||||||
|
|
||||||
|
procedure Test.5 (Test.67, Test.68, Test.69):
|
||||||
|
joinpoint Test.41 Test.29 Test.30 Test.31:
|
||||||
|
let Test.51 : U8 = 0i64;
|
||||||
|
let Test.52 : U8 = GetTagId Test.29;
|
||||||
|
let Test.53 : Int1 = lowlevel Eq Test.51 Test.52;
|
||||||
|
if Test.53 then
|
||||||
|
let Test.32 : [<rnu><null>, C *self *self] = UnionAtIndex (Id 0) (Index 0) Test.29;
|
||||||
|
inc Test.32;
|
||||||
|
let Test.33 : [<rnu><null>, C *self *self] = UnionAtIndex (Id 0) (Index 1) Test.29;
|
||||||
|
inc Test.33;
|
||||||
|
let #Derived_gen.0 : [<rnu><null>, C *self *self] = Reset { symbol: Test.29, id: UpdateModeId { id: 0 } };
|
||||||
|
let #Derived_gen.1 : [<rnu>C [<rnu><null>, C *self *self] *self, <null>] = lowlevel PtrCast #Derived_gen.0;
|
||||||
|
let Test.43 : [<rnu>C [<rnu><null>, C *self *self] *self, <null>] = Reuse #Derived_gen.1 UpdateModeId { id: 0 } TagId(1) Test.33 Test.30;
|
||||||
|
let Test.45 : I64 = 1i64;
|
||||||
|
let Test.44 : I64 = CallByName Num.19 Test.31 Test.45;
|
||||||
|
jump Test.41 Test.32 Test.43 Test.44;
|
||||||
|
else
|
||||||
|
let Test.48 : U8 = 1i64;
|
||||||
|
let Test.49 : U8 = GetTagId Test.30;
|
||||||
|
let Test.50 : Int1 = lowlevel Eq Test.48 Test.49;
|
||||||
|
if Test.50 then
|
||||||
|
let Test.35 : [<rnu><null>, C *self *self] = UnionAtIndex (Id 1) (Index 0) Test.30;
|
||||||
|
let Test.36 : [<rnu>C [<rnu><null>, C *self *self] *self, <null>] = UnionAtIndex (Id 1) (Index 1) Test.30;
|
||||||
|
let #Derived_gen.3 : Int1 = lowlevel RefCountIsUnique Test.30;
|
||||||
|
if #Derived_gen.3 then
|
||||||
|
decref Test.30;
|
||||||
|
jump Test.41 Test.35 Test.36 Test.31;
|
||||||
|
else
|
||||||
|
inc Test.35;
|
||||||
|
inc Test.36;
|
||||||
|
decref Test.30;
|
||||||
|
jump Test.41 Test.35 Test.36 Test.31;
|
||||||
|
else
|
||||||
|
ret Test.31;
|
||||||
|
in
|
||||||
|
jump Test.41 Test.67 Test.68 Test.69;
|
||||||
|
|
||||||
|
procedure Test.0 ():
|
||||||
|
let Test.64 : [<rnu><null>, C *self *self] = TagId(1) ;
|
||||||
|
let Test.65 : [<rnu><null>, C *self *self] = TagId(1) ;
|
||||||
|
let Test.62 : [<rnu><null>, C *self *self] = TagId(0) Test.64 Test.65;
|
||||||
|
let Test.63 : [<rnu><null>, C *self *self] = TagId(1) ;
|
||||||
|
let Test.58 : [<rnu><null>, C *self *self] = TagId(0) Test.62 Test.63;
|
||||||
|
let Test.60 : [<rnu><null>, C *self *self] = TagId(1) ;
|
||||||
|
let Test.61 : [<rnu><null>, C *self *self] = TagId(1) ;
|
||||||
|
let Test.59 : [<rnu><null>, C *self *self] = TagId(0) Test.60 Test.61;
|
||||||
|
let Test.54 : [<rnu><null>, C *self *self] = TagId(0) Test.58 Test.59;
|
||||||
|
let Test.56 : [<rnu><null>, C *self *self] = TagId(1) ;
|
||||||
|
let Test.57 : [<rnu><null>, C *self *self] = TagId(1) ;
|
||||||
|
let Test.55 : [<rnu><null>, C *self *self] = TagId(0) Test.56 Test.57;
|
||||||
|
let Test.10 : [<rnu><null>, C *self *self] = TagId(0) Test.54 Test.55;
|
||||||
|
let Test.37 : I64 = CallByName Test.4 Test.10;
|
||||||
|
ret Test.37;
|
|
@ -113,22 +113,23 @@ procedure Test.1 (Test.77):
|
||||||
let Test.49 : [<r>C I64, C List *self] = StructAtIndex 1 Test.6;
|
let Test.49 : [<r>C I64, C List *self] = StructAtIndex 1 Test.6;
|
||||||
dec Test.50;
|
dec Test.50;
|
||||||
let Test.10 : I64 = UnionAtIndex (Id 0) (Index 0) Test.49;
|
let Test.10 : I64 = UnionAtIndex (Id 0) (Index 0) Test.49;
|
||||||
joinpoint #Derived_gen.7:
|
joinpoint #Derived_gen.9:
|
||||||
let Test.27 : Int1 = CallByName Num.22 Test.8 Test.10;
|
let Test.27 : Int1 = CallByName Num.22 Test.8 Test.10;
|
||||||
ret Test.27;
|
ret Test.27;
|
||||||
in
|
in
|
||||||
let #Derived_gen.8 : Int1 = lowlevel RefCountIsUnique Test.49;
|
let #Derived_gen.10 : Int1 = lowlevel RefCountIsUnique Test.49;
|
||||||
if #Derived_gen.8 then
|
if #Derived_gen.10 then
|
||||||
decref Test.49;
|
decref Test.49;
|
||||||
jump #Derived_gen.7;
|
jump #Derived_gen.9;
|
||||||
else
|
else
|
||||||
decref Test.49;
|
decref Test.49;
|
||||||
jump #Derived_gen.7;
|
jump #Derived_gen.9;
|
||||||
else
|
else
|
||||||
let Test.39 : [<r>C I64, C List *self] = StructAtIndex 0 Test.6;
|
let Test.39 : [<r>C I64, C List *self] = StructAtIndex 0 Test.6;
|
||||||
let Test.42 : [<r>C I64, C List *self] = StructAtIndex 1 Test.6;
|
let Test.42 : [<r>C I64, C List *self] = StructAtIndex 1 Test.6;
|
||||||
let Test.41 : List [<r>C I64, C List *self] = Array [Test.42];
|
let Test.41 : List [<r>C I64, C List *self] = Array [Test.42];
|
||||||
let Test.40 : [<r>C I64, C List *self] = Reuse #Derived_gen.1 UpdateModeId { id: 1 } TagId(1) Test.41;
|
let #Derived_gen.4 : [<r>C I64, C List *self] = lowlevel PtrCast #Derived_gen.1;
|
||||||
|
let Test.40 : [<r>C I64, C List *self] = Reuse #Derived_gen.4 UpdateModeId { id: 1 } TagId(1) Test.41;
|
||||||
let Test.38 : {[<r>C I64, C List *self], [<r>C I64, C List *self]} = Struct {Test.39, Test.40};
|
let Test.38 : {[<r>C I64, C List *self], [<r>C I64, C List *self]} = Struct {Test.39, Test.40};
|
||||||
jump Test.26 Test.38;
|
jump Test.26 Test.38;
|
||||||
else
|
else
|
||||||
|
@ -136,17 +137,17 @@ procedure Test.1 (Test.77):
|
||||||
inc Test.61;
|
inc Test.61;
|
||||||
let Test.62 : U8 = 1i64;
|
let Test.62 : U8 = 1i64;
|
||||||
let Test.63 : U8 = GetTagId Test.61;
|
let Test.63 : U8 = GetTagId Test.61;
|
||||||
let #Derived_gen.4 : [<r>C I64, C List *self] = Reset { symbol: Test.61, id: UpdateModeId { id: 4 } };
|
let #Derived_gen.5 : [<r>C I64, C List *self] = Reset { symbol: Test.61, id: UpdateModeId { id: 4 } };
|
||||||
let Test.64 : Int1 = lowlevel Eq Test.62 Test.63;
|
let Test.64 : Int1 = lowlevel Eq Test.62 Test.63;
|
||||||
if Test.64 then
|
if Test.64 then
|
||||||
decref #Derived_gen.4;
|
decref #Derived_gen.5;
|
||||||
let Test.52 : [<r>C I64, C List *self] = StructAtIndex 0 Test.6;
|
let Test.52 : [<r>C I64, C List *self] = StructAtIndex 0 Test.6;
|
||||||
let Test.12 : List [<r>C I64, C List *self] = UnionAtIndex (Id 1) (Index 0) Test.52;
|
let Test.12 : List [<r>C I64, C List *self] = UnionAtIndex (Id 1) (Index 0) Test.52;
|
||||||
inc Test.12;
|
inc Test.12;
|
||||||
let Test.51 : [<r>C I64, C List *self] = StructAtIndex 1 Test.6;
|
let Test.51 : [<r>C I64, C List *self] = StructAtIndex 1 Test.6;
|
||||||
dec Test.52;
|
dec Test.52;
|
||||||
let Test.14 : List [<r>C I64, C List *self] = UnionAtIndex (Id 1) (Index 0) Test.51;
|
let Test.14 : List [<r>C I64, C List *self] = UnionAtIndex (Id 1) (Index 0) Test.51;
|
||||||
joinpoint #Derived_gen.9:
|
joinpoint #Derived_gen.11:
|
||||||
let Test.35 : {} = Struct {};
|
let Test.35 : {} = Struct {};
|
||||||
inc Test.12;
|
inc Test.12;
|
||||||
inc Test.14;
|
inc Test.14;
|
||||||
|
@ -166,19 +167,22 @@ procedure Test.1 (Test.77):
|
||||||
let Test.28 : Int1 = CallByName Bool.1;
|
let Test.28 : Int1 = CallByName Bool.1;
|
||||||
ret Test.28;
|
ret Test.28;
|
||||||
in
|
in
|
||||||
let #Derived_gen.10 : Int1 = lowlevel RefCountIsUnique Test.51;
|
let #Derived_gen.12 : Int1 = lowlevel RefCountIsUnique Test.51;
|
||||||
if #Derived_gen.10 then
|
if #Derived_gen.12 then
|
||||||
decref Test.51;
|
decref Test.51;
|
||||||
jump #Derived_gen.9;
|
jump #Derived_gen.11;
|
||||||
else
|
else
|
||||||
inc Test.14;
|
inc Test.14;
|
||||||
decref Test.51;
|
decref Test.51;
|
||||||
jump #Derived_gen.9;
|
jump #Derived_gen.11;
|
||||||
else
|
else
|
||||||
let Test.48 : [<r>C I64, C List *self] = StructAtIndex 0 Test.6;
|
let Test.48 : [<r>C I64, C List *self] = StructAtIndex 0 Test.6;
|
||||||
|
inc Test.48;
|
||||||
let Test.47 : List [<r>C I64, C List *self] = Array [Test.48];
|
let Test.47 : List [<r>C I64, C List *self] = Array [Test.48];
|
||||||
let Test.45 : [<r>C I64, C List *self] = Reuse #Derived_gen.4 UpdateModeId { id: 4 } TagId(1) Test.47;
|
let #Derived_gen.8 : [<r>C I64, C List *self] = lowlevel PtrCast #Derived_gen.5;
|
||||||
|
let Test.45 : [<r>C I64, C List *self] = Reuse #Derived_gen.8 UpdateModeId { id: 4 } TagId(1) Test.47;
|
||||||
let Test.46 : [<r>C I64, C List *self] = StructAtIndex 1 Test.6;
|
let Test.46 : [<r>C I64, C List *self] = StructAtIndex 1 Test.6;
|
||||||
|
dec Test.48;
|
||||||
let Test.44 : {[<r>C I64, C List *self], [<r>C I64, C List *self]} = Struct {Test.45, Test.46};
|
let Test.44 : {[<r>C I64, C List *self], [<r>C I64, C List *self]} = Struct {Test.45, Test.46};
|
||||||
jump Test.26 Test.44;
|
jump Test.26 Test.44;
|
||||||
in
|
in
|
||||||
|
|
|
@ -9,7 +9,8 @@ procedure Test.2 (Test.5):
|
||||||
let #Derived_gen.1 : [<rnnu>C List *self] = Reset { symbol: Test.5, id: UpdateModeId { id: 1 } };
|
let #Derived_gen.1 : [<rnnu>C List *self] = Reset { symbol: Test.5, id: UpdateModeId { id: 1 } };
|
||||||
let Test.15 : {} = Struct {};
|
let Test.15 : {} = Struct {};
|
||||||
let Test.7 : List [<rnnu>C List *self] = CallByName List.5 Test.6 Test.15;
|
let Test.7 : List [<rnnu>C List *self] = CallByName List.5 Test.6 Test.15;
|
||||||
let Test.14 : [<rnnu>C List *self] = Reuse #Derived_gen.1 UpdateModeId { id: 1 } TagId(0) Test.7;
|
let #Derived_gen.2 : [<rnnu>C List *self] = lowlevel PtrCast #Derived_gen.1;
|
||||||
|
let Test.14 : [<rnnu>C List *self] = Reuse #Derived_gen.2 UpdateModeId { id: 1 } TagId(0) Test.7;
|
||||||
ret Test.14;
|
ret Test.14;
|
||||||
|
|
||||||
procedure Test.0 ():
|
procedure Test.0 ():
|
||||||
|
|
|
@ -2923,3 +2923,35 @@ fn error_on_erroneous_condition() {
|
||||||
"#
|
"#
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[mono_test]
|
||||||
|
fn binary_tree_fbip() {
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
app "test" provides [main] to "./platform"
|
||||||
|
|
||||||
|
main =
|
||||||
|
tree = Node (Node (Node (Node Tip Tip) Tip) (Node Tip Tip)) (Node Tip Tip)
|
||||||
|
checkFbip tree
|
||||||
|
|
||||||
|
Tree : [Node Tree Tree, Tip]
|
||||||
|
|
||||||
|
check : Tree -> Num a
|
||||||
|
check = \t -> when t is
|
||||||
|
Node l r -> check l + check r + 1
|
||||||
|
Tip -> 0
|
||||||
|
|
||||||
|
Visit : [NodeR Tree Visit, Done]
|
||||||
|
|
||||||
|
checkFbip : Tree -> Num a
|
||||||
|
checkFbip = \t -> checkFbipHelper t Done 0
|
||||||
|
|
||||||
|
checkFbipHelper : Tree, Visit, Num a-> Num a
|
||||||
|
checkFbipHelper = \t, v, a -> when t is
|
||||||
|
Node l r -> checkFbipHelper l (NodeR r v) (a + 1)
|
||||||
|
Tip -> when v is
|
||||||
|
NodeR r v2 -> checkFbipHelper r v2 a
|
||||||
|
Done -> a
|
||||||
|
"#
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue