Skip to content

Commit

Permalink
Create bitmask
Browse files Browse the repository at this point in the history
  • Loading branch information
JTeeuwissen committed Jul 30, 2023
1 parent cb22c3e commit 302b65a
Show file tree
Hide file tree
Showing 10 changed files with 162 additions and 54 deletions.
4 changes: 2 additions & 2 deletions crates/compiler/gen_dev/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ impl<'a> LastSeenMap<'a> {
arguments, reuse, ..
} => {
if let Some(ru) = reuse {
self.set_last_seen(ru.symbol, stmt);
self.set_last_seen(ru.0.symbol, stmt);
}

for sym in *arguments {
Expand Down Expand Up @@ -846,7 +846,7 @@ trait Backend<'a> {
reuse,
} => {
self.load_literal_symbols(arguments);
let reuse = reuse.map(|ru| ru.symbol);
let reuse = reuse.map(|ru| ru.0.symbol);
self.tag(sym, arguments, tag_layout, *tag_id, reuse);
}
Expr::NullPointer => {
Expand Down
2 changes: 1 addition & 1 deletion crates/compiler/gen_llvm/src/llvm/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1092,7 +1092,7 @@ pub(crate) fn build_exp_expr<'a, 'ctx>(
tag_id,
reuse,
} => {
let reuse_ptr = reuse.map(|ru| scope.load_symbol(&ru.symbol).into_pointer_value());
let reuse_ptr = reuse.map(|ru| scope.load_symbol(&ru.0.symbol).into_pointer_value());

build_tag(
env,
Expand Down
2 changes: 1 addition & 1 deletion crates/compiler/gen_wasm/src/backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1106,7 +1106,7 @@ impl<'a, 'r> WasmBackend<'a, 'r> {
arguments,
reuse,
} => {
let reuse = reuse.map(|ru| ru.symbol);
let reuse = reuse.map(|ru| ru.0.symbol);
self.expr_tag(union_layout, *tag_id, arguments, sym, storage, reuse)
}

Expand Down
2 changes: 1 addition & 1 deletion crates/compiler/mono/src/debug/checker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -427,7 +427,7 @@ impl<'a, 'r> Ctx<'a, 'r> {
.interner
.insert_direct_no_semantic(LayoutRepr::Union(tag_layout));

if let Some(reuse_token) = reuse {
if let Some((reuse_token, _)) = reuse {
self.check_sym_layout(reuse_token.symbol, interned_layout, UseKind::TagReuse);
}

Expand Down
18 changes: 13 additions & 5 deletions crates/compiler/mono/src/ir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1848,6 +1848,7 @@ pub struct ReuseToken {
pub symbol: Symbol,
pub update_tag_id: bool,
pub update_mode: UpdateModeId,
pub original_symbol: Option<Symbol>,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
Expand All @@ -1870,7 +1871,7 @@ pub enum Expr<'a> {
tag_layout: UnionLayout<'a>,
tag_id: TagIdIntType,
arguments: &'a [Symbol],
reuse: Option<ReuseToken>,
reuse: Option<(ReuseToken, u64)>,
},
Struct(&'a [Symbol]),
NullPointer,
Expand Down Expand Up @@ -2037,15 +2038,22 @@ impl<'a> Expr<'a> {
Tag {
tag_id,
arguments,
reuse: Some(reuse_token),
reuse: Some((reuse_token, reuse_specialisation_mask)),
..
} => {
let doc_tag = alloc
.text("TagId(")
.append(alloc.text(tag_id.to_string()))
.append(")");

let it = arguments.iter().map(|s| symbol_to_doc(alloc, *s, pretty));
let it = arguments.iter().enumerate().map(|(index, s)| {
// We mark reused arguments with a star.
if (reuse_specialisation_mask & (1 << index)) != 0 {
symbol_to_doc(alloc, *s, pretty).append("*")
} else {
symbol_to_doc(alloc, *s, pretty)
}
});

alloc
.text("Reuse ")
Expand Down Expand Up @@ -7771,10 +7779,10 @@ fn substitute_in_expr<'a>(
);

let reuse = match *reuse {
Some(mut ru) => match substitute(subs, ru.symbol) {
Some(mut ru) => match substitute(subs, ru.0.symbol) {
Some(s) => {
did_change = true;
ru.symbol = s;
ru.0.symbol = s;
Some(ru)
}
None => Some(ru),
Expand Down
168 changes: 134 additions & 34 deletions crates/compiler/mono/src/reset_reuse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,14 +157,48 @@ fn insert_reset_reuse_operations_stmt<'a, 'i>(
inlayout: layout_info,
}) => {
if layout_info == layout {
let reuse_specialisation_mask = match reuse_token
.original_symbol
.and_then(|original_symbol| {
environment
.get_symbol_tag_child(&original_symbol)
}) {
// The original symbol has the same tag as the new allocation.
// If we have arguments that match the original children, we can skip setting their value.
Some((original_tag, original_children))
if (original_tag == *tag_id) =>
{
arguments.iter().enumerate().fold(
0,
|mask, (index, argument)| {
match original_children
.get(&(index as u64))
{
Some(original_symbol)
if *original_symbol
== *argument =>
{
mask | (1 << index)
}
_ => mask,
}
},
)
}
_ => 0,
};

// The reuse token layout is the same, we can use it without casting.
(
None,
Expr::Tag {
tag_layout: *tag_layout,
tag_id: *tag_id,
arguments,
reuse: Some(reuse_token),
reuse: Some((
reuse_token,
reuse_specialisation_mask,
)),
},
)
} else {
Expand All @@ -190,7 +224,11 @@ fn insert_reset_reuse_operations_stmt<'a, 'i>(
tag_layout: *tag_layout,
tag_id: *tag_id,
arguments,
reuse: Some(reuse_token),
reuse: Some((
reuse_token,
// If the layout is not the same, the fields can't be either.
0,
)),
},
)
}
Expand All @@ -204,6 +242,15 @@ fn insert_reset_reuse_operations_stmt<'a, 'i>(
Reuse::Nonreusable => (None, expr.clone()),
}
}
Expr::UnionAtIndex {
structure,
tag_id,
union_layout: _,
index,
} => {
environment.add_symbol_tag_child(*structure, *tag_id, *index, *binding);
(None, expr.clone())
}
_ => (None, expr.clone()),
};

Expand Down Expand Up @@ -483,6 +530,7 @@ fn insert_reset_reuse_operations_stmt<'a, 'i>(
update_mode: update_mode_ids.next_id(),
// for now, always overwrite the tag ID just to be sure
update_tag_id: true,
original_symbol: Some(symbol),
};

let owned_layout = **layout;
Expand Down Expand Up @@ -706,37 +754,73 @@ fn insert_reset_reuse_operations_stmt<'a, 'i>(
(first_pass_environment, first_pass_remainder)
};

let max_reuse_tokens =
match first_pass_remainder_environment.get_jump_reuse_tokens(*joinpoint_id) {
Some(all_reuse_maps) => {
let all_token_layouts = all_reuse_maps
let max_reuse_tokens = match first_pass_remainder_environment
.get_jump_reuse_tokens(*joinpoint_id)
{
Some(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_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 token_inlayouts = 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_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()
.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");
(token_layout, max_token_inlayouts)
});
Vec::from_iter_in(reuse_layouts_max_tokens, arena)
}
// Normally the remainder should always have jumps and this would not be None,
// But for testing this might not be the case, so default to no available reuse tokens.
None => Vec::new_in(arena),
};
.filter_map(|reuse_map| reuse_map.get(token_layout))
.collect_in::<Vec<_>>(arena);
let max_token_inlayouts = token_inlayouts
.iter()
.max_by_key(|tokens| tokens.len())
.expect("all layouts should be in at least one of the reuse maps");

// We update the known original symbol for each token.
// We move from right to left, as the rightmost tokens are consumed first and matched.
let corrected_max_token_inlayouts = max_token_inlayouts
.iter()
.copied()
.rev()
.enumerate()
.map(|(index, mut token)| {
if let Some(max_original_symbol) = token.token.original_symbol {
if !token_inlayouts.iter().all(|token_inlayout| {
token_inlayout.get(token_inlayout.len() - index).map_or(
// If other jumps don't have a token for this layout, they will pass a null pointer which won't break reuse specialization.
true,
|other_token| {
other_token.token.original_symbol.map_or(
// If other jumps have a token for this layout, their original symbol has to match. If they have no original_symbol, it didn't match before.
false,
|og_symbol| og_symbol == max_original_symbol,
)
},
)
}) {
token.token.original_symbol = None;
}
}
token
});

(
*token_layout,
corrected_max_token_inlayouts.collect_in::<Vec<_>>(arena),
)
});
Vec::from_iter_in(reuse_layouts_max_tokens, arena)
}
// Normally the remainder should always have jumps and this would not be None,
// But for testing this might not be the case, so default to no available reuse tokens.
None => Vec::new_in(arena),
};

let (first_pass_body_environment, first_pass_body, used_reuse_tokens) = {
// 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()
.copied()
.map(|(token_layout, tokens)| {
(
*token_layout,
Expand All @@ -747,6 +831,7 @@ fn insert_reset_reuse_operations_stmt<'a, 'i>(
update_mode: update_mode_ids.next_id(),
// for now, always overwrite the tag ID just to be sure
update_tag_id: true,
original_symbol: token.token.original_symbol,
},
inlayout: token.inlayout,
}),
Expand Down Expand Up @@ -1181,7 +1266,8 @@ enum JoinPointReuseTokens<'a> {
#[derive(Clone)]
struct ReuseEnvironment<'a> {
target_info: TargetInfo,
symbol_tags: MutMap<Symbol, Tag>,
// A map containing known tags for a symbol, and any indexed symbols.
symbol_tags: MutMap<Symbol, (Tag, MutMap<u64, Symbol>)>,
non_unique_symbols: MutSet<Symbol>,
reuse_tokens: ReuseTokens<'a>,
symbol_layouts: SymbolLayout<'a>,
Expand All @@ -1197,14 +1283,27 @@ impl<'a> ReuseEnvironment<'a> {
Used to optimize reuse of unions that are know to have a null pointer.
*/
fn add_symbol_tag(&mut self, symbol: Symbol, tag: Tag) {
self.symbol_tags.insert(symbol, tag);
self.symbol_tags.insert(symbol, (tag, MutMap::default()));
}

/**
Add a known child symbol for a layout.
*/
fn add_symbol_tag_child(&mut self, symbol: Symbol, tag: Tag, index: u64, child: Symbol) {
let current_tag = self
.symbol_tags
.entry(symbol)
.or_insert((tag, MutMap::default()));
current_tag.1.insert(index, child);
}

/**
Retrieve the known tag for a layout.
*/
fn get_symbol_tag(&self, symbol: &Symbol) -> Option<Tag> {
self.symbol_tags.get(symbol).copied()
fn get_symbol_tag_child(&self, symbol: &Symbol) -> Option<(Tag, &MutMap<u64, Symbol>)> {
self.symbol_tags
.get(symbol)
.map(|(tag, children)| (*tag, children))
}

/**
Expand Down Expand Up @@ -1344,9 +1443,10 @@ fn symbol_layout_reusability<'a>(
layout: &InLayout<'a>,
) -> Reuse<'a> {
match layout_interner.get_repr(*layout) {
LayoutRepr::Union(union_layout) => {
can_reuse_union_layout_tag(union_layout, environment.get_symbol_tag(symbol))
}
LayoutRepr::Union(union_layout) => can_reuse_union_layout_tag(
union_layout,
environment.get_symbol_tag_child(symbol).map(|(tag, _)| tag),
),
// Strings literals are constants.
// Arrays are probably given to functions and reused there. Little use to reuse them here.
_ => Reuse::Nonreusable,
Expand Down
2 changes: 1 addition & 1 deletion crates/compiler/mono/src/tail_recursion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1095,7 +1095,7 @@ fn expr_contains_symbol(expr: &Expr, needle: Symbol) -> bool {
arguments, reuse, ..
} => match reuse {
None => arguments.contains(&needle),
Some(ru) => ru.symbol == needle || arguments.contains(&needle),
Some(ru) => ru.0.symbol == needle || arguments.contains(&needle),
},
Expr::Struct(fields) => fields.contains(&needle),
Expr::NullPointer | Expr::FunctionPointer { .. } => false,
Expand Down
2 changes: 1 addition & 1 deletion crates/compiler/test_mono/generated/linked_list_filter.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ procedure Test.2 (#Derived_gen.0, #Derived_gen.1):
let Test.19 : Int1 = CallByName Num.31 Test.7;
if Test.19 then
let #Derived_gen.9 : [<rnu><null>, C I64 *self] = NullPointer;
let Test.20 : [<rnu><null>, C I64 *self] = Reuse #Derived_gen.14 UpdateModeId { id: 1 } TagId(0) Test.7 #Derived_gen.9;
let Test.20 : [<rnu><null>, C I64 *self] = Reuse #Derived_gen.14 UpdateModeId { id: 1 } TagId(0) Test.7* #Derived_gen.9;
let #Derived_gen.10 : Ptr([<rnu><null>, C I64 *self]) = UnionFieldPtrAtIndex (Id 0) (Index 1) Test.20;
let #Derived_gen.11 : {} = lowlevel PtrStore #Derived_gen.5 Test.20;
jump #Derived_gen.4 Test.8 Test.5 #Derived_gen.10 #Derived_gen.6;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ procedure Test.3 (#Derived_gen.0, #Derived_gen.1):
let Test.9 : I64 = UnionAtIndex (Id 0) (Index 0) Test.8;
let Test.10 : [<rnu><null>, C I64 *self] = UnionAtIndex (Id 0) (Index 1) Test.8;
joinpoint #Derived_gen.2 #Derived_gen.4:
let Test.21 : [<rnu><null>, C I64 *self] = Reuse #Derived_gen.4 UpdateModeId { id: 1 } TagId(0) Test.9 Test.7;
let Test.21 : [<rnu><null>, C I64 *self] = Reuse #Derived_gen.4 UpdateModeId { id: 1 } TagId(0) Test.9* Test.7;
jump Test.18 Test.21 Test.10;
in
let #Derived_gen.3 : Int1 = lowlevel RefCountIsUnique Test.8;
Expand Down
Loading

0 comments on commit 302b65a

Please sign in to comment.