1use std::{
2 hash::Hash,
3 ops::{Deref, DerefMut},
4};
5
6use anyhow::{Context, Result, bail};
7use either::Either;
8use indexmap::map::Entry;
9use roaring::RoaringBitmap;
10use rustc_hash::FxHashMap;
11use serde::{Deserialize, Serialize};
12use tracing::Instrument;
13use turbo_rcstr::RcStr;
14use turbo_tasks::{
15 FxIndexMap, FxIndexSet, NonLocalValue, ResolvedVc, TaskInput, TryJoinIterExt, ValueToString,
16 Vc, debug::ValueDebugFormat, trace::TraceRawVcs,
17};
18
19use crate::{
20 chunk::ChunkingType,
21 module::Module,
22 module_graph::{GraphTraversalAction, ModuleGraphRef, RefData, SingleModuleGraphModuleNode},
23};
24
25#[derive(
26 Clone, Debug, Default, PartialEq, Serialize, Deserialize, TraceRawVcs, ValueDebugFormat,
27)]
28#[repr(transparent)]
29pub struct RoaringBitmapWrapper(#[turbo_tasks(trace_ignore)] pub RoaringBitmap);
30
31impl TaskInput for RoaringBitmapWrapper {
32 fn is_transient(&self) -> bool {
33 false
34 }
35}
36
37impl RoaringBitmapWrapper {
38 pub fn is_proper_superset(&self, other: &Self) -> bool {
42 !self.is_subset(other)
43 }
44
45 pub fn into_inner(self) -> RoaringBitmap {
46 self.0
47 }
48}
49unsafe impl NonLocalValue for RoaringBitmapWrapper {}
50
51impl Eq for RoaringBitmapWrapper {}
55
56impl Deref for RoaringBitmapWrapper {
57 type Target = RoaringBitmap;
58 fn deref(&self) -> &Self::Target {
59 &self.0
60 }
61}
62impl DerefMut for RoaringBitmapWrapper {
63 fn deref_mut(&mut self) -> &mut Self::Target {
64 &mut self.0
65 }
66}
67impl Hash for RoaringBitmapWrapper {
68 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
69 struct HasherWriter<'a, H: std::hash::Hasher>(&'a mut H);
70 impl<H: std::hash::Hasher> std::io::Write for HasherWriter<'_, H> {
71 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
72 self.0.write(buf);
73 Ok(buf.len())
74 }
75 fn flush(&mut self) -> std::io::Result<()> {
76 Ok(())
77 }
78 }
79 self.0.serialize_into(HasherWriter(state)).unwrap();
80 }
81}
82
83#[turbo_tasks::value]
84pub struct ChunkGroupInfo {
85 pub module_chunk_groups: FxHashMap<ResolvedVc<Box<dyn Module>>, RoaringBitmapWrapper>,
86 #[turbo_tasks(trace_ignore)]
87 pub chunk_groups: FxIndexSet<ChunkGroup>,
88 #[turbo_tasks(trace_ignore)]
89 pub chunk_group_keys: FxIndexSet<ChunkGroupKey>,
90}
91
92#[turbo_tasks::value_impl]
93impl ChunkGroupInfo {
94 #[turbo_tasks::function]
95 pub async fn get_index_of(&self, chunk_group: ChunkGroup) -> Result<Vc<usize>> {
96 if let Some(idx) = self.chunk_groups.get_index_of(&chunk_group) {
97 Ok(Vc::cell(idx))
98 } else {
99 bail!(
100 "Couldn't find chunk group index for {} in {}",
101 chunk_group.debug_str(self).await?,
102 self.chunk_groups
103 .iter()
104 .map(|c| c.debug_str(self))
105 .try_join()
106 .await?
107 .join(", ")
108 );
109 }
110 }
111}
112
113#[derive(
114 Debug, Clone, Hash, TaskInput, PartialEq, Eq, Serialize, Deserialize, TraceRawVcs, NonLocalValue,
115)]
116pub enum ChunkGroupEntry {
117 Entry(Vec<ResolvedVc<Box<dyn Module>>>),
119 Async(ResolvedVc<Box<dyn Module>>),
121 Isolated(ResolvedVc<Box<dyn Module>>),
123 IsolatedMerged {
125 parent: Box<ChunkGroupEntry>,
126 merge_tag: RcStr,
127 entries: Vec<ResolvedVc<Box<dyn Module>>>,
128 },
129 Shared(ResolvedVc<Box<dyn Module>>),
131 SharedMerged {
133 parent: Box<ChunkGroupEntry>,
134 merge_tag: RcStr,
135 entries: Vec<ResolvedVc<Box<dyn Module>>>,
136 },
137}
138impl ChunkGroupEntry {
139 pub fn entries(&self) -> impl Iterator<Item = ResolvedVc<Box<dyn Module>>> + '_ {
140 match self {
141 Self::Async(e) | Self::Isolated(e) | Self::Shared(e) => {
142 Either::Left(std::iter::once(*e))
143 }
144 Self::Entry(entries)
145 | Self::IsolatedMerged { entries, .. }
146 | Self::SharedMerged { entries, .. } => Either::Right(entries.iter().copied()),
147 }
148 }
149}
150
151#[derive(Debug, Clone, Hash, TaskInput, PartialEq, Eq, Serialize, Deserialize, TraceRawVcs)]
152pub enum ChunkGroup {
153 Entry(Vec<ResolvedVc<Box<dyn Module>>>),
155 Async(ResolvedVc<Box<dyn Module>>),
157 Isolated(ResolvedVc<Box<dyn Module>>),
159 IsolatedMerged {
161 parent: usize,
162 merge_tag: RcStr,
163 entries: Vec<ResolvedVc<Box<dyn Module>>>,
164 },
165 Shared(ResolvedVc<Box<dyn Module>>),
167 SharedMerged {
169 parent: usize,
170 merge_tag: RcStr,
171 entries: Vec<ResolvedVc<Box<dyn Module>>>,
172 },
173}
174
175impl ChunkGroup {
176 pub fn get_merged_parent(&self) -> Option<usize> {
179 match self {
180 ChunkGroup::IsolatedMerged { parent, .. } | ChunkGroup::SharedMerged { parent, .. } => {
181 Some(*parent)
182 }
183 _ => None,
184 }
185 }
186
187 pub fn entries(&self) -> impl Iterator<Item = ResolvedVc<Box<dyn Module>>> + Clone + '_ {
190 match self {
191 ChunkGroup::Async(e) | ChunkGroup::Isolated(e) | ChunkGroup::Shared(e) => {
192 Either::Left(std::iter::once(*e))
193 }
194 ChunkGroup::Entry(entries)
195 | ChunkGroup::IsolatedMerged { entries, .. }
196 | ChunkGroup::SharedMerged { entries, .. } => Either::Right(entries.iter().copied()),
197 }
198 }
199
200 pub fn entries_count(&self) -> usize {
201 match self {
202 ChunkGroup::Async(_) | ChunkGroup::Isolated(_) | ChunkGroup::Shared(_) => 1,
203 ChunkGroup::Entry(entries)
204 | ChunkGroup::IsolatedMerged { entries, .. }
205 | ChunkGroup::SharedMerged { entries, .. } => entries.len(),
206 }
207 }
208
209 pub async fn debug_str(&self, chunk_group_info: &ChunkGroupInfo) -> Result<String> {
210 Ok(match self {
211 ChunkGroup::Entry(entries) => format!(
212 "ChunkGroup::Entry({:?})",
213 entries
214 .iter()
215 .map(|m| m.ident().to_string())
216 .try_join()
217 .await?
218 ),
219 ChunkGroup::Async(entry) => {
220 format!("ChunkGroup::Async({:?})", entry.ident().to_string().await?)
221 }
222 ChunkGroup::Isolated(entry) => {
223 format!(
224 "ChunkGroup::Isolated({:?})",
225 entry.ident().to_string().await?
226 )
227 }
228 ChunkGroup::Shared(entry) => {
229 format!("ChunkGroup::Shared({:?})", entry.ident().to_string().await?)
230 }
231 ChunkGroup::IsolatedMerged {
232 parent,
233 merge_tag,
234 entries,
235 } => {
236 format!(
237 "ChunkGroup::IsolatedMerged({}, {}, {:?})",
238 Box::pin(chunk_group_info.chunk_groups[*parent].debug_str(chunk_group_info))
239 .await?,
240 merge_tag,
241 entries
242 .iter()
243 .map(|m| m.ident().to_string())
244 .try_join()
245 .await?
246 )
247 }
248 ChunkGroup::SharedMerged {
249 parent,
250 merge_tag,
251 entries,
252 } => {
253 format!(
254 "ChunkGroup::SharedMerged({}, {}, {:?})",
255 Box::pin(chunk_group_info.chunk_groups[*parent].debug_str(chunk_group_info))
256 .await?,
257 merge_tag,
258 entries
259 .iter()
260 .map(|m| m.ident().to_string())
261 .try_join()
262 .await?
263 )
264 }
265 })
266 }
267}
268
269#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
270pub enum ChunkGroupKey {
271 Entry(Vec<ResolvedVc<Box<dyn Module>>>),
273 Async(ResolvedVc<Box<dyn Module>>),
275 Isolated(ResolvedVc<Box<dyn Module>>),
277 IsolatedMerged {
279 parent: ChunkGroupId,
280 merge_tag: RcStr,
281 },
282 Shared(ResolvedVc<Box<dyn Module>>),
284 SharedMerged {
286 parent: ChunkGroupId,
287 merge_tag: RcStr,
288 },
289}
290
291impl ChunkGroupKey {
292 pub async fn debug_str(
293 &self,
294 keys: impl std::ops::Index<usize, Output = Self>,
295 ) -> Result<String> {
296 Ok(match self {
297 ChunkGroupKey::Entry(entries) => format!(
298 "Entry({:?})",
299 entries
300 .iter()
301 .map(|m| m.ident().to_string())
302 .try_join()
303 .await?
304 ),
305 ChunkGroupKey::Async(module) => {
306 format!("Async({:?})", module.ident().to_string().await?)
307 }
308 ChunkGroupKey::Isolated(module) => {
309 format!("Isolated({:?})", module.ident().to_string().await?)
310 }
311 ChunkGroupKey::IsolatedMerged { parent, merge_tag } => {
312 format!(
313 "IsolatedMerged {{ parent: {}, merge_tag: {:?} }}",
314 Box::pin(keys.index(parent.0 as usize).clone().debug_str(keys)).await?,
315 merge_tag
316 )
317 }
318 ChunkGroupKey::Shared(module) => {
319 format!("Shared({:?})", module.ident().to_string().await?)
320 }
321 ChunkGroupKey::SharedMerged { parent, merge_tag } => {
322 format!(
323 "SharedMerged {{ parent: {}, merge_tag: {:?} }}",
324 Box::pin(keys.index(parent.0 as usize).clone().debug_str(keys)).await?,
325 merge_tag
326 )
327 }
328 })
329 }
330}
331
332#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
333pub struct ChunkGroupId(u32);
334
335impl From<usize> for ChunkGroupId {
336 fn from(id: usize) -> Self {
337 Self(id as u32)
338 }
339}
340
341impl Deref for ChunkGroupId {
342 type Target = u32;
343 fn deref(&self) -> &Self::Target {
344 &self.0
345 }
346}
347
348#[derive(Debug, Clone, PartialEq, Eq)]
349struct TraversalPriority {
350 depth: usize,
351 chunk_group_len: u64,
352}
353impl PartialOrd for TraversalPriority {
354 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
355 Some(self.cmp(other))
356 }
357}
358impl Ord for TraversalPriority {
359 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
360 let depth_order = self.depth.cmp(&other.depth).reverse();
364 let chunk_group_len_order = self.chunk_group_len.cmp(&other.chunk_group_len).reverse();
366
367 depth_order.then(chunk_group_len_order)
368 }
369}
370
371pub async fn compute_chunk_group_info(graph: &ModuleGraphRef) -> Result<Vc<ChunkGroupInfo>> {
372 let span_outer = tracing::info_span!(
373 "compute chunk group info",
374 module_count = tracing::field::Empty,
375 visit_count = tracing::field::Empty,
376 chunk_group_count = tracing::field::Empty
377 );
378
379 let span = span_outer.clone();
380 async move {
381 #[allow(clippy::type_complexity)]
382 let mut chunk_groups_map: FxIndexMap<
383 ChunkGroupKey,
384 (ChunkGroupId, FxIndexSet<ResolvedVc<Box<dyn Module>>>),
385 > = FxIndexMap::default();
386
387 let mut module_chunk_groups: FxHashMap<ResolvedVc<Box<dyn Module>>, RoaringBitmapWrapper> =
390 FxHashMap::default();
391
392 let module_count = graph
393 .graphs
394 .iter()
395 .map(|g| g.graph.node_count())
396 .sum::<usize>();
397 span.record("module_count", module_count);
398
399 let entries = graph
401 .graphs
402 .iter()
403 .flat_map(|g| g.entries.iter())
404 .collect::<Vec<_>>();
405
406 let module_depth: FxHashMap<ResolvedVc<Box<dyn Module>>, usize> = {
408 let mut module_depth =
409 FxHashMap::with_capacity_and_hasher(module_count, Default::default());
410 graph.traverse_edges_from_entries_bfs(
411 entries.iter().flat_map(|e| e.entries()),
412 |parent, node| {
413 if let Some((parent, _)) = parent {
414 let parent_depth = *module_depth
415 .get(&parent.module)
416 .context("Module depth not found")?;
417 module_depth.entry(node.module).or_insert(parent_depth + 1);
418 } else {
419 module_depth.insert(node.module, 0);
420 };
421
422 module_chunk_groups.insert(node.module, RoaringBitmapWrapper::default());
423
424 Ok(GraphTraversalAction::Continue)
425 },
426 )?;
427 module_depth
428 };
429
430 #[allow(clippy::type_complexity)]
433 fn entry_to_chunk_group_id(
434 entry: ChunkGroupEntry,
435 chunk_groups_map: &mut FxIndexMap<
436 ChunkGroupKey,
437 (ChunkGroupId, FxIndexSet<ResolvedVc<Box<dyn Module>>>),
438 >,
439 ) -> ChunkGroupKey {
440 match entry {
441 ChunkGroupEntry::Entry(entries) => ChunkGroupKey::Entry(entries),
442 ChunkGroupEntry::Async(entry) => ChunkGroupKey::Async(entry),
443 ChunkGroupEntry::Isolated(entry) => ChunkGroupKey::Isolated(entry),
444 ChunkGroupEntry::Shared(entry) => ChunkGroupKey::Shared(entry),
445 ChunkGroupEntry::IsolatedMerged {
446 parent,
447 merge_tag,
448 entries: _,
449 } => {
450 let parent = entry_to_chunk_group_id(*parent, chunk_groups_map);
451 let len = chunk_groups_map.len();
452 let parent = chunk_groups_map
453 .entry(parent)
454 .or_insert_with(|| (ChunkGroupId(len as u32), FxIndexSet::default()))
455 .0;
456
457 ChunkGroupKey::IsolatedMerged {
458 parent: ChunkGroupId(*parent as u32),
459 merge_tag,
460 }
461 }
462 ChunkGroupEntry::SharedMerged {
463 parent,
464 merge_tag,
465 entries: _,
466 } => {
467 let parent = entry_to_chunk_group_id(*parent, chunk_groups_map);
468 let len = chunk_groups_map.len();
469 let parent = chunk_groups_map
470 .entry(parent)
471 .or_insert_with(|| (ChunkGroupId(len as u32), FxIndexSet::default()))
472 .0;
473
474 ChunkGroupKey::SharedMerged {
475 parent: ChunkGroupId(*parent as u32),
476 merge_tag,
477 }
478 }
479 }
480 }
481
482 let entry_chunk_group_keys = graph
483 .graphs
484 .iter()
485 .flat_map(|g| g.entries.iter())
486 .flat_map(|chunk_group| {
487 let chunk_group_key =
488 entry_to_chunk_group_id(chunk_group.clone(), &mut chunk_groups_map);
489 chunk_group
490 .entries()
491 .map(move |e| (e, chunk_group_key.clone()))
492 })
493 .collect::<FxHashMap<_, _>>();
494
495 let visit_count = graph.traverse_edges_fixed_point_with_priority(
496 entries
497 .iter()
498 .flat_map(|e| e.entries())
499 .map(|e| {
500 Ok((
501 e,
502 TraversalPriority {
503 depth: *module_depth.get(&e).context("Module depth not found")?,
504 chunk_group_len: 0,
505 },
506 ))
507 })
508 .collect::<Result<Vec<_>>>()?,
509 &mut module_chunk_groups,
510 |parent_info: Option<(&'_ SingleModuleGraphModuleNode, &'_ RefData)>,
511 node: &'_ SingleModuleGraphModuleNode,
512 module_chunk_groups: &mut FxHashMap<
513 ResolvedVc<Box<dyn Module>>,
514 RoaringBitmapWrapper,
515 >|
516 -> Result<GraphTraversalAction> {
517 enum ChunkGroupInheritance<It: Iterator<Item = ChunkGroupKey>> {
518 Inherit(ResolvedVc<Box<dyn Module>>),
519 ChunkGroup(It),
520 }
521 let chunk_groups = if let Some((parent, ref_data)) = parent_info {
522 match &ref_data.chunking_type {
523 ChunkingType::Parallel { .. } => {
524 ChunkGroupInheritance::Inherit(parent.module)
525 }
526 ChunkingType::Async => ChunkGroupInheritance::ChunkGroup(Either::Left(
527 std::iter::once(ChunkGroupKey::Async(node.module)),
528 )),
529 ChunkingType::Isolated {
530 merge_tag: None, ..
531 } => ChunkGroupInheritance::ChunkGroup(Either::Left(std::iter::once(
532 ChunkGroupKey::Isolated(node.module),
533 ))),
534 ChunkingType::Shared {
535 merge_tag: None, ..
536 } => ChunkGroupInheritance::ChunkGroup(Either::Left(std::iter::once(
537 ChunkGroupKey::Shared(node.module),
538 ))),
539 ChunkingType::Isolated {
540 merge_tag: Some(merge_tag),
541 ..
542 } => {
543 let parents = module_chunk_groups
544 .get(&parent.module)
545 .context("Module chunk group not found")?;
546 let chunk_groups =
547 parents.iter().map(|parent| ChunkGroupKey::IsolatedMerged {
548 parent: ChunkGroupId(parent),
549 merge_tag: merge_tag.clone(),
550 });
551 ChunkGroupInheritance::ChunkGroup(Either::Right(Either::Left(
552 chunk_groups,
553 )))
554 }
555 ChunkingType::Shared {
556 merge_tag: Some(merge_tag),
557 ..
558 } => {
559 let parents = module_chunk_groups
560 .get(&parent.module)
561 .context("Module chunk group not found")?;
562 let chunk_groups =
563 parents.iter().map(|parent| ChunkGroupKey::SharedMerged {
564 parent: ChunkGroupId(parent),
565 merge_tag: merge_tag.clone(),
566 });
567 ChunkGroupInheritance::ChunkGroup(Either::Right(Either::Right(
568 chunk_groups,
569 )))
570 }
571 ChunkingType::Traced => {
572 return Ok(GraphTraversalAction::Skip);
574 }
575 }
576 } else {
577 ChunkGroupInheritance::ChunkGroup(Either::Left(std::iter::once(
578 entry_chunk_group_keys
580 .get(&node.module)
581 .context("Module chunk group not found")?
582 .clone(),
583 )))
584 };
585
586 Ok(match chunk_groups {
587 ChunkGroupInheritance::ChunkGroup(chunk_groups) => {
588 let chunk_group_ids = chunk_groups.map(|chunk_group| {
590 let len = chunk_groups_map.len();
591 let is_merged = matches!(
592 chunk_group,
593 ChunkGroupKey::IsolatedMerged { .. }
594 | ChunkGroupKey::SharedMerged { .. }
595 );
596 match chunk_groups_map.entry(chunk_group) {
597 Entry::Occupied(mut e) => {
598 let (id, merged_entries) = e.get_mut();
599 if is_merged {
600 merged_entries.insert(node.module);
601 }
602 **id
603 }
604 Entry::Vacant(e) => {
605 let chunk_group_id = len as u32;
606 let mut set = FxIndexSet::default();
607 if is_merged {
608 set.insert(node.module);
609 }
610 e.insert((ChunkGroupId(chunk_group_id), set));
611 chunk_group_id
612 }
613 }
614 });
615
616 let chunk_groups =
617 RoaringBitmapWrapper(RoaringBitmap::from_iter(chunk_group_ids));
618
619 let bitset = module_chunk_groups
621 .get_mut(&node.module)
622 .context("Module chunk group not found")?;
623 if chunk_groups.is_proper_superset(bitset) {
624 **bitset |= chunk_groups.into_inner();
626
627 GraphTraversalAction::Continue
628 } else {
629 GraphTraversalAction::Skip
631 }
632 }
633 ChunkGroupInheritance::Inherit(parent) => {
634 if parent == node.module {
638 GraphTraversalAction::Skip
640 } else {
641 let [Some(parent_chunk_groups), Some(current_chunk_groups)] =
642 module_chunk_groups.get_disjoint_mut([&parent, &node.module])
643 else {
644 bail!("Module chunk groups not found");
648 };
649
650 if current_chunk_groups.is_empty() {
651 *current_chunk_groups = parent_chunk_groups.clone();
653 GraphTraversalAction::Continue
654 } else if parent_chunk_groups.is_proper_superset(current_chunk_groups) {
655 **current_chunk_groups |= &**parent_chunk_groups;
657 GraphTraversalAction::Continue
658 } else {
659 GraphTraversalAction::Skip
661 }
662 }
663 }
664 })
665 },
666 |successor, module_chunk_groups| {
674 Ok(TraversalPriority {
675 depth: *module_depth
676 .get(&successor.module)
677 .context("Module depth not found")?,
678 chunk_group_len: module_chunk_groups
679 .get(&successor.module)
680 .context("Module chunk group not found")?
681 .len(),
682 })
683 },
684 )?;
685
686 span.record("visit_count", visit_count);
687 span.record("chunk_group_count", chunk_groups_map.len());
688
689 #[cfg(debug_assertions)]
690 {
691 use once_cell::sync::Lazy;
692 static PRINT_CHUNK_GROUP_INFO: Lazy<bool> =
693 Lazy::new(|| match std::env::var_os("TURBOPACK_PRINT_CHUNK_GROUPS") {
694 Some(v) => v == "1",
695 None => false,
696 });
697 if *PRINT_CHUNK_GROUP_INFO {
698 use std::{
699 collections::{BTreeMap, BTreeSet},
700 path::absolute,
701 };
702
703 let mut buckets = BTreeMap::default();
704 for (module, key) in &module_chunk_groups {
705 if !key.is_empty() {
706 buckets
707 .entry(key.iter().collect::<Vec<_>>())
708 .or_insert(BTreeSet::new())
709 .insert(module.ident().to_string().await?);
710 }
711 }
712
713 let mut result = vec![];
714 result.push("Chunk Groups:".to_string());
715 for (i, (key, _)) in chunk_groups_map.iter().enumerate() {
716 result.push(format!(
717 " {:?}: {}",
718 i,
719 key.debug_str(chunk_groups_map.keys()).await?
720 ));
721 }
722 result.push("# Module buckets:".to_string());
723 for (key, modules) in buckets.iter() {
724 result.push(format!("## {:?}:", key.iter().collect::<Vec<_>>()));
725 for module in modules {
726 result.push(format!(" {module}"));
727 }
728 result.push("".to_string());
729 }
730 let f = absolute("chunk_group_info.log")?;
731 println!("written to {}", f.display());
732 std::fs::write(f, result.join("\n"))?;
733 }
734 }
735
736 Ok(ChunkGroupInfo {
737 module_chunk_groups,
738 chunk_group_keys: chunk_groups_map.keys().cloned().collect(),
739 chunk_groups: chunk_groups_map
740 .into_iter()
741 .map(|(k, (_, merged_entries))| match k {
742 ChunkGroupKey::Entry(entries) => ChunkGroup::Entry(entries),
743 ChunkGroupKey::Async(module) => ChunkGroup::Async(module),
744 ChunkGroupKey::Isolated(module) => ChunkGroup::Isolated(module),
745 ChunkGroupKey::IsolatedMerged { parent, merge_tag } => {
746 ChunkGroup::IsolatedMerged {
747 parent: parent.0 as usize,
748 merge_tag,
749 entries: merged_entries.into_iter().collect(),
750 }
751 }
752 ChunkGroupKey::Shared(module) => ChunkGroup::Shared(module),
753 ChunkGroupKey::SharedMerged { parent, merge_tag } => ChunkGroup::SharedMerged {
754 parent: parent.0 as usize,
755 merge_tag,
756 entries: merged_entries.into_iter().collect(),
757 },
758 })
759 .collect(),
760 }
761 .cell())
762 }
763 .instrument(span_outer)
764 .await
765}