Merge pull request #5406 from JTeeuwissen/cross-layout-reuse

Cross layout reuse
This commit is contained in:
Folkert de Vries 2023-05-15 15:20:23 +02:00 committed by GitHub
commit f2a033d0da
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 1075 additions and 270 deletions

View file

@ -1184,9 +1184,24 @@ pub fn build_exp_expr<'a, 'ctx>(
};
let ctx = env.context;
let check_if_null = ctx.append_basic_block(parent, "check_if_null");
let check_if_unique = ctx.append_basic_block(parent, "check_if_unique");
let cont_block = ctx.append_basic_block(parent, "cont");
env.builder.build_unconditional_branch(check_if_null);
env.builder.position_at_end(check_if_null);
env.builder.build_conditional_branch(
env.builder.build_is_null(tag_ptr, "is_tag_null"),
cont_block,
check_if_unique,
);
env.builder.position_at_end(check_if_unique);
let then_block = ctx.append_basic_block(parent, "then_reset");
let else_block = ctx.append_basic_block(parent, "else_decref");
let cont_block = ctx.append_basic_block(parent, "cont");
let refcount_ptr =
PointerToRefcount::from_ptr_to_data(env, tag_pointer_clear_tag_id(env, tag_ptr));
@ -1227,7 +1242,11 @@ pub fn build_exp_expr<'a, 'ctx>(
let phi = env.builder.build_phi(tag_ptr.get_type(), "branch");
let null_ptr = tag_ptr.get_type().const_null();
phi.add_incoming(&[(&tag_ptr, then_block), (&null_ptr, else_block)]);
phi.add_incoming(&[
(&null_ptr, check_if_null),
(&tag_ptr, then_block),
(&null_ptr, else_block),
]);
phi.as_basic_value()
}
@ -1246,9 +1265,24 @@ pub fn build_exp_expr<'a, 'ctx>(
let tag_ptr = tag_ptr.into_pointer_value();
let ctx = env.context;
let not_unique_block = ctx.append_basic_block(parent, "else_decref");
let check_if_null = ctx.append_basic_block(parent, "check_if_null");
let check_if_unique = ctx.append_basic_block(parent, "check_if_unique");
let cont_block = ctx.append_basic_block(parent, "cont");
env.builder.build_unconditional_branch(check_if_null);
env.builder.position_at_end(check_if_null);
env.builder.build_conditional_branch(
env.builder.build_is_null(tag_ptr, "is_tag_null"),
cont_block,
check_if_unique,
);
env.builder.position_at_end(check_if_unique);
let not_unique_block = ctx.append_basic_block(parent, "else_decref");
let refcount_ptr =
PointerToRefcount::from_ptr_to_data(env, tag_pointer_clear_tag_id(env, tag_ptr));
@ -1274,7 +1308,11 @@ pub fn build_exp_expr<'a, 'ctx>(
let phi = env.builder.build_phi(tag_ptr.get_type(), "branch");
let null_ptr = tag_ptr.get_type().const_null();
phi.add_incoming(&[(&tag_ptr, parent_block), (&null_ptr, not_unique_block)]);
phi.add_incoming(&[
(&null_ptr, check_if_null),
(&tag_ptr, parent_block),
(&null_ptr, not_unique_block),
]);
phi.as_basic_value()
}

View file

@ -1256,7 +1256,20 @@ pub(crate) fn run_low_level<'a, 'ctx>(
unreachable!("The {:?} operation is turned into mono Expr", op)
}
PtrCast | PtrWrite | RefCountIncRcPtr | RefCountDecRcPtr | RefCountIncDataPtr
PtrCast => {
arguments!(data_ptr);
let target_type =
basic_type_from_layout(env, layout_interner, layout).into_pointer_type();
debug_assert!(data_ptr.is_pointer_value());
env.builder
.build_pointer_cast(data_ptr.into_pointer_value(), target_type, "ptr_cast")
.into()
}
PtrWrite | RefCountIncRcPtr | RefCountDecRcPtr | RefCountIncDataPtr
| RefCountDecDataPtr => {
unreachable!("Not used in LLVM backend: {:?}", op);
}

View file

@ -3110,17 +3110,6 @@ fn update<'a>(
debug_print_ir!(state, &layout_interner, ROC_PRINT_IR_AFTER_REFCOUNT);
reset_reuse::insert_reset_reuse_operations(
arena,
&layout_interner,
module_id,
ident_ids,
&mut update_mode_ids,
&mut state.procedures,
);
debug_print_ir!(state, &layout_interner, ROC_PRINT_IR_AFTER_RESET_REUSE);
drop_specialization::specialize_drops(
arena,
&mut layout_interner,
@ -3136,6 +3125,18 @@ fn update<'a>(
ROC_PRINT_IR_AFTER_DROP_SPECIALIZATION
);
reset_reuse::insert_reset_reuse_operations(
arena,
&layout_interner,
module_id,
ident_ids,
state.target_info,
&mut update_mode_ids,
&mut state.procedures,
);
debug_print_ir!(state, &layout_interner, ROC_PRINT_IR_AFTER_RESET_REUSE);
// This is not safe with the new non-recursive RC updates that we do for tag unions
//
// Proc::optimize_refcount_operations(

View file

@ -500,13 +500,12 @@ impl<'a> CodeGenHelp<'a> {
niche: Niche::NONE,
}
}
HelperOp::Reset => ProcLayout {
HelperOp::Reset | HelperOp::ResetRef => ProcLayout {
arguments: self.arena.alloc([layout]),
result: layout,
niche: Niche::NONE,
},
HelperOp::DecRef(_) => unreachable!("No generated Proc for DecRef"),
HelperOp::ResetRef => unreachable!("No generated Proc for ResetRef"),
HelperOp::Eq => ProcLayout {
arguments: self.arena.alloc([layout, layout]),
result: LAYOUT_BOOL,

View file

@ -485,7 +485,7 @@ pub fn refcount_resetref_proc_body<'a>(
let union_layout = match layout_interner.get(layout).repr {
LayoutRepr::Union(u) => u,
_ => unimplemented!("Reset is only implemented for UnionLayout"),
_ => unimplemented!("Resetref is only implemented for UnionLayout"),
};
// Whenever we recurse into a child layout we will want to Decrement
@ -498,29 +498,33 @@ pub fn refcount_resetref_proc_body<'a>(
// Reset structure is not unique. Decrement it and return a NULL pointer.
let else_stmt = {
let decrement_unit = root.create_symbol(ident_ids, "decrement_unit");
let decrement_expr = root
.call_specialized_op(
ident_ids,
ctx,
layout_interner,
layout,
root.arena.alloc([structure]),
)
.unwrap();
let decrement_stmt = |next| Stmt::Let(decrement_unit, decrement_expr, LAYOUT_UNIT, next);
// Set up the context for a decref.
let jp_decref = JoinPointId(root.create_symbol(ident_ids, "jp_decref"));
ctx.op = HelperOp::DecRef(jp_decref);
// Generate the decref code.
let rc_stmt = refcount_generic(root, ident_ids, ctx, layout_interner, layout, structure);
// Null pointer with union layout
let null = root.create_symbol(ident_ids, "null");
let null_stmt = |next| Stmt::Let(null, Expr::NullPointer, layout, next);
decrement_stmt(root.arena.alloc(
//
null_stmt(root.arena.alloc(
// Inline the refcounting code instead of making a function. Don't iterate fields,
// and replace any return statements with jumps to the `following` statement.
let join = Stmt::Join {
id: jp_decref,
parameters: &[],
body: root.arena.alloc(root.arena.alloc(
//
Stmt::Ret(null),
null_stmt(root.arena.alloc(
//
Stmt::Ret(null),
)),
)),
))
remainder: root.arena.alloc(rc_stmt),
};
root.arena.alloc(join)
};
let if_stmt = Stmt::if_then_else(

View file

@ -124,17 +124,11 @@ fn specialize_drops_stmt<'a, 'i>(
}
}
}
Expr::Struct(_) => {
let mut new_environment = environment.clone_without_incremented();
alloc_let_with_continuation!(&mut new_environment)
}
Expr::Tag { tag_id, .. } => {
let mut new_environment = environment.clone_without_incremented();
environment.symbol_tag.insert(*binding, *tag_id);
new_environment.symbol_tag.insert(*binding, *tag_id);
alloc_let_with_continuation!(&mut new_environment)
alloc_let_with_continuation!(environment)
}
Expr::StructAtIndex {
index, structure, ..
@ -185,8 +179,8 @@ fn specialize_drops_stmt<'a, 'i>(
}
alloc_let_with_continuation!(environment)
}
Expr::RuntimeErrorFunction(_)
Expr::Struct(_)
| Expr::RuntimeErrorFunction(_)
| Expr::ExprBox { .. }
| Expr::NullPointer
| Expr::GetTagId { .. }

View file

@ -648,20 +648,20 @@ fn insert_refcount_operations_stmt<'v, 'a>(
} => {
let new_remainder = insert_refcount_operations_stmt(arena, environment, remainder);
let new_expect = arena.alloc(Stmt::Expect {
let newer_remainder = consume_and_insert_dec_stmts(
arena,
environment,
environment.borrowed_usages(lookups.iter().copied()),
new_remainder,
);
arena.alloc(Stmt::Expect {
condition: *condition,
region: *region,
lookups,
variables,
remainder: new_remainder,
});
consume_and_insert_inc_stmts(
arena,
environment,
environment.owned_usages(lookups.iter().copied()),
new_expect,
)
remainder: newer_remainder,
})
}
Stmt::ExpectFx {
condition,
@ -672,20 +672,20 @@ fn insert_refcount_operations_stmt<'v, 'a>(
} => {
let new_remainder = insert_refcount_operations_stmt(arena, environment, remainder);
let new_expectfx = arena.alloc(Stmt::ExpectFx {
let newer_remainder = consume_and_insert_dec_stmts(
arena,
environment,
environment.borrowed_usages(lookups.iter().copied()),
new_remainder,
);
arena.alloc(Stmt::ExpectFx {
condition: *condition,
region: *region,
lookups,
variables,
remainder: new_remainder,
});
consume_and_insert_inc_stmts(
arena,
environment,
environment.owned_usages(lookups.iter().copied()),
new_expectfx,
)
remainder: newer_remainder,
})
}
Stmt::Dbg {
symbol,
@ -694,20 +694,11 @@ fn insert_refcount_operations_stmt<'v, 'a>(
} => {
let new_remainder = insert_refcount_operations_stmt(arena, environment, remainder);
let new_debug = arena.alloc(Stmt::Dbg {
arena.alloc(Stmt::Dbg {
symbol: *symbol,
variable: *variable,
remainder: new_remainder,
});
// TODO this assumes the debug statement to consume the variable. I'm not sure if that is (always) the case.
// But the old inc_dec pass passes variables
consume_and_insert_inc_stmts(
arena,
environment,
environment.owned_usages([*symbol]),
new_debug,
)
})
}
Stmt::Join {
id: joinpoint_id,

View file

@ -5,6 +5,8 @@
// Implementation based of Reference Counting with Frame Limited Reuse
// https://www.microsoft.com/en-us/research/uploads/prod/2021/11/flreuse-tr.pdf
use std::hash::Hash;
use crate::borrow::Ownership;
use crate::ir::{
BranchInfo, Expr, JoinPointId, ModifyRc, Param, Proc, ProcLayout, Stmt, UpdateModeId,
@ -18,6 +20,7 @@ use bumpalo::collections::vec::Vec;
use bumpalo::collections::CollectIn;
use roc_collections::{MutMap, MutSet};
use roc_module::symbol::{IdentIds, ModuleId, Symbol};
use roc_target::TargetInfo;
/**
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>,
home: ModuleId,
ident_ids: &'i mut IdentIds,
target_info: TargetInfo,
update_mode_ids: &'i mut UpdateModeIds,
procs: &mut MutMap<(Symbol, ProcLayout<'a>), Proc<'a>>,
) {
@ -42,6 +46,7 @@ pub fn insert_reset_reuse_operations<'a, 'i>(
layout_interner,
home,
ident_ids,
target_info,
update_mode_ids,
global_layouts.clone(),
proc.clone(),
@ -55,6 +60,7 @@ fn insert_reset_reuse_operations_proc<'a, 'i>(
layout_interner: &'i STLayoutInterner<'a>,
home: ModuleId,
ident_ids: &'i mut IdentIds,
target_info: TargetInfo,
update_mode_ids: &'i mut UpdateModeIds,
mut symbol_layout: SymbolLayout<'a>,
mut proc: Proc<'a>,
@ -76,6 +82,7 @@ fn insert_reset_reuse_operations_proc<'a, 'i>(
layout_interner,
home,
ident_ids,
target_info,
update_mode_ids,
&mut env,
arena.alloc(proc.body),
@ -93,6 +100,7 @@ fn insert_reset_reuse_operations_stmt<'a, 'i>(
layout_interner: &'i STLayoutInterner<'a>,
home: ModuleId,
ident_ids: &'i mut IdentIds,
target_info: TargetInfo,
update_mode_ids: &'i mut UpdateModeIds,
environment: &mut ReuseEnvironment<'a>,
stmt: &'a Stmt<'a>,
@ -131,33 +139,70 @@ fn insert_reset_reuse_operations_stmt<'a, 'i>(
environment.add_symbol_tag(*binding, *tag_id);
// 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.
Reuse::Reusable => {
Reuse::Reusable(union_layout) => {
// See if we have a token.
match environment.pop_reuse_token(layout) {
match environment.pop_reuse_token(&get_reuse_layout_info(
layout_interner,
target_info,
union_layout,
)) {
// We have a reuse token for this layout, use it.
Some(reuse_token) => {
Expr::Reuse {
symbol: reuse_token.symbol,
update_mode: reuse_token.update_mode_id,
// for now, always overwrite the tag ID just to be sure
update_tag_id: true,
tag_layout: *tag_layout,
tag_id: *tag_id,
arguments,
Some(TokenWithInLayout {
token: reuse_token,
inlayout: layout_info,
}) => {
// The reuse token layout is the same, we can use it without casting.
if layout_info == layout {
(
None,
Expr::Reuse {
symbol: reuse_token.symbol,
update_mode: reuse_token.update_mode_id,
// for now, always overwrite the tag ID just to be sure
update_tag_id: true,
tag_layout: *tag_layout,
tag_id: *tag_id,
arguments,
},
)
}
// The reuse token has a different layout from the tag, we need to pointercast it before.
else {
let new_symbol =
Symbol::new(home, ident_ids.gen_unique());
(
Some(move |new_let| {
arena.alloc(Stmt::Let(
new_symbol,
create_ptr_cast(arena, reuse_token.symbol),
*layout,
new_let,
))
}),
Expr::Reuse {
symbol: new_symbol,
update_mode: reuse_token.update_mode_id,
// for now, always overwrite the tag ID just to be sure
update_tag_id: true,
tag_layout: *tag_layout,
tag_id: *tag_id,
arguments,
},
)
}
}
// 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.
Reuse::Nonreusable => expr.clone(),
Reuse::Nonreusable => (None, expr.clone()),
}
}
_ => expr.clone(),
_ => (None, expr.clone()),
};
environment.add_symbol_layout(*binding, layout);
@ -169,6 +214,7 @@ fn insert_reset_reuse_operations_stmt<'a, 'i>(
layout_interner,
home,
ident_ids,
target_info,
update_mode_ids,
environment,
current_stmt,
@ -176,8 +222,14 @@ fn insert_reset_reuse_operations_stmt<'a, 'i>(
new_triplets.into_iter().rev().fold(
new_continuation,
|new_continuation, (binding, new_expr, layout)| {
arena.alloc(Stmt::Let(*binding, new_expr, *layout, new_continuation))
|new_continuation, (binding, (reused, new_expr), layout)| {
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(wrap) => wrap(new_let),
None => new_let,
}
},
)
}
@ -206,6 +258,7 @@ fn insert_reset_reuse_operations_stmt<'a, 'i>(
layout_interner,
home,
ident_ids,
target_info,
update_mode_ids,
&mut branch_env,
branch,
@ -233,6 +286,7 @@ fn insert_reset_reuse_operations_stmt<'a, 'i>(
layout_interner,
home,
ident_ids,
target_info,
update_mode_ids,
&mut branch_env,
branch,
@ -277,37 +331,37 @@ fn insert_reset_reuse_operations_stmt<'a, 'i>(
// Then we drop any unused reuse tokens in branches where the minimum is not reached.
let msg =
"All layouts in the environment should be in the layout_min_reuse_tokens map.";
let newer_branches =
Vec::from_iter_in(
new_branches
.iter()
.map(|(label, info, branch, branch_env)| {
let unused_tokens = branch_env.reuse_tokens.iter().flat_map(
|(layout, reuse_tokens)| {
let min_reuse_tokens =
layout_min_reuse_tokens.get(layout).expect(msg);
&reuse_tokens[*min_reuse_tokens..]
},
);
let newer_branches = Vec::from_iter_in(
new_branches
.iter()
.map(|(label, info, branch, branch_env)| {
let unused_tokens = branch_env
.reuse_tokens
.iter()
.flat_map(|(layout, reuse_tokens)| {
let min_reuse_tokens =
layout_min_reuse_tokens.get(layout).expect(msg);
&reuse_tokens[*min_reuse_tokens..]
})
.map(|token_with_layout| token_with_layout.token);
let newer_branch =
drop_unused_reuse_tokens(arena, unused_tokens.copied(), branch);
let newer_branch = drop_unused_reuse_tokens(arena, unused_tokens, branch);
(*label, info.clone(), newer_branch.clone())
}),
arena,
)
.into_bump_slice();
(*label, info.clone(), newer_branch.clone())
}),
arena,
)
.into_bump_slice();
let newer_default_branch = {
// let (info, branch, branch_env) = new_default_branch;
let unused_tokens= new_default_branch.2.reuse_tokens.iter().flat_map(|(layout, reuse_tokens) |{
let min_reuse_tokens = layout_min_reuse_tokens.get(layout).expect("All layouts in the environment should be in the layout_min_reuse_tokens map.");
&reuse_tokens[*min_reuse_tokens..]
});
}).map(|token_with_layout|{token_with_layout.token});
let newer_branch =
drop_unused_reuse_tokens(arena, unused_tokens.copied(), new_default_branch.1);
drop_unused_reuse_tokens(arena, unused_tokens, new_default_branch.1);
(new_default_branch.0, newer_branch)
};
@ -353,30 +407,39 @@ 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.
match layout_option.clone() {
LayoutOption::Layout(layout)
if matches!(
symbol_layout_reusability(
layout_interner,
environment,
symbol,
layout
),
Reuse::Reusable
) =>
{
let reuse_token = ReuseToken {
symbol: Symbol::new(home, ident_ids.gen_unique()),
update_mode_id: update_mode_ids.next_id(),
};
LayoutOption::Layout(layout) => {
match symbol_layout_reusability(
layout_interner,
environment,
symbol,
layout,
) {
Reuse::Reusable(union_layout) => {
let reuse_token = ReuseToken {
symbol: Symbol::new(home, ident_ids.gen_unique()),
update_mode_id: update_mode_ids.next_id(),
};
let dec_ref = match rc {
ModifyRc::Dec(_) => false,
ModifyRc::DecRef(_) => true,
_ => unreachable!(),
};
let dec_ref = match rc {
ModifyRc::Dec(_) => false,
ModifyRc::DecRef(_) => true,
_ => unreachable!(),
};
environment.push_reuse_token(arena, layout, reuse_token);
Some((layout, *symbol, reuse_token, dec_ref))
environment.push_reuse_token(
arena,
get_reuse_layout_info(
layout_interner,
target_info,
union_layout,
),
reuse_token,
layout,
);
Some((layout, union_layout, *symbol, reuse_token, dec_ref))
}
Reuse::Nonreusable => None,
}
}
_ => None,
}
@ -388,20 +451,29 @@ fn insert_reset_reuse_operations_stmt<'a, 'i>(
layout_interner,
home,
ident_ids,
target_info,
update_mode_ids,
environment,
continuation,
);
// 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 {
let stack_reuse_token = environment.peek_reuse_token(layout);
if let Some((layout, union_layout, symbol, reuse_token, dec_ref)) = reuse_pair {
let stack_reuse_token = environment.peek_reuse_token(&get_reuse_layout_info(
layout_interner,
target_info,
union_layout,
));
match stack_reuse_token {
Some(reuse_symbol) if reuse_symbol == reuse_token => {
Some(token_with_layout) if token_with_layout.token == reuse_token => {
// 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.
let _ = environment.pop_reuse_token(layout);
let _ = environment.pop_reuse_token(&get_reuse_layout_info(
layout_interner,
target_info,
union_layout,
));
}
_ => {
// The token we inserted is no longer on the stack, it must have been consumed.
@ -453,6 +525,7 @@ fn insert_reset_reuse_operations_stmt<'a, 'i>(
layout_interner,
home,
ident_ids,
target_info,
update_mode_ids,
environment,
remainder,
@ -478,6 +551,7 @@ fn insert_reset_reuse_operations_stmt<'a, 'i>(
layout_interner,
home,
ident_ids,
target_info,
update_mode_ids,
environment,
remainder,
@ -501,6 +575,7 @@ fn insert_reset_reuse_operations_stmt<'a, 'i>(
layout_interner,
home,
ident_ids,
target_info,
update_mode_ids,
environment,
remainder,
@ -536,6 +611,7 @@ fn insert_reset_reuse_operations_stmt<'a, 'i>(
layout_interner,
home,
ident_ids,
target_info,
update_mode_ids,
&mut first_pass_environment,
remainder,
@ -549,24 +625,21 @@ fn insert_reset_reuse_operations_stmt<'a, 'i>(
let max_reuse_tokens =
match first_pass_remainder_environment.get_jump_reuse_tokens(*joinpoint_id) {
Some(all_reuse_maps) => {
let all_reuse_layouts = all_reuse_maps
let all_token_layouts = all_reuse_maps
.iter()
.flat_map(|reuse_map| reuse_map.keys())
// PERF: replace this collect with an unique iterator. To make sure every layout is only used once.
.collect::<MutSet<_>>()
.into_iter();
let reuse_layouts_max_tokens = all_reuse_layouts.map(|reuse_layout| {
let max_token = all_reuse_maps
let reuse_layouts_max_tokens = all_token_layouts.map(|token_layout| {
// We get the tokens from the jump with the most tokens for this token layout.
// So we have an inlayout for each token. And can cast when needed.
let max_token_inlayouts = all_reuse_maps
.iter()
.map(|reuse_map| {
reuse_map
.get(reuse_layout)
.map(|tokens| tokens.len())
.unwrap_or(0)
})
.max()
.filter_map(|reuse_map| reuse_map.get(token_layout))
.max_by_key(|tokens| tokens.len())
.expect("all layouts should be in at least one of the reuse maps");
(reuse_layout, max_token)
(token_layout, max_token_inlayouts)
});
Vec::from_iter_in(reuse_layouts_max_tokens, arena)
}
@ -579,13 +652,17 @@ fn insert_reset_reuse_operations_stmt<'a, 'i>(
// For each possibly available reuse token, create a reuse token to add to the join point environment.
let max_reuse_token_symbols = max_reuse_tokens
.iter()
.map(|(layout, size)| {
.copied()
.map(|(token_layout, tokens)| {
(
**layout,
*token_layout,
Vec::from_iter_in(
(0..*size).map(|_| ReuseToken {
symbol: Symbol::new(home, ident_ids.gen_unique()),
update_mode_id: update_mode_ids.next_id(),
tokens.iter().map(|token| TokenWithInLayout {
token: ReuseToken {
symbol: Symbol::new(home, ident_ids.gen_unique()),
update_mode_id: update_mode_ids.next_id(),
},
inlayout: token.inlayout,
}),
arena,
),
@ -616,6 +693,7 @@ fn insert_reset_reuse_operations_stmt<'a, 'i>(
layout_interner,
home,
ident_ids,
target_info,
update_mode_ids,
&mut first_pass_body_environment,
body,
@ -684,9 +762,9 @@ fn insert_reset_reuse_operations_stmt<'a, 'i>(
let layouts_for_reuse = {
let mut layouts = Vec::from_iter_in(
used_reuse_tokens
.iter()
.flat_map(|(layout, tokens)| tokens.iter().map(|_| *layout)),
used_reuse_tokens.iter().flat_map(|(layout, tokens)| {
tokens.iter().map(|token| (token.inlayout, *layout))
}),
arena,
);
// Make sure the layouts are sorted, so that we can provide them from the jump.
@ -706,6 +784,7 @@ fn insert_reset_reuse_operations_stmt<'a, 'i>(
layout_interner,
home,
ident_ids,
target_info,
update_mode_ids,
environment,
remainder,
@ -733,10 +812,10 @@ fn insert_reset_reuse_operations_stmt<'a, 'i>(
let token_params =
layouts_for_reuse_with_token
.into_iter()
.map(|(layout, token)| Param {
symbol: token.symbol,
.map(|(_reuse_layout, token)| Param {
symbol: token.token.symbol,
ownership: Ownership::Owned,
layout,
layout: *token.inlayout,
});
// Add the void tokens to the jump arguments to match the expected arguments of the join point.
@ -792,6 +871,7 @@ fn insert_reset_reuse_operations_stmt<'a, 'i>(
layout_interner,
home,
ident_ids,
target_info,
update_mode_ids,
&mut body_environment,
body,
@ -829,36 +909,64 @@ fn insert_reset_reuse_operations_stmt<'a, 'i>(
let token_layouts_clone = token_layouts.clone();
let mut reuse_tokens_to_cast = Vec::new_in(arena);
let mut void_pointer_layout_symbols = Vec::new_in(arena);
// See what tokens we can get from the env, if none are available, use a void pointer.
let tokens = token_layouts_clone.iter().map(|token_layout| {
environment.pop_reuse_token(token_layout).map_or_else(
|| match void_pointer_layout_symbols
.iter()
.find(|(layout, _)| layout == token_layout)
{
Some(existing_symbol) => existing_symbol.1,
None => {
let new_symbol = Symbol::new(home, ident_ids.gen_unique());
void_pointer_layout_symbols.push((*token_layout, new_symbol));
new_symbol
let tokens = token_layouts_clone
.iter()
.map(|(param_layout, token_layout)| {
match environment.pop_reuse_token(token_layout) {
Some(reuse_token) => {
if reuse_token.inlayout != *param_layout {
let new_symbol = Symbol::new(home, ident_ids.gen_unique());
reuse_tokens_to_cast.push((
*param_layout,
reuse_token.token.symbol,
new_symbol,
));
new_symbol
} else {
reuse_token.token.symbol
}
}
},
|reuse_token| reuse_token.symbol,
)
});
None => match void_pointer_layout_symbols
.iter()
.find(|(layout, _)| layout == param_layout)
{
Some(existing_symbol) => existing_symbol.1,
None => {
let new_symbol = Symbol::new(home, ident_ids.gen_unique());
void_pointer_layout_symbols
.push((*param_layout, new_symbol));
new_symbol
}
},
}
});
// Add the void tokens to the jump arguments to match the expected arguments of the join point.
let extended_arguments =
Vec::from_iter_in(arguments.iter().copied().chain(tokens), arena)
.into_bump_slice();
let casted_tokens = reuse_tokens_to_cast.into_iter().fold(
arena.alloc(Stmt::Jump(*id, extended_arguments)),
|child, (layout, old_symbol, new_symbol)| {
arena.alloc(Stmt::Let(
new_symbol,
create_ptr_cast(arena, old_symbol),
*layout,
child,
))
},
);
// Wrap the jump in a let statement for each void pointer token layout.
void_pointer_layout_symbols.into_iter().fold(
arena.alloc(Stmt::Jump(*id, extended_arguments)),
|child, (layout, symbol)| {
arena.alloc(Stmt::Let(symbol, Expr::NullPointer, layout, child))
casted_tokens,
|child, (param_layout, symbol)| {
arena.alloc(Stmt::Let(symbol, Expr::NullPointer, *param_layout, child))
},
)
}
@ -870,25 +978,22 @@ fn insert_reset_reuse_operations_stmt<'a, 'i>(
// 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.
let mut void_pointer_layout_symbols: std::vec::Vec<(InLayout, Symbol)> = vec![];
let mut void_pointer_layout_symbols: std::vec::Vec<(&'a InLayout<'a>, Symbol)> =
vec![];
let void_tokens =
token_layouts
let void_tokens = token_layouts.iter().map(|(param_layout, _token_layout)| {
match void_pointer_layout_symbols
.iter()
.map(|token_layout| {
match void_pointer_layout_symbols
.iter()
.find(|(layout, _)| layout == token_layout)
{
Some(existing_symbol) => existing_symbol.1,
None => {
let new_symbol = Symbol::new(home, ident_ids.gen_unique());
void_pointer_layout_symbols
.push((*token_layout, new_symbol));
new_symbol
}
}
});
.find(|(void_layout, _)| void_layout == param_layout)
{
Some(existing_symbol) => existing_symbol.1,
None => {
let new_symbol = Symbol::new(home, ident_ids.gen_unique());
void_pointer_layout_symbols.push((*param_layout, new_symbol));
new_symbol
}
}
});
// Add the void tokens to the jump arguments to match the expected arguments of the join point.
let extended_arguments =
@ -899,7 +1004,7 @@ fn insert_reset_reuse_operations_stmt<'a, 'i>(
void_pointer_layout_symbols.into_iter().fold(
arena.alloc(Stmt::Jump(*id, extended_arguments)),
|child, (layout, symbol)| {
arena.alloc(Stmt::Let(symbol, Expr::NullPointer, layout, child))
arena.alloc(Stmt::Let(symbol, Expr::NullPointer, *layout, child))
},
)
}
@ -909,12 +1014,22 @@ fn insert_reset_reuse_operations_stmt<'a, 'i>(
}
}
fn create_ptr_cast(arena: &Bump, symbol: Symbol) -> Expr {
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([symbol], arena).into_bump_slice(),
})
}
// 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.
enum Reuse {
enum Reuse<'a> {
// Reuseable but the pointer *might* be null, which will cause a fresh allocation.
Reusable,
Reusable(UnionLayout<'a>),
Nonreusable,
}
@ -922,7 +1037,17 @@ enum Reuse {
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.
*/
type ReuseTokens<'a> = MutMap<InLayout<'a>, Vec<'a, ReuseToken>>;
type ReuseTokens<'a> = MutMap<TokenLayout, Vec<'a, TokenWithInLayout<'a>>>;
/**
Struct to to check whether two reuse layouts are interchangeable.
*/
#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
struct TokenLayout {
has_tag: bool,
size: u32,
alignment: u32,
}
/**
A reuse token is a symbol that is used to reset a layout.
@ -937,6 +1062,15 @@ struct ReuseToken {
update_mode_id: UpdateModeId,
}
/**
Combines a reuse token with it's possible layout info.
*/
#[derive(Clone, Copy, PartialEq, Eq)]
struct TokenWithInLayout<'a> {
token: ReuseToken,
inlayout: &'a InLayout<'a>,
}
type Tag = u16;
/**
@ -962,7 +1096,7 @@ enum JoinPointReuseTokens<'a> {
BodyFirst,
// 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, (&'a InLayout<'a>, TokenLayout)>),
// 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.
@ -970,7 +1104,7 @@ enum JoinPointReuseTokens<'a> {
// 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.
RemainderSecond(Vec<'a, InLayout<'a>>),
RemainderSecond(Vec<'a, (&'a InLayout<'a>, TokenLayout)>),
}
#[derive(Default, Clone)]
@ -1003,13 +1137,13 @@ impl<'a> ReuseEnvironment<'a> {
/**
Retrieve a reuse token for a layout from the stack for said layout.
*/
fn pop_reuse_token(&mut self, layout: &InLayout<'a>) -> Option<ReuseToken> {
let reuse_tokens = self.reuse_tokens.get_mut(layout)?;
fn pop_reuse_token(&mut self, token_layout: &TokenLayout) -> Option<TokenWithInLayout> {
let reuse_tokens = self.reuse_tokens.get_mut(token_layout)?;
// If the layout is in the map, pop the token from the stack.
let reuse_token = reuse_tokens.pop();
// If the stack is empty, remove the layout from the map.
if reuse_tokens.is_empty() {
self.reuse_tokens.remove(layout);
self.reuse_tokens.remove(token_layout);
}
reuse_token
}
@ -1018,8 +1152,8 @@ impl<'a> ReuseEnvironment<'a> {
Retrieve a reuse token for a layout from the stack for said layout.
Without consuming the token.
*/
fn peek_reuse_token(&mut self, layout: &InLayout<'a>) -> Option<ReuseToken> {
let reuse_tokens = self.reuse_tokens.get(layout)?;
fn peek_reuse_token(&mut self, token_layout: &TokenLayout) -> Option<TokenWithInLayout> {
let reuse_tokens = self.reuse_tokens.get(token_layout)?;
// If the layout is in the map, peek at the last element.
let reuse_token = reuse_tokens.last();
reuse_token.copied()
@ -1028,16 +1162,26 @@ impl<'a> ReuseEnvironment<'a> {
/**
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) {
match self.reuse_tokens.get_mut(layout) {
fn push_reuse_token(
&mut self,
arena: &'a Bump,
token_layout: TokenLayout,
reuse_token: ReuseToken,
layout: &'a InLayout<'a>,
) {
let with_info = TokenWithInLayout {
token: reuse_token,
inlayout: layout,
};
match self.reuse_tokens.get_mut(&token_layout) {
Some(reuse_tokens) => {
// If the layout is already in the map, push the token on the stack.
reuse_tokens.push(token);
reuse_tokens.push(with_info);
}
None => {
// If the layout is not in the map, create a new stack with the token.
self.reuse_tokens
.insert(*layout, Vec::from_iter_in([token], arena));
.insert(token_layout, Vec::from_iter_in([with_info], arena));
}
};
}
@ -1132,10 +1276,10 @@ fn symbol_layout_reusability<'a>(
environment: &ReuseEnvironment<'a>,
symbol: &Symbol,
layout: &InLayout<'a>,
) -> Reuse {
) -> Reuse<'a> {
match layout_interner.get(*layout).repr {
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.
// Arrays are probably given to functions and reused there. Little use to reuse them here.
@ -1146,13 +1290,13 @@ fn symbol_layout_reusability<'a>(
/**
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(union_layout: UnionLayout, tag_id_option: Option<Tag>) -> Reuse {
match union_layout {
UnionLayout::NonRecursive(_) => Reuse::Nonreusable,
// Non nullable union layouts
UnionLayout::Recursive(_) | UnionLayout::NonNullableUnwrapped(_) => {
// Non nullable union layouts can always be reused.
Reuse::Reusable
Reuse::Reusable(union_layout)
}
// Nullable union layouts
UnionLayout::NullableWrapped { .. } | UnionLayout::NullableUnwrapped { .. } => {
@ -1163,13 +1307,13 @@ fn can_reuse_union_layout_tag(union_layout: &UnionLayout<'_>, tag_id_option: Opt
Reuse::Nonreusable
} else {
// Symbol of layout is not null, so it can be reused.
Reuse::Reusable
Reuse::Reusable(union_layout)
}
}
None => {
// Symbol of layout might be null, so it might be reused.
// If null will cause null pointer and fresh allocation.
Reuse::Reusable
Reuse::Reusable(union_layout)
}
}
}
@ -1193,3 +1337,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>,
) -> TokenLayout {
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,
};
TokenLayout {
has_tag,
size,
alignment,
}
}

View file

@ -0,0 +1,71 @@
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;
let Test.33 : [<rnu><null>, C *self *self] = UnionAtIndex (Id 0) (Index 1) Test.29;
joinpoint #Derived_gen.0 #Derived_gen.5:
let #Derived_gen.6 : [<rnu>C [<rnu><null>, C *self *self] *self, <null>] = lowlevel PtrCast #Derived_gen.5;
let Test.43 : [<rnu>C [<rnu><null>, C *self *self] *self, <null>] = Reuse #Derived_gen.6 UpdateModeId { id: 2 } 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;
in
let #Derived_gen.1 : Int1 = lowlevel RefCountIsUnique Test.29;
if #Derived_gen.1 then
let #Derived_gen.7 : [<rnu><null>, C *self *self] = ResetRef { symbol: Test.29, id: UpdateModeId { id: 3 } };
jump #Derived_gen.0 #Derived_gen.7;
else
inc Test.32;
inc Test.33;
let #Derived_gen.8 : [<rnu><null>, C *self *self] = ResetRef { symbol: Test.29, id: UpdateModeId { id: 4 } };
jump #Derived_gen.0 #Derived_gen.8;
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.2 : Int1 = lowlevel RefCountIsUnique Test.30;
if #Derived_gen.2 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;

View file

@ -78,7 +78,7 @@ procedure Test.2 (Test.6):
let Test.31 : Int1 = lowlevel Eq Test.29 Test.30;
if Test.31 then
let Test.7 : [<r>C List *self, C *self] = UnionAtIndex (Id 1) (Index 0) Test.6;
joinpoint #Derived_gen.2:
joinpoint #Derived_gen.0:
let Test.8 : Str = CallByName Test.2 Test.7;
let Test.18 : Int1 = CallByName Bool.1;
if Test.18 then
@ -88,17 +88,17 @@ procedure Test.2 (Test.6):
let Test.17 : Str = "foo";
ret Test.17;
in
let #Derived_gen.3 : Int1 = lowlevel RefCountIsUnique Test.6;
if #Derived_gen.3 then
let #Derived_gen.1 : Int1 = lowlevel RefCountIsUnique Test.6;
if #Derived_gen.1 then
decref Test.6;
jump #Derived_gen.2;
jump #Derived_gen.0;
else
inc Test.7;
decref Test.6;
jump #Derived_gen.2;
jump #Derived_gen.0;
else
let Test.9 : List [<r>C List *self, C *self] = UnionAtIndex (Id 0) (Index 0) Test.6;
joinpoint #Derived_gen.4:
joinpoint #Derived_gen.2:
let Test.24 : {} = Struct {};
let Test.23 : List Str = CallByName List.5 Test.9 Test.24;
let Test.21 : [C {}, C Str] = CallByName List.9 Test.23;
@ -106,14 +106,14 @@ procedure Test.2 (Test.6):
let Test.20 : Str = CallByName Result.5 Test.21 Test.22;
ret Test.20;
in
let #Derived_gen.5 : Int1 = lowlevel RefCountIsUnique Test.6;
if #Derived_gen.5 then
let #Derived_gen.3 : Int1 = lowlevel RefCountIsUnique Test.6;
if #Derived_gen.3 then
decref Test.6;
jump #Derived_gen.4;
jump #Derived_gen.2;
else
inc Test.9;
decref Test.6;
jump #Derived_gen.4;
jump #Derived_gen.2;
procedure Test.0 ():
let Test.32 : List [<r>C List *self, C *self] = Array [];

View file

@ -58,7 +58,7 @@ procedure Test.11 (Test.53, Test.54):
joinpoint Test.27 Test.12 #Attr.12:
let Test.8 : Int1 = UnionAtIndex (Id 2) (Index 1) #Attr.12;
let Test.7 : [<rnw><null>, C *self Int1, C *self Int1] = UnionAtIndex (Id 2) (Index 0) #Attr.12;
joinpoint #Derived_gen.3:
joinpoint #Derived_gen.0:
joinpoint Test.31 Test.29:
let Test.30 : U8 = GetTagId Test.7;
switch Test.30:
@ -85,14 +85,14 @@ procedure Test.11 (Test.53, Test.54):
jump Test.31 Test.32;
in
let #Derived_gen.4 : Int1 = lowlevel RefCountIsUnique #Attr.12;
if #Derived_gen.4 then
let #Derived_gen.1 : Int1 = lowlevel RefCountIsUnique #Attr.12;
if #Derived_gen.1 then
decref #Attr.12;
jump #Derived_gen.3;
jump #Derived_gen.0;
else
inc Test.7;
decref #Attr.12;
jump #Derived_gen.3;
jump #Derived_gen.0;
in
jump Test.27 Test.53 Test.54;
@ -125,7 +125,7 @@ procedure Test.6 (Test.7, Test.8, Test.5):
procedure Test.9 (Test.10, #Attr.12):
let Test.8 : Int1 = UnionAtIndex (Id 1) (Index 1) #Attr.12;
let Test.7 : [<rnw><null>, C *self Int1, C *self Int1] = UnionAtIndex (Id 1) (Index 0) #Attr.12;
joinpoint #Derived_gen.5:
joinpoint #Derived_gen.2:
let Test.37 : U8 = GetTagId Test.7;
joinpoint Test.38 Test.36:
switch Test.8:
@ -153,14 +153,14 @@ procedure Test.9 (Test.10, #Attr.12):
jump Test.38 Test.39;
in
let #Derived_gen.6 : Int1 = lowlevel RefCountIsUnique #Attr.12;
if #Derived_gen.6 then
let #Derived_gen.3 : Int1 = lowlevel RefCountIsUnique #Attr.12;
if #Derived_gen.3 then
decref #Attr.12;
jump #Derived_gen.5;
jump #Derived_gen.2;
else
inc Test.7;
decref #Attr.12;
jump #Derived_gen.5;
jump #Derived_gen.2;
procedure Test.0 ():
let Test.41 : Int1 = false;

View file

@ -13,8 +13,8 @@ procedure Test.2 (Test.19):
ret Test.14;
else
let Test.5 : [<rnu><null>, C *self] = UnionAtIndex (Id 0) (Index 0) Test.7;
let #Derived_gen.2 : Int1 = lowlevel RefCountIsUnique Test.7;
if #Derived_gen.2 then
let #Derived_gen.0 : Int1 = lowlevel RefCountIsUnique Test.7;
if #Derived_gen.0 then
decref Test.7;
jump Test.13 Test.5;
else

View file

@ -363,5 +363,7 @@ procedure Test.3 ():
let Test.5 : Int1 = CallByName Bool.11 Test.1 Test.6;
dec Test.6;
expect Test.5;
dec Test.0;
dec Test.1;
let Test.4 : {} = Struct {};
ret Test.4;

View file

@ -101,54 +101,47 @@ procedure Test.1 (Test.77):
let Test.68 : Int1 = lowlevel Eq Test.66 Test.67;
if Test.68 then
let Test.57 : [<r>C I64, C List *self] = StructAtIndex 0 Test.6;
inc Test.57;
let Test.58 : U8 = 0i64;
let Test.59 : U8 = GetTagId Test.57;
let #Derived_gen.1 : [<r>C I64, C List *self] = Reset { symbol: Test.57, id: UpdateModeId { id: 1 } };
let Test.60 : Int1 = lowlevel Eq Test.58 Test.59;
if Test.60 then
decref #Derived_gen.1;
let Test.50 : [<r>C I64, C List *self] = StructAtIndex 0 Test.6;
let Test.8 : I64 = UnionAtIndex (Id 0) (Index 0) Test.50;
let Test.49 : [<r>C I64, C List *self] = StructAtIndex 1 Test.6;
dec Test.50;
let Test.10 : I64 = UnionAtIndex (Id 0) (Index 0) Test.49;
joinpoint #Derived_gen.7:
joinpoint #Derived_gen.0:
let Test.27 : Int1 = CallByName Num.22 Test.8 Test.10;
ret Test.27;
in
let #Derived_gen.8 : Int1 = lowlevel RefCountIsUnique Test.49;
if #Derived_gen.8 then
let #Derived_gen.1 : Int1 = lowlevel RefCountIsUnique Test.49;
if #Derived_gen.1 then
decref Test.49;
jump #Derived_gen.7;
jump #Derived_gen.0;
else
decref Test.49;
jump #Derived_gen.7;
jump #Derived_gen.0;
else
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.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 Test.40 : [<r>C I64, C List *self] = TagId(1) Test.41;
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;
else
let Test.61 : [<r>C I64, C List *self] = StructAtIndex 0 Test.6;
inc Test.61;
let Test.62 : U8 = 1i64;
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 Test.64 : Int1 = lowlevel Eq Test.62 Test.63;
if Test.64 then
decref #Derived_gen.4;
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;
inc Test.12;
inc 2 Test.12;
let Test.51 : [<r>C I64, C List *self] = StructAtIndex 1 Test.6;
dec Test.52;
let Test.14 : List [<r>C I64, C List *self] = UnionAtIndex (Id 1) (Index 0) Test.51;
joinpoint #Derived_gen.9:
joinpoint #Derived_gen.2:
let Test.35 : {} = Struct {};
inc Test.12;
inc Test.14;
let Test.33 : List {[<r>C I64, C List *self], [<r>C I64, C List *self]} = CallByName List.23 Test.12 Test.14 Test.35;
let Test.34 : {} = Struct {};
@ -166,18 +159,18 @@ procedure Test.1 (Test.77):
let Test.28 : Int1 = CallByName Bool.1;
ret Test.28;
in
let #Derived_gen.10 : Int1 = lowlevel RefCountIsUnique Test.51;
if #Derived_gen.10 then
let #Derived_gen.3 : Int1 = lowlevel RefCountIsUnique Test.51;
if #Derived_gen.3 then
decref Test.51;
jump #Derived_gen.9;
jump #Derived_gen.2;
else
inc Test.14;
decref Test.51;
jump #Derived_gen.9;
jump #Derived_gen.2;
else
let Test.48 : [<r>C I64, C List *self] = StructAtIndex 0 Test.6;
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 Test.45 : [<r>C I64, C List *self] = TagId(1) Test.47;
let Test.46 : [<r>C I64, C List *self] = StructAtIndex 1 Test.6;
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;

View file

@ -393,5 +393,6 @@ procedure Test.12 ():
let Test.14 : Int1 = CallByName Bool.11 Test.10 Test.15;
dec Test.15;
expect Test.14;
dec Test.10;
let Test.13 : {} = Struct {};
ret Test.13;

View file

@ -8,7 +8,7 @@ procedure Test.0 ():
let Test.18 : Int1 = lowlevel Eq Test.16 Test.17;
if Test.18 then
let Test.12 : [<rnu><null>, C *self] = UnionAtIndex (Id 0) (Index 0) Test.2;
joinpoint #Derived_gen.2:
joinpoint #Derived_gen.0:
let Test.13 : U8 = 0i64;
let Test.14 : U8 = GetTagId Test.12;
dec Test.12;
@ -20,14 +20,14 @@ procedure Test.0 ():
let Test.9 : I64 = 0i64;
ret Test.9;
in
let #Derived_gen.3 : Int1 = lowlevel RefCountIsUnique Test.2;
if #Derived_gen.3 then
let #Derived_gen.1 : Int1 = lowlevel RefCountIsUnique Test.2;
if #Derived_gen.1 then
decref Test.2;
jump #Derived_gen.2;
jump #Derived_gen.0;
else
inc Test.12;
decref Test.2;
jump #Derived_gen.2;
jump #Derived_gen.0;
else
let Test.10 : I64 = 0i64;
ret Test.10;

View file

@ -0,0 +1,447 @@
procedure Num.22 (#Attr.2, #Attr.3):
let Num.284 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;
ret Num.284;
procedure Num.24 (#Attr.2, #Attr.3):
let Num.282 : Int1 = lowlevel NumGt #Attr.2 #Attr.3;
ret Num.282;
procedure Test.3 (Test.9, Test.10, Test.11):
let Test.254 : U8 = 0i64;
let Test.255 : U8 = GetTagId Test.9;
let Test.256 : Int1 = lowlevel Eq Test.254 Test.255;
if Test.256 then
let Test.113 : [<rnu>C *self I64 *self I32 Int1, <null>] = TagId(0) ;
let Test.114 : [<rnu>C *self I64 *self I32 Int1, <null>] = TagId(0) ;
let Test.112 : Int1 = true;
let Test.111 : [<rnu>C *self I64 *self I32 Int1, <null>] = TagId(1) Test.113 Test.11 Test.114 Test.10 Test.112;
ret Test.111;
else
let Test.251 : Int1 = UnionAtIndex (Id 1) (Index 4) Test.9;
let Test.252 : Int1 = false;
let Test.253 : Int1 = lowlevel Eq Test.252 Test.251;
if Test.253 then
let Test.16 : [<rnu>C *self I64 *self I32 Int1, <null>] = UnionAtIndex (Id 1) (Index 0) Test.9;
let Test.18 : I64 = UnionAtIndex (Id 1) (Index 1) Test.9;
let Test.19 : [<rnu>C *self I64 *self I32 Int1, <null>] = UnionAtIndex (Id 1) (Index 2) Test.9;
let Test.17 : I32 = UnionAtIndex (Id 1) (Index 3) Test.9;
joinpoint #Derived_gen.16 #Derived_gen.22:
let Test.179 : Int1 = CallByName Num.22 Test.10 Test.17;
if Test.179 then
joinpoint Test.238 #Derived_gen.96:
let Test.233 : [<rnu>C *self I64 *self I32 Int1, <null>] = CallByName Test.3 Test.16 Test.10 Test.11;
let Test.232 : Int1 = false;
let Test.231 : [<rnu>C *self I64 *self I32 Int1, <null>] = Reuse #Derived_gen.96 UpdateModeId { id: 71 } TagId(1) Test.233 Test.18 Test.19 Test.17 Test.232;
ret Test.231;
in
let Test.236 : U8 = 1i64;
let Test.237 : U8 = GetTagId Test.16;
let Test.240 : Int1 = lowlevel Eq Test.236 Test.237;
if Test.240 then
let Test.234 : Int1 = UnionAtIndex (Id 1) (Index 4) Test.16;
let Test.235 : Int1 = true;
let Test.239 : Int1 = lowlevel Eq Test.235 Test.234;
if Test.239 then
let Test.180 : [<rnu>C *self I64 *self I32 Int1, <null>] = CallByName Test.3 Test.16 Test.10 Test.11;
joinpoint Test.199 #Derived_gen.128:
let Test.198 : [<rnu>C *self I64 *self I32 Int1, <null>] = UnionAtIndex (Id 1) (Index 0) Test.180;
let Test.20 : [<rnu>C *self I64 *self I32 Int1, <null>] = UnionAtIndex (Id 1) (Index 0) Test.198;
inc Test.20;
let Test.22 : I64 = UnionAtIndex (Id 1) (Index 1) Test.198;
let Test.23 : [<rnu>C *self I64 *self I32 Int1, <null>] = UnionAtIndex (Id 1) (Index 2) Test.198;
inc Test.23;
let Test.21 : I32 = UnionAtIndex (Id 1) (Index 3) Test.198;
let Test.25 : I64 = UnionAtIndex (Id 1) (Index 1) Test.180;
let Test.26 : [<rnu>C *self I64 *self I32 Int1, <null>] = UnionAtIndex (Id 1) (Index 2) Test.180;
let Test.24 : I32 = UnionAtIndex (Id 1) (Index 3) Test.180;
joinpoint #Derived_gen.0 #Derived_gen.132 #Derived_gen.133 #Derived_gen.134:
let Test.186 : Int1 = false;
let Test.183 : [<rnu>C *self I64 *self I32 Int1, <null>] = Reuse #Derived_gen.134 UpdateModeId { id: 107 } TagId(1) Test.20 Test.22 Test.23 Test.21 Test.186;
let Test.185 : Int1 = false;
let Test.184 : [<rnu>C *self I64 *self I32 Int1, <null>] = Reuse #Derived_gen.133 UpdateModeId { id: 106 } TagId(1) Test.26 Test.18 Test.19 Test.17 Test.185;
let Test.182 : Int1 = true;
let Test.181 : [<rnu>C *self I64 *self I32 Int1, <null>] = Reuse #Derived_gen.132 UpdateModeId { id: 105 } TagId(1) Test.183 Test.25 Test.184 Test.24 Test.182;
ret Test.181;
in
let #Derived_gen.1 : Int1 = lowlevel RefCountIsUnique Test.180;
if #Derived_gen.1 then
let #Derived_gen.135 : [<rnu>C *self I64 *self I32 Int1, <null>] = Reset { symbol: Test.198, id: UpdateModeId { id: 108 } };
let #Derived_gen.136 : [<rnu>C *self I64 *self I32 Int1, <null>] = ResetRef { symbol: Test.180, id: UpdateModeId { id: 109 } };
jump #Derived_gen.0 #Derived_gen.136 #Derived_gen.135 #Derived_gen.128;
else
inc Test.26;
let #Derived_gen.137 : [<rnu>C *self I64 *self I32 Int1, <null>] = ResetRef { symbol: Test.180, id: UpdateModeId { id: 110 } };
let #Derived_gen.138 : [<rnu>C *self I64 *self I32 Int1, <null>] = NullPointer;
jump #Derived_gen.0 #Derived_gen.137 #Derived_gen.128 #Derived_gen.138;
in
let Test.228 : U8 = 1i64;
let Test.229 : U8 = GetTagId Test.180;
let Test.230 : Int1 = lowlevel Eq Test.228 Test.229;
if Test.230 then
joinpoint Test.225 #Derived_gen.150:
joinpoint Test.216 #Derived_gen.151:
let Test.46 : [<rnu>C *self I64 *self I32 Int1, <null>] = UnionAtIndex (Id 1) (Index 0) Test.180;
let Test.48 : I64 = UnionAtIndex (Id 1) (Index 1) Test.180;
let Test.49 : [<rnu>C *self I64 *self I32 Int1, <null>] = UnionAtIndex (Id 1) (Index 2) Test.180;
let Test.47 : I32 = UnionAtIndex (Id 1) (Index 3) Test.180;
joinpoint #Derived_gen.2 #Derived_gen.154 #Derived_gen.155:
let Test.196 : Int1 = true;
let Test.195 : [<rnu>C *self I64 *self I32 Int1, <null>] = Reuse #Derived_gen.155 UpdateModeId { id: 126 } TagId(1) Test.46 Test.48 Test.49 Test.47 Test.196;
let Test.194 : Int1 = false;
let Test.193 : [<rnu>C *self I64 *self I32 Int1, <null>] = Reuse #Derived_gen.154 UpdateModeId { id: 125 } TagId(1) Test.195 Test.18 Test.19 Test.17 Test.194;
ret Test.193;
in
let #Derived_gen.3 : Int1 = lowlevel RefCountIsUnique Test.180;
if #Derived_gen.3 then
let #Derived_gen.156 : [<rnu>C *self I64 *self I32 Int1, <null>] = ResetRef { symbol: Test.180, id: UpdateModeId { id: 127 } };
jump #Derived_gen.2 #Derived_gen.156 #Derived_gen.151;
else
inc Test.46;
inc Test.49;
let #Derived_gen.157 : [<rnu>C *self I64 *self I32 Int1, <null>] = ResetRef { symbol: Test.180, id: UpdateModeId { id: 128 } };
jump #Derived_gen.2 #Derived_gen.157 #Derived_gen.151;
in
let Test.213 : [<rnu>C *self I64 *self I32 Int1, <null>] = UnionAtIndex (Id 1) (Index 0) Test.180;
let Test.214 : U8 = 1i64;
let Test.215 : U8 = GetTagId Test.213;
let Test.218 : Int1 = lowlevel Eq Test.214 Test.215;
if Test.218 then
let Test.210 : [<rnu>C *self I64 *self I32 Int1, <null>] = UnionAtIndex (Id 1) (Index 0) Test.180;
let Test.211 : Int1 = UnionAtIndex (Id 1) (Index 4) Test.210;
let Test.212 : Int1 = true;
let Test.217 : Int1 = lowlevel Eq Test.212 Test.211;
if Test.217 then
jump Test.199 #Derived_gen.150;
else
jump Test.216 #Derived_gen.150;
else
jump Test.216 #Derived_gen.150;
in
let Test.222 : [<rnu>C *self I64 *self I32 Int1, <null>] = UnionAtIndex (Id 1) (Index 2) Test.180;
let Test.223 : U8 = 1i64;
let Test.224 : U8 = GetTagId Test.222;
let Test.227 : Int1 = lowlevel Eq Test.223 Test.224;
if Test.227 then
let Test.219 : [<rnu>C *self I64 *self I32 Int1, <null>] = UnionAtIndex (Id 1) (Index 2) Test.180;
let Test.220 : Int1 = UnionAtIndex (Id 1) (Index 4) Test.219;
let Test.221 : Int1 = true;
let Test.226 : Int1 = lowlevel Eq Test.221 Test.220;
if Test.226 then
joinpoint Test.207 #Derived_gen.158:
let Test.33 : [<rnu>C *self I64 *self I32 Int1, <null>] = UnionAtIndex (Id 1) (Index 0) Test.180;
let Test.35 : I64 = UnionAtIndex (Id 1) (Index 1) Test.180;
let Test.200 : [<rnu>C *self I64 *self I32 Int1, <null>] = UnionAtIndex (Id 1) (Index 2) Test.180;
let Test.36 : [<rnu>C *self I64 *self I32 Int1, <null>] = UnionAtIndex (Id 1) (Index 0) Test.200;
inc Test.36;
let Test.38 : I64 = UnionAtIndex (Id 1) (Index 1) Test.200;
let Test.39 : [<rnu>C *self I64 *self I32 Int1, <null>] = UnionAtIndex (Id 1) (Index 2) Test.200;
inc Test.39;
let Test.37 : I32 = UnionAtIndex (Id 1) (Index 3) Test.200;
let Test.34 : I32 = UnionAtIndex (Id 1) (Index 3) Test.180;
joinpoint #Derived_gen.4 #Derived_gen.162 #Derived_gen.163 #Derived_gen.164:
let Test.192 : Int1 = false;
let Test.189 : [<rnu>C *self I64 *self I32 Int1, <null>] = Reuse #Derived_gen.164 UpdateModeId { id: 135 } TagId(1) Test.33 Test.35 Test.36 Test.34 Test.192;
let Test.191 : Int1 = false;
let Test.190 : [<rnu>C *self I64 *self I32 Int1, <null>] = Reuse #Derived_gen.163 UpdateModeId { id: 134 } TagId(1) Test.39 Test.18 Test.19 Test.17 Test.191;
let Test.188 : Int1 = true;
let Test.187 : [<rnu>C *self I64 *self I32 Int1, <null>] = Reuse #Derived_gen.162 UpdateModeId { id: 133 } TagId(1) Test.189 Test.38 Test.190 Test.37 Test.188;
ret Test.187;
in
let #Derived_gen.5 : Int1 = lowlevel RefCountIsUnique Test.180;
if #Derived_gen.5 then
let #Derived_gen.165 : [<rnu>C *self I64 *self I32 Int1, <null>] = Reset { symbol: Test.200, id: UpdateModeId { id: 136 } };
let #Derived_gen.166 : [<rnu>C *self I64 *self I32 Int1, <null>] = ResetRef { symbol: Test.180, id: UpdateModeId { id: 137 } };
jump #Derived_gen.4 #Derived_gen.166 #Derived_gen.165 #Derived_gen.158;
else
inc Test.33;
let #Derived_gen.167 : [<rnu>C *self I64 *self I32 Int1, <null>] = ResetRef { symbol: Test.180, id: UpdateModeId { id: 138 } };
let #Derived_gen.168 : [<rnu>C *self I64 *self I32 Int1, <null>] = NullPointer;
jump #Derived_gen.4 #Derived_gen.167 #Derived_gen.158 #Derived_gen.168;
in
let Test.204 : [<rnu>C *self I64 *self I32 Int1, <null>] = UnionAtIndex (Id 1) (Index 0) Test.180;
let Test.205 : U8 = 1i64;
let Test.206 : U8 = GetTagId Test.204;
let Test.209 : Int1 = lowlevel Eq Test.205 Test.206;
if Test.209 then
let Test.201 : [<rnu>C *self I64 *self I32 Int1, <null>] = UnionAtIndex (Id 1) (Index 0) Test.180;
let Test.202 : Int1 = UnionAtIndex (Id 1) (Index 4) Test.201;
let Test.203 : Int1 = true;
let Test.208 : Int1 = lowlevel Eq Test.203 Test.202;
if Test.208 then
jump Test.199 #Derived_gen.22;
else
jump Test.207 #Derived_gen.22;
else
jump Test.207 #Derived_gen.22;
else
jump Test.225 #Derived_gen.22;
else
jump Test.225 #Derived_gen.22;
else
decref #Derived_gen.22;
dec Test.19;
let Test.197 : [<rnu>C *self I64 *self I32 Int1, <null>] = TagId(0) ;
ret Test.197;
else
jump Test.238 #Derived_gen.22;
else
jump Test.238 #Derived_gen.22;
else
let Test.117 : Int1 = CallByName Num.24 Test.10 Test.17;
if Test.117 then
joinpoint Test.176 #Derived_gen.271:
let Test.171 : [<rnu>C *self I64 *self I32 Int1, <null>] = CallByName Test.3 Test.19 Test.10 Test.11;
let Test.170 : Int1 = false;
let Test.169 : [<rnu>C *self I64 *self I32 Int1, <null>] = Reuse #Derived_gen.271 UpdateModeId { id: 241 } TagId(1) Test.16 Test.18 Test.171 Test.17 Test.170;
ret Test.169;
in
let Test.174 : U8 = 1i64;
let Test.175 : U8 = GetTagId Test.19;
let Test.178 : Int1 = lowlevel Eq Test.174 Test.175;
if Test.178 then
let Test.172 : Int1 = UnionAtIndex (Id 1) (Index 4) Test.19;
let Test.173 : Int1 = true;
let Test.177 : Int1 = lowlevel Eq Test.173 Test.172;
if Test.177 then
let #Derived_gen.272 : [<rnu>C *self I64 *self I32 Int1, <null>] = Reset { symbol: Test.16, id: UpdateModeId { id: 242 } };
inc Test.19;
let Test.118 : [<rnu>C *self I64 *self I32 Int1, <null>] = CallByName Test.3 Test.19 Test.10 Test.11;
joinpoint Test.137 #Derived_gen.317 #Derived_gen.318:
let Test.136 : [<rnu>C *self I64 *self I32 Int1, <null>] = UnionAtIndex (Id 1) (Index 0) Test.118;
let Test.57 : [<rnu>C *self I64 *self I32 Int1, <null>] = UnionAtIndex (Id 1) (Index 0) Test.136;
inc Test.57;
let Test.59 : I64 = UnionAtIndex (Id 1) (Index 1) Test.136;
let Test.60 : [<rnu>C *self I64 *self I32 Int1, <null>] = UnionAtIndex (Id 1) (Index 2) Test.136;
inc Test.60;
let Test.58 : I32 = UnionAtIndex (Id 1) (Index 3) Test.136;
let Test.62 : I64 = UnionAtIndex (Id 1) (Index 1) Test.118;
let Test.63 : [<rnu>C *self I64 *self I32 Int1, <null>] = UnionAtIndex (Id 1) (Index 2) Test.118;
let Test.61 : I32 = UnionAtIndex (Id 1) (Index 3) Test.118;
joinpoint #Derived_gen.6 #Derived_gen.323 #Derived_gen.324 #Derived_gen.325:
let Test.124 : Int1 = false;
let Test.121 : [<rnu>C *self I64 *self I32 Int1, <null>] = Reuse #Derived_gen.325 UpdateModeId { id: 295 } TagId(1) Test.57 Test.59 Test.60 Test.58 Test.124;
let Test.123 : Int1 = false;
let Test.122 : [<rnu>C *self I64 *self I32 Int1, <null>] = Reuse #Derived_gen.324 UpdateModeId { id: 294 } TagId(1) Test.63 Test.18 Test.19 Test.17 Test.123;
let Test.120 : Int1 = true;
let Test.119 : [<rnu>C *self I64 *self I32 Int1, <null>] = Reuse #Derived_gen.323 UpdateModeId { id: 293 } TagId(1) Test.121 Test.62 Test.122 Test.61 Test.120;
ret Test.119;
in
let #Derived_gen.7 : Int1 = lowlevel RefCountIsUnique Test.118;
if #Derived_gen.7 then
decref #Derived_gen.317;
let #Derived_gen.326 : [<rnu>C *self I64 *self I32 Int1, <null>] = Reset { symbol: Test.136, id: UpdateModeId { id: 296 } };
let #Derived_gen.327 : [<rnu>C *self I64 *self I32 Int1, <null>] = ResetRef { symbol: Test.118, id: UpdateModeId { id: 297 } };
jump #Derived_gen.6 #Derived_gen.327 #Derived_gen.326 #Derived_gen.318;
else
inc Test.63;
let #Derived_gen.328 : [<rnu>C *self I64 *self I32 Int1, <null>] = ResetRef { symbol: Test.118, id: UpdateModeId { id: 298 } };
jump #Derived_gen.6 #Derived_gen.328 #Derived_gen.318 #Derived_gen.317;
in
let Test.166 : U8 = 1i64;
let Test.167 : U8 = GetTagId Test.118;
let Test.168 : Int1 = lowlevel Eq Test.166 Test.167;
if Test.168 then
joinpoint Test.163 #Derived_gen.341 #Derived_gen.342:
joinpoint Test.154 #Derived_gen.344:
let Test.83 : [<rnu>C *self I64 *self I32 Int1, <null>] = UnionAtIndex (Id 1) (Index 0) Test.118;
let Test.85 : I64 = UnionAtIndex (Id 1) (Index 1) Test.118;
let Test.86 : [<rnu>C *self I64 *self I32 Int1, <null>] = UnionAtIndex (Id 1) (Index 2) Test.118;
let Test.84 : I32 = UnionAtIndex (Id 1) (Index 3) Test.118;
joinpoint #Derived_gen.8 #Derived_gen.348 #Derived_gen.349:
let Test.134 : Int1 = true;
let Test.133 : [<rnu>C *self I64 *self I32 Int1, <null>] = Reuse #Derived_gen.349 UpdateModeId { id: 319 } TagId(1) Test.83 Test.85 Test.86 Test.84 Test.134;
let Test.132 : Int1 = false;
let Test.131 : [<rnu>C *self I64 *self I32 Int1, <null>] = Reuse #Derived_gen.348 UpdateModeId { id: 318 } TagId(1) Test.133 Test.18 Test.19 Test.17 Test.132;
ret Test.131;
in
let #Derived_gen.9 : Int1 = lowlevel RefCountIsUnique Test.118;
if #Derived_gen.9 then
let #Derived_gen.350 : [<rnu>C *self I64 *self I32 Int1, <null>] = ResetRef { symbol: Test.118, id: UpdateModeId { id: 320 } };
jump #Derived_gen.8 #Derived_gen.350 #Derived_gen.344;
else
inc Test.83;
inc Test.86;
let #Derived_gen.351 : [<rnu>C *self I64 *self I32 Int1, <null>] = ResetRef { symbol: Test.118, id: UpdateModeId { id: 321 } };
jump #Derived_gen.8 #Derived_gen.351 #Derived_gen.344;
in
let Test.151 : [<rnu>C *self I64 *self I32 Int1, <null>] = UnionAtIndex (Id 1) (Index 0) Test.118;
let Test.152 : U8 = 1i64;
let Test.153 : U8 = GetTagId Test.151;
let Test.156 : Int1 = lowlevel Eq Test.152 Test.153;
if Test.156 then
let Test.148 : [<rnu>C *self I64 *self I32 Int1, <null>] = UnionAtIndex (Id 1) (Index 0) Test.118;
let Test.149 : Int1 = UnionAtIndex (Id 1) (Index 4) Test.148;
let Test.150 : Int1 = true;
let Test.155 : Int1 = lowlevel Eq Test.150 Test.149;
if Test.155 then
jump Test.137 #Derived_gen.342 #Derived_gen.341;
else
decref #Derived_gen.341;
jump Test.154 #Derived_gen.342;
else
decref #Derived_gen.341;
jump Test.154 #Derived_gen.342;
in
let Test.160 : [<rnu>C *self I64 *self I32 Int1, <null>] = UnionAtIndex (Id 1) (Index 2) Test.118;
let Test.161 : U8 = 1i64;
let Test.162 : U8 = GetTagId Test.160;
let Test.165 : Int1 = lowlevel Eq Test.161 Test.162;
if Test.165 then
let Test.157 : [<rnu>C *self I64 *self I32 Int1, <null>] = UnionAtIndex (Id 1) (Index 2) Test.118;
let Test.158 : Int1 = UnionAtIndex (Id 1) (Index 4) Test.157;
let Test.159 : Int1 = true;
let Test.164 : Int1 = lowlevel Eq Test.159 Test.158;
if Test.164 then
joinpoint Test.145 #Derived_gen.352 #Derived_gen.353:
let Test.70 : [<rnu>C *self I64 *self I32 Int1, <null>] = UnionAtIndex (Id 1) (Index 0) Test.118;
let Test.72 : I64 = UnionAtIndex (Id 1) (Index 1) Test.118;
let Test.138 : [<rnu>C *self I64 *self I32 Int1, <null>] = UnionAtIndex (Id 1) (Index 2) Test.118;
let Test.73 : [<rnu>C *self I64 *self I32 Int1, <null>] = UnionAtIndex (Id 1) (Index 0) Test.138;
inc Test.73;
let Test.75 : I64 = UnionAtIndex (Id 1) (Index 1) Test.138;
let Test.76 : [<rnu>C *self I64 *self I32 Int1, <null>] = UnionAtIndex (Id 1) (Index 2) Test.138;
inc Test.76;
let Test.74 : I32 = UnionAtIndex (Id 1) (Index 3) Test.138;
let Test.71 : I32 = UnionAtIndex (Id 1) (Index 3) Test.118;
joinpoint #Derived_gen.10 #Derived_gen.358 #Derived_gen.359 #Derived_gen.360:
let Test.130 : Int1 = false;
let Test.127 : [<rnu>C *self I64 *self I32 Int1, <null>] = Reuse #Derived_gen.360 UpdateModeId { id: 330 } TagId(1) Test.70 Test.72 Test.73 Test.71 Test.130;
let Test.129 : Int1 = false;
let Test.128 : [<rnu>C *self I64 *self I32 Int1, <null>] = Reuse #Derived_gen.359 UpdateModeId { id: 329 } TagId(1) Test.76 Test.18 Test.19 Test.17 Test.129;
let Test.126 : Int1 = true;
let Test.125 : [<rnu>C *self I64 *self I32 Int1, <null>] = Reuse #Derived_gen.358 UpdateModeId { id: 328 } TagId(1) Test.127 Test.75 Test.128 Test.74 Test.126;
ret Test.125;
in
let #Derived_gen.11 : Int1 = lowlevel RefCountIsUnique Test.118;
if #Derived_gen.11 then
decref #Derived_gen.352;
let #Derived_gen.361 : [<rnu>C *self I64 *self I32 Int1, <null>] = Reset { symbol: Test.138, id: UpdateModeId { id: 331 } };
let #Derived_gen.362 : [<rnu>C *self I64 *self I32 Int1, <null>] = ResetRef { symbol: Test.118, id: UpdateModeId { id: 332 } };
jump #Derived_gen.10 #Derived_gen.362 #Derived_gen.361 #Derived_gen.353;
else
inc Test.70;
let #Derived_gen.363 : [<rnu>C *self I64 *self I32 Int1, <null>] = ResetRef { symbol: Test.118, id: UpdateModeId { id: 333 } };
jump #Derived_gen.10 #Derived_gen.363 #Derived_gen.353 #Derived_gen.352;
in
let Test.142 : [<rnu>C *self I64 *self I32 Int1, <null>] = UnionAtIndex (Id 1) (Index 0) Test.118;
let Test.143 : U8 = 1i64;
let Test.144 : U8 = GetTagId Test.142;
let Test.147 : Int1 = lowlevel Eq Test.143 Test.144;
if Test.147 then
let Test.139 : [<rnu>C *self I64 *self I32 Int1, <null>] = UnionAtIndex (Id 1) (Index 0) Test.118;
let Test.140 : Int1 = UnionAtIndex (Id 1) (Index 4) Test.139;
let Test.141 : Int1 = true;
let Test.146 : Int1 = lowlevel Eq Test.141 Test.140;
if Test.146 then
jump Test.137 #Derived_gen.272 #Derived_gen.22;
else
jump Test.145 #Derived_gen.272 #Derived_gen.22;
else
jump Test.145 #Derived_gen.272 #Derived_gen.22;
else
jump Test.163 #Derived_gen.272 #Derived_gen.22;
else
jump Test.163 #Derived_gen.272 #Derived_gen.22;
else
decref #Derived_gen.272;
decref #Derived_gen.22;
joinpoint #Derived_gen.12:
let Test.135 : [<rnu>C *self I64 *self I32 Int1, <null>] = TagId(0) ;
ret Test.135;
in
let #Derived_gen.15 : Int1 = lowlevel RefCountIsUnique Test.19;
if #Derived_gen.15 then
let #Derived_gen.14 : [<rnu>C *self I64 *self I32 Int1, <null>] = UnionAtIndex (Id 1) (Index 0) Test.19;
dec #Derived_gen.14;
let #Derived_gen.13 : [<rnu>C *self I64 *self I32 Int1, <null>] = UnionAtIndex (Id 1) (Index 2) Test.19;
dec #Derived_gen.13;
decref Test.19;
jump #Derived_gen.12;
else
decref Test.19;
jump #Derived_gen.12;
else
jump Test.176 #Derived_gen.22;
else
jump Test.176 #Derived_gen.22;
else
let Test.116 : Int1 = false;
let Test.115 : [<rnu>C *self I64 *self I32 Int1, <null>] = Reuse #Derived_gen.22 UpdateModeId { id: 2 } TagId(1) Test.16 Test.11 Test.19 Test.10 Test.116;
ret Test.115;
in
let #Derived_gen.17 : Int1 = lowlevel RefCountIsUnique Test.9;
if #Derived_gen.17 then
let #Derived_gen.373 : [<rnu>C *self I64 *self I32 Int1, <null>] = ResetRef { symbol: Test.9, id: UpdateModeId { id: 343 } };
jump #Derived_gen.16 #Derived_gen.373;
else
inc Test.16;
inc Test.19;
let #Derived_gen.374 : [<rnu>C *self I64 *self I32 Int1, <null>] = ResetRef { symbol: Test.9, id: UpdateModeId { id: 344 } };
jump #Derived_gen.16 #Derived_gen.374;
else
let Test.96 : [<rnu>C *self I64 *self I32 Int1, <null>] = UnionAtIndex (Id 1) (Index 0) Test.9;
let Test.98 : I64 = UnionAtIndex (Id 1) (Index 1) Test.9;
let Test.99 : [<rnu>C *self I64 *self I32 Int1, <null>] = UnionAtIndex (Id 1) (Index 2) Test.9;
let Test.97 : I32 = UnionAtIndex (Id 1) (Index 3) Test.9;
joinpoint #Derived_gen.18 #Derived_gen.377:
let Test.247 : Int1 = CallByName Num.22 Test.10 Test.97;
if Test.247 then
let Test.250 : [<rnu>C *self I64 *self I32 Int1, <null>] = CallByName Test.3 Test.96 Test.10 Test.11;
let Test.249 : Int1 = true;
let Test.248 : [<rnu>C *self I64 *self I32 Int1, <null>] = Reuse #Derived_gen.377 UpdateModeId { id: 347 } TagId(1) Test.250 Test.98 Test.99 Test.97 Test.249;
ret Test.248;
else
let Test.243 : Int1 = CallByName Num.24 Test.10 Test.97;
if Test.243 then
let Test.246 : [<rnu>C *self I64 *self I32 Int1, <null>] = CallByName Test.3 Test.99 Test.10 Test.11;
let Test.245 : Int1 = true;
let Test.244 : [<rnu>C *self I64 *self I32 Int1, <null>] = Reuse #Derived_gen.377 UpdateModeId { id: 347 } TagId(1) Test.96 Test.98 Test.246 Test.97 Test.245;
ret Test.244;
else
let Test.242 : Int1 = true;
let Test.241 : [<rnu>C *self I64 *self I32 Int1, <null>] = Reuse #Derived_gen.377 UpdateModeId { id: 347 } TagId(1) Test.96 Test.11 Test.99 Test.10 Test.242;
ret Test.241;
in
let #Derived_gen.19 : Int1 = lowlevel RefCountIsUnique Test.9;
if #Derived_gen.19 then
let #Derived_gen.378 : [<rnu>C *self I64 *self I32 Int1, <null>] = ResetRef { symbol: Test.9, id: UpdateModeId { id: 348 } };
jump #Derived_gen.18 #Derived_gen.378;
else
inc Test.96;
inc Test.99;
let #Derived_gen.379 : [<rnu>C *self I64 *self I32 Int1, <null>] = ResetRef { symbol: Test.9, id: UpdateModeId { id: 349 } };
jump #Derived_gen.18 #Derived_gen.379;
procedure Test.0 ():
let Test.281 : [<rnu>C *self I64 *self I32 Int1, <null>] = TagId(0) ;
let Test.282 : I32 = 0i64;
let Test.283 : I64 = 0i64;
let Test.278 : [<rnu>C *self I64 *self I32 Int1, <null>] = CallByName Test.3 Test.281 Test.282 Test.283;
let Test.279 : I32 = 5i64;
let Test.280 : I64 = 1i64;
let Test.275 : [<rnu>C *self I64 *self I32 Int1, <null>] = CallByName Test.3 Test.278 Test.279 Test.280;
let Test.276 : I32 = 6i64;
let Test.277 : I64 = 2i64;
let Test.272 : [<rnu>C *self I64 *self I32 Int1, <null>] = CallByName Test.3 Test.275 Test.276 Test.277;
let Test.273 : I32 = 4i64;
let Test.274 : I64 = 3i64;
let Test.269 : [<rnu>C *self I64 *self I32 Int1, <null>] = CallByName Test.3 Test.272 Test.273 Test.274;
let Test.270 : I32 = 9i64;
let Test.271 : I64 = 4i64;
let Test.266 : [<rnu>C *self I64 *self I32 Int1, <null>] = CallByName Test.3 Test.269 Test.270 Test.271;
let Test.267 : I32 = 3i64;
let Test.268 : I64 = 5i64;
let Test.263 : [<rnu>C *self I64 *self I32 Int1, <null>] = CallByName Test.3 Test.266 Test.267 Test.268;
let Test.264 : I32 = 2i64;
let Test.265 : I64 = 6i64;
let Test.260 : [<rnu>C *self I64 *self I32 Int1, <null>] = CallByName Test.3 Test.263 Test.264 Test.265;
let Test.261 : I32 = 1i64;
let Test.262 : I64 = 7i64;
let Test.257 : [<rnu>C *self I64 *self I32 Int1, <null>] = CallByName Test.3 Test.260 Test.261 Test.262;
let Test.258 : I32 = 8i64;
let Test.259 : I64 = 8i64;
let Test.107 : [<rnu>C *self I64 *self I32 Int1, <null>] = CallByName Test.3 Test.257 Test.258 Test.259;
let Test.108 : I32 = 7i64;
let Test.109 : I64 = 9i64;
let Test.106 : [<rnu>C *self I64 *self I32 Int1, <null>] = CallByName Test.3 Test.107 Test.108 Test.109;
ret Test.106;

View file

@ -39,7 +39,7 @@ procedure Test.4 (Test.28, Test.29):
joinpoint Test.15 Test.5 #Attr.12:
let Test.2 : U8 = UnionAtIndex (Id 0) (Index 1) #Attr.12;
let Test.3 : [<rnu><null>, C *self U8] = UnionAtIndex (Id 0) (Index 0) #Attr.12;
joinpoint #Derived_gen.1:
joinpoint #Derived_gen.0:
let Test.17 : U8 = CallByName Num.21 Test.2 Test.5;
let Test.18 : U8 = GetTagId Test.3;
switch Test.18:
@ -52,14 +52,14 @@ procedure Test.4 (Test.28, Test.29):
ret Test.16;
in
let #Derived_gen.2 : Int1 = lowlevel RefCountIsUnique #Attr.12;
if #Derived_gen.2 then
let #Derived_gen.1 : Int1 = lowlevel RefCountIsUnique #Attr.12;
if #Derived_gen.1 then
decref #Attr.12;
jump #Derived_gen.1;
jump #Derived_gen.0;
else
inc Test.3;
decref #Attr.12;
jump #Derived_gen.1;
jump #Derived_gen.0;
in
jump Test.15 Test.28 Test.29;

View file

@ -2923,3 +2923,90 @@ 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
"#
)
}
#[mono_test]
fn rb_tree_fbip() {
indoc!(
r#"
app "test" provides [main] to "./platform"
main = Leaf
|> ins 0 0
|> ins 5 1
|> ins 6 2
|> ins 4 3
|> ins 9 4
|> ins 3 5
|> ins 2 6
|> ins 1 7
|> ins 8 8
|> ins 7 9
Color : [Red, Black]
Tree a : [Node Color (Tree a) I32 a (Tree a), Leaf]
ins : Tree a, I32, a -> Tree a
ins = \t, k, v -> when t is
Leaf -> Node Red Leaf k v Leaf
Node Black l kx vx r ->
if k < kx
then when l is
Node Red _ _ _ _ -> when (ins l k v) is
Node _ (Node Red ly ky vy ry) kz vz rz -> Node Red (Node Black ly ky vy ry) kz vz (Node Black rz kx vx r)
Node _ lz kz vz (Node Red ly ky vy ry) -> Node Red (Node Black lz kz vz ly) ky vy (Node Black ry kx vx r)
Node _ ly ky vy ry -> Node Black (Node Red ly ky vy ry) kx vx r
Leaf -> Leaf
_ -> Node Black (ins l k v) kx vx r
else
if k > kx
then when r is
Node Red _ _ _ _ -> when ins r k v is
Node _ (Node Red ly ky vy ry) kz vz rz -> Node Red (Node Black ly ky vy ry) kz vz (Node Black rz kx vx r)
Node _ lz kz vz (Node Red ly ky vy ry) -> Node Red (Node Black lz kz vz ly) ky vy (Node Black ry kx vx r)
Node _ ly ky vy ry -> Node Black (Node Red ly ky vy ry) kx vx r
Leaf -> Leaf
_ -> Node Black l kx vx (ins r k v)
else Node Black l k v r
Node Red l kx vx r ->
if k < kx
then Node Red (ins l k v) kx vx r
else
if k > kx
then Node Red l kx vx (ins r k v)
else Node Red l k v r
"#
)
}