From 302b65aa4c2b7d1d1f383f7158953ac1cfcd173f Mon Sep 17 00:00:00 2001 From: "J.Teeuwissen" Date: Sun, 30 Jul 2023 21:03:00 +0200 Subject: [PATCH] Create bitmask --- crates/compiler/gen_dev/src/lib.rs | 4 +- crates/compiler/gen_llvm/src/llvm/build.rs | 2 +- crates/compiler/gen_wasm/src/backend.rs | 2 +- crates/compiler/mono/src/debug/checker.rs | 2 +- crates/compiler/mono/src/ir.rs | 18 +- crates/compiler/mono/src/reset_reuse.rs | 168 ++++++++++++++---- crates/compiler/mono/src/tail_recursion.rs | 2 +- .../generated/linked_list_filter.txt | 2 +- .../generated/linked_list_reverse.txt | 2 +- .../test_mono/generated/rb_tree_fbip.txt | 14 +- 10 files changed, 162 insertions(+), 54 deletions(-) diff --git a/crates/compiler/gen_dev/src/lib.rs b/crates/compiler/gen_dev/src/lib.rs index 7522a70132e..155cde81e42 100644 --- a/crates/compiler/gen_dev/src/lib.rs +++ b/crates/compiler/gen_dev/src/lib.rs @@ -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 { @@ -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 => { diff --git a/crates/compiler/gen_llvm/src/llvm/build.rs b/crates/compiler/gen_llvm/src/llvm/build.rs index b98908371b7..4fcb7414250 100644 --- a/crates/compiler/gen_llvm/src/llvm/build.rs +++ b/crates/compiler/gen_llvm/src/llvm/build.rs @@ -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, diff --git a/crates/compiler/gen_wasm/src/backend.rs b/crates/compiler/gen_wasm/src/backend.rs index 9818d2bc5ee..5b8430d2e32 100644 --- a/crates/compiler/gen_wasm/src/backend.rs +++ b/crates/compiler/gen_wasm/src/backend.rs @@ -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) } diff --git a/crates/compiler/mono/src/debug/checker.rs b/crates/compiler/mono/src/debug/checker.rs index 6509d6bb686..4600accd7c8 100644 --- a/crates/compiler/mono/src/debug/checker.rs +++ b/crates/compiler/mono/src/debug/checker.rs @@ -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); } diff --git a/crates/compiler/mono/src/ir.rs b/crates/compiler/mono/src/ir.rs index ace36dc8405..92dc6832487 100644 --- a/crates/compiler/mono/src/ir.rs +++ b/crates/compiler/mono/src/ir.rs @@ -1848,6 +1848,7 @@ pub struct ReuseToken { pub symbol: Symbol, pub update_tag_id: bool, pub update_mode: UpdateModeId, + pub original_symbol: Option, } #[derive(Clone, Copy, Debug, PartialEq, Eq)] @@ -1870,7 +1871,7 @@ pub enum Expr<'a> { tag_layout: UnionLayout<'a>, tag_id: TagIdIntType, arguments: &'a [Symbol], - reuse: Option, + reuse: Option<(ReuseToken, u64)>, }, Struct(&'a [Symbol]), NullPointer, @@ -2037,7 +2038,7 @@ impl<'a> Expr<'a> { Tag { tag_id, arguments, - reuse: Some(reuse_token), + reuse: Some((reuse_token, reuse_specialisation_mask)), .. } => { let doc_tag = alloc @@ -2045,7 +2046,14 @@ impl<'a> Expr<'a> { .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 ") @@ -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), diff --git a/crates/compiler/mono/src/reset_reuse.rs b/crates/compiler/mono/src/reset_reuse.rs index f68da9279b7..e235ac2218c 100644 --- a/crates/compiler/mono/src/reset_reuse.rs +++ b/crates/compiler/mono/src/reset_reuse.rs @@ -157,6 +157,37 @@ 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, @@ -164,7 +195,10 @@ 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, + reuse_specialisation_mask, + )), }, ) } else { @@ -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, + )), }, ) } @@ -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()), }; @@ -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; @@ -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::>() + .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::>() - .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::>(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::>(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, @@ -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, }), @@ -1181,7 +1266,8 @@ enum JoinPointReuseTokens<'a> { #[derive(Clone)] struct ReuseEnvironment<'a> { target_info: TargetInfo, - symbol_tags: MutMap, + // A map containing known tags for a symbol, and any indexed symbols. + symbol_tags: MutMap)>, non_unique_symbols: MutSet, reuse_tokens: ReuseTokens<'a>, symbol_layouts: SymbolLayout<'a>, @@ -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 { - self.symbol_tags.get(symbol).copied() + fn get_symbol_tag_child(&self, symbol: &Symbol) -> Option<(Tag, &MutMap)> { + self.symbol_tags + .get(symbol) + .map(|(tag, children)| (*tag, children)) } /** @@ -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, diff --git a/crates/compiler/mono/src/tail_recursion.rs b/crates/compiler/mono/src/tail_recursion.rs index 79d9a093d9f..f26e68e999e 100644 --- a/crates/compiler/mono/src/tail_recursion.rs +++ b/crates/compiler/mono/src/tail_recursion.rs @@ -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, diff --git a/crates/compiler/test_mono/generated/linked_list_filter.txt b/crates/compiler/test_mono/generated/linked_list_filter.txt index 988e96f2fa0..071069e314e 100644 --- a/crates/compiler/test_mono/generated/linked_list_filter.txt +++ b/crates/compiler/test_mono/generated/linked_list_filter.txt @@ -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 : [, C I64 *self] = NullPointer; - let Test.20 : [, C I64 *self] = Reuse #Derived_gen.14 UpdateModeId { id: 1 } TagId(0) Test.7 #Derived_gen.9; + let Test.20 : [, C I64 *self] = Reuse #Derived_gen.14 UpdateModeId { id: 1 } TagId(0) Test.7* #Derived_gen.9; let #Derived_gen.10 : Ptr([, 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; diff --git a/crates/compiler/test_mono/generated/linked_list_reverse.txt b/crates/compiler/test_mono/generated/linked_list_reverse.txt index 892df1a7132..4ee05bff533 100644 --- a/crates/compiler/test_mono/generated/linked_list_reverse.txt +++ b/crates/compiler/test_mono/generated/linked_list_reverse.txt @@ -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 : [, C I64 *self] = UnionAtIndex (Id 0) (Index 1) Test.8; joinpoint #Derived_gen.2 #Derived_gen.4: - let Test.21 : [, C I64 *self] = Reuse #Derived_gen.4 UpdateModeId { id: 1 } TagId(0) Test.9 Test.7; + let Test.21 : [, 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; diff --git a/crates/compiler/test_mono/generated/rb_tree_fbip.txt b/crates/compiler/test_mono/generated/rb_tree_fbip.txt index 28f4d749260..112a7fde6c0 100644 --- a/crates/compiler/test_mono/generated/rb_tree_fbip.txt +++ b/crates/compiler/test_mono/generated/rb_tree_fbip.txt @@ -36,7 +36,7 @@ procedure Test.3 (#Derived_gen.0, #Derived_gen.1, #Derived_gen.2): joinpoint Test.238 #Derived_gen.166: let Test.232 : Int1 = false; let #Derived_gen.10 : [C *self I64 *self I32 Int1, ] = NullPointer; - let Test.231 : [C *self I64 *self I32 Int1, ] = Reuse #Derived_gen.166 UpdateModeId { id: 56 } TagId(1) #Derived_gen.10 Test.18 Test.19 Test.17 Test.232; + let Test.231 : [C *self I64 *self I32 Int1, ] = Reuse #Derived_gen.166 UpdateModeId { id: 56 } TagId(1) #Derived_gen.10 Test.18* Test.19* Test.17* Test.232; let #Derived_gen.11 : Ptr([C *self I64 *self I32 Int1, ]) = UnionFieldPtrAtIndex (Id 1) (Index 0) Test.231; let #Derived_gen.12 : {} = lowlevel PtrStore #Derived_gen.6 Test.231; jump #Derived_gen.5 Test.16 Test.10 Test.11 #Derived_gen.11 #Derived_gen.7; @@ -67,7 +67,7 @@ procedure Test.3 (#Derived_gen.0, #Derived_gen.1, #Derived_gen.2): let Test.185 : Int1 = false; let Test.184 : [C *self I64 *self I32 Int1, ] = Reuse #Derived_gen.190 UpdateModeId { id: 84 } TagId(1) Test.26 Test.18 Test.19 Test.17 Test.185; let Test.182 : Int1 = true; - let Test.181 : [C *self I64 *self I32 Int1, ] = Reuse #Derived_gen.189 UpdateModeId { id: 83 } TagId(1) Test.183 Test.25 Test.184 Test.24 Test.182; + let Test.181 : [C *self I64 *self I32 Int1, ] = Reuse #Derived_gen.189 UpdateModeId { id: 83 } TagId(1) Test.183 Test.25* Test.184 Test.24* Test.182; let #Derived_gen.14 : {} = lowlevel PtrStore #Derived_gen.6 Test.181; let #Derived_gen.13 : [C *self I64 *self I32 Int1, ] = lowlevel PtrLoad #Derived_gen.7; ret #Derived_gen.13; @@ -205,7 +205,7 @@ procedure Test.3 (#Derived_gen.0, #Derived_gen.1, #Derived_gen.2): joinpoint Test.176 #Derived_gen.288: let Test.170 : Int1 = false; let #Derived_gen.21 : [C *self I64 *self I32 Int1, ] = NullPointer; - let Test.169 : [C *self I64 *self I32 Int1, ] = Reuse #Derived_gen.288 UpdateModeId { id: 196 } TagId(1) Test.16 Test.18 #Derived_gen.21 Test.17 Test.170; + let Test.169 : [C *self I64 *self I32 Int1, ] = Reuse #Derived_gen.288 UpdateModeId { id: 196 } TagId(1) Test.16* Test.18* #Derived_gen.21 Test.17* Test.170; let #Derived_gen.22 : Ptr([C *self I64 *self I32 Int1, ]) = UnionFieldPtrAtIndex (Id 1) (Index 2) Test.169; let #Derived_gen.23 : {} = lowlevel PtrStore #Derived_gen.6 Test.169; jump #Derived_gen.5 Test.19 Test.10 Test.11 #Derived_gen.22 #Derived_gen.7; @@ -387,7 +387,7 @@ procedure Test.3 (#Derived_gen.0, #Derived_gen.1, #Derived_gen.2): jump Test.176 #Derived_gen.118; else let Test.116 : Int1 = false; - let Test.115 : [C *self I64 *self I32 Int1, ] = Reuse #Derived_gen.118 UpdateModeId { id: 1 } TagId(1) Test.16 Test.11 Test.19 Test.10 Test.116; + let Test.115 : [C *self I64 *self I32 Int1, ] = Reuse #Derived_gen.118 UpdateModeId { id: 1 } TagId(1) Test.16* Test.11 Test.19* Test.10 Test.116; let #Derived_gen.33 : {} = lowlevel PtrStore #Derived_gen.6 Test.115; let #Derived_gen.32 : [C *self I64 *self I32 Int1, ] = lowlevel PtrLoad #Derived_gen.7; ret #Derived_gen.32; @@ -411,7 +411,7 @@ procedure Test.3 (#Derived_gen.0, #Derived_gen.1, #Derived_gen.2): if Test.247 then let Test.249 : Int1 = true; let #Derived_gen.34 : [C *self I64 *self I32 Int1, ] = NullPointer; - let Test.248 : [C *self I64 *self I32 Int1, ] = Reuse #Derived_gen.364 UpdateModeId { id: 284 } TagId(1) #Derived_gen.34 Test.98 Test.99 Test.97 Test.249; + let Test.248 : [C *self I64 *self I32 Int1, ] = Reuse #Derived_gen.364 UpdateModeId { id: 284 } TagId(1) #Derived_gen.34 Test.98* Test.99* Test.97* Test.249; let #Derived_gen.35 : Ptr([C *self I64 *self I32 Int1, ]) = UnionFieldPtrAtIndex (Id 1) (Index 0) Test.248; let #Derived_gen.36 : {} = lowlevel PtrStore #Derived_gen.6 Test.248; jump #Derived_gen.5 Test.96 Test.10 Test.11 #Derived_gen.35 #Derived_gen.7; @@ -420,13 +420,13 @@ procedure Test.3 (#Derived_gen.0, #Derived_gen.1, #Derived_gen.2): if Test.243 then let Test.245 : Int1 = true; let #Derived_gen.37 : [C *self I64 *self I32 Int1, ] = NullPointer; - let Test.244 : [C *self I64 *self I32 Int1, ] = Reuse #Derived_gen.364 UpdateModeId { id: 284 } TagId(1) Test.96 Test.98 #Derived_gen.37 Test.97 Test.245; + let Test.244 : [C *self I64 *self I32 Int1, ] = Reuse #Derived_gen.364 UpdateModeId { id: 284 } TagId(1) Test.96* Test.98* #Derived_gen.37 Test.97* Test.245; let #Derived_gen.38 : Ptr([C *self I64 *self I32 Int1, ]) = UnionFieldPtrAtIndex (Id 1) (Index 2) Test.244; let #Derived_gen.39 : {} = lowlevel PtrStore #Derived_gen.6 Test.244; jump #Derived_gen.5 Test.99 Test.10 Test.11 #Derived_gen.38 #Derived_gen.7; else let Test.242 : Int1 = true; - let Test.241 : [C *self I64 *self I32 Int1, ] = Reuse #Derived_gen.364 UpdateModeId { id: 284 } TagId(1) Test.96 Test.11 Test.99 Test.10 Test.242; + let Test.241 : [C *self I64 *self I32 Int1, ] = Reuse #Derived_gen.364 UpdateModeId { id: 284 } TagId(1) Test.96* Test.11 Test.99* Test.10 Test.242; let #Derived_gen.41 : {} = lowlevel PtrStore #Derived_gen.6 Test.241; let #Derived_gen.40 : [C *self I64 *self I32 Int1, ] = lowlevel PtrLoad #Derived_gen.7; ret #Derived_gen.40;