@@ -287,105 +287,6 @@ static void claim_unresolved_symbols(Context<E> &ctx) {
287287 });
288288}
289289
290- template <typename E>
291- static void merge_cstring_sections (Context<E> &ctx) {
292- Timer t (ctx, " merge_cstring_sections" );
293-
294- struct Entry {
295- Entry (Subsection<E> *subsec) : owner(subsec) {}
296-
297- Entry (const Entry &other) :
298- owner (other.owner.load()), p2align(other.p2align.load()) {}
299-
300- std::atomic<Subsection<E> *> owner = nullptr ;
301- std::atomic_uint8_t p2align = 0 ;
302- };
303-
304- struct SubsecRef {
305- Subsection<E> &subsec;
306- u64 hash = 0 ;
307- Entry *ent = nullptr ;
308- };
309-
310- std::vector<std::vector<SubsecRef>> vec (ctx.objs .size ());
311-
312- // Estimate the number of unique strings.
313- HyperLogLog estimator;
314-
315- tbb::parallel_for ((i64 )0 , (i64 )ctx.objs .size (), [&](i64 i) {
316- ObjectFile<E> *file = ctx.objs [i];
317- HyperLogLog e;
318-
319- for (Subsection<E> *subsec : file->subsections ) {
320- if (&subsec->isec .osec == ctx.cstring ) {
321- std::string_view str = subsec->get_contents ();
322- u64 h = hash_string (str);
323- vec[i].push_back ({*subsec, h, nullptr });
324- estimator.insert (h);
325- }
326- }
327- estimator.merge (e);
328- });
329-
330- // Create a hash map large enough to hold all strings.
331- ConcurrentMap<Entry> map (estimator.get_cardinality () * 3 / 2 );
332-
333- // Insert all strings into the hash table.
334- tbb::parallel_for ((i64 )0 , (i64 )ctx.objs .size (), [&](i64 i) {
335- ObjectFile<E> *file = ctx.objs [i];
336-
337- for (i64 j = 0 ; j < vec[i].size (); j++) {
338- SubsecRef &ref = vec[i][j];
339- std::string_view s = ref.subsec .get_contents ();
340- ref.ent = map.insert (s, ref.hash , {&ref.subsec }).first ;
341-
342- Subsection<E> *existing = ref.ent ->owner ;
343- while (existing->isec .file .priority < file->priority &&
344- !ref.ent ->owner .compare_exchange_weak (existing, &ref.subsec ));
345-
346- update_maximum (ref.ent ->p2align , ref.subsec .p2align .load ());
347- }
348- });
349-
350- // Decide who will become the owner for each subsection.
351- tbb::parallel_for ((i64 )0 , (i64 )ctx.objs .size (), [&](i64 i) {
352- for (i64 j = 0 ; j < vec[i].size (); j++) {
353- SubsecRef &ref = vec[i][j];
354- if (ref.ent ->owner != &ref.subsec ) {
355- ref.subsec .is_coalesced = true ;
356- ref.subsec .replacer = ref.ent ->owner ;
357-
358- static Counter counter (" num_merged_strings" );
359- counter++;
360- }
361- }
362- });
363-
364- // Merge strings
365- tbb::parallel_for_each (ctx.objs , [&](ObjectFile<E> *file) {
366- for (std::unique_ptr<InputSection<E>> &isec : file->sections )
367- if (isec)
368- for (Relocation<E> &r : isec->rels )
369- if (r.subsec && r.subsec ->is_coalesced )
370- r.subsec = r.subsec ->replacer ;
371- });
372-
373- auto replace = [&](InputFile<E> *file) {
374- for (Symbol<E> *sym : file->syms )
375- if (sym->subsec && sym->subsec ->is_coalesced )
376- sym->subsec = sym->subsec ->replacer ;
377- };
378-
379- tbb::parallel_for_each (ctx.objs , replace);
380- tbb::parallel_for_each (ctx.dylibs , replace);
381-
382- tbb::parallel_for_each (ctx.objs , [&](ObjectFile<E> *file) {
383- std::erase_if (file->subsections , [](Subsection<E> *subsec) {
384- return subsec->is_coalesced ;
385- });
386- });
387- }
388-
389290template <typename E>
390291static Chunk<E> *find_section (Context<E> &ctx, std::string_view segname,
391292 std::string_view sectname) {
@@ -446,6 +347,109 @@ static void create_synthetic_chunks(Context<E> &ctx) {
446347 sort (seg->chunks , compare_chunks<E>);
447348}
448349
350+ template <typename E>
351+ static void uniquify_cstrings (Context<E> &ctx, OutputSection<E> &osec) {
352+ Timer t (ctx, " uniquify_cstrings" );
353+
354+ struct Entry {
355+ Entry (Subsection<E> *subsec) : owner(subsec) {}
356+
357+ Entry (const Entry &other) :
358+ owner (other.owner.load()), p2align(other.p2align.load()) {}
359+
360+ std::atomic<Subsection<E> *> owner = nullptr ;
361+ std::atomic_uint8_t p2align = 0 ;
362+ };
363+
364+ struct SubsecRef {
365+ Subsection<E> *subsec = nullptr ;
366+ u64 hash = 0 ;
367+ Entry *ent = nullptr ;
368+ };
369+
370+ std::vector<SubsecRef> vec (osec.members .size ());
371+
372+ // Estimate the number of unique strings.
373+ tbb::enumerable_thread_specific<HyperLogLog> estimators;
374+
375+ tbb::parallel_for ((i64 )0 , (i64 )osec.members .size (), [&](i64 i) {
376+ Subsection<E> *subsec = osec.members [i];
377+ if (subsec->is_cstring ) {
378+ u64 h = hash_string (subsec->get_contents ());
379+ vec[i].subsec = subsec;
380+ vec[i].hash = h;
381+ estimators.local ().insert (h);
382+ }
383+ });
384+
385+ HyperLogLog estimator;
386+ for (HyperLogLog &e : estimators)
387+ estimator.merge (e);
388+
389+ // Create a hash map large enough to hold all strings.
390+ ConcurrentMap<Entry> map (estimator.get_cardinality () * 3 / 2 );
391+
392+ // Insert all strings into the hash table.
393+ tbb::parallel_for_each (vec, [&](SubsecRef &ref) {
394+ if (ref.subsec ) {
395+ std::string_view s = ref.subsec ->get_contents ();
396+ ref.ent = map.insert (s, ref.hash , {ref.subsec }).first ;
397+
398+ Subsection<E> *existing = ref.ent ->owner ;
399+ while (existing->isec .file .priority < ref.subsec ->isec .file .priority &&
400+ !ref.ent ->owner .compare_exchange_weak (existing, ref.subsec ));
401+
402+ update_maximum (ref.ent ->p2align , ref.subsec ->p2align .load ());
403+ }
404+ });
405+
406+ // Decide who will become the owner for each subsection.
407+ tbb::parallel_for_each (vec, [&](SubsecRef &ref) {
408+ if (ref.subsec && ref.subsec != ref.ent ->owner ) {
409+ ref.subsec ->is_coalesced = true ;
410+ ref.subsec ->replacer = ref.ent ->owner ;
411+ }
412+ });
413+
414+ static Counter counter (" num_merged_strings" );
415+ counter += std::erase_if (osec.members , [](Subsection<E> *subsec) {
416+ return subsec->is_coalesced ;
417+ });
418+ }
419+
420+ template <typename E>
421+ static void merge_cstring_sections (Context<E> &ctx) {
422+ Timer t (ctx, " merge_cstring_sections" );
423+
424+ for (Chunk<E> *chunk : ctx.chunks )
425+ if (chunk->is_output_section && chunk->hdr .type == S_CSTRING_LITERALS)
426+ uniquify_cstrings (ctx, *(OutputSection<E> *)chunk);
427+
428+ // Rewrite relocations and symbols.
429+ tbb::parallel_for_each (ctx.objs , [&](ObjectFile<E> *file) {
430+ for (std::unique_ptr<InputSection<E>> &isec : file->sections )
431+ if (isec)
432+ for (Relocation<E> &r : isec->rels )
433+ if (r.subsec && r.subsec ->is_coalesced )
434+ r.subsec = r.subsec ->replacer ;
435+ });
436+
437+ std::vector<InputFile<E> *> files;
438+ append (files, ctx.objs );
439+ append (files, ctx.dylibs );
440+
441+ for (InputFile<E> *file: files)
442+ for (Symbol<E> *sym : file->syms )
443+ if (sym && sym->subsec && sym->subsec ->is_coalesced )
444+ sym->subsec = sym->subsec ->replacer ;
445+
446+ tbb::parallel_for_each (ctx.objs , [&](ObjectFile<E> *file) {
447+ std::erase_if (file->subsections , [](Subsection<E> *subsec) {
448+ return subsec->is_coalesced ;
449+ });
450+ });
451+ }
452+
449453template <typename E>
450454static void scan_unwind_info (Context<E> &ctx) {
451455 tbb::parallel_for_each (ctx.objs , [&](ObjectFile<E> *file) {
@@ -930,12 +934,11 @@ static int do_main(int argc, char **argv) {
930934
931935 claim_unresolved_symbols (ctx);
932936
933- merge_cstring_sections (ctx);
934-
935937 if (ctx.arg .dead_strip )
936938 dead_strip (ctx);
937939
938940 create_synthetic_chunks (ctx);
941+ merge_cstring_sections (ctx);
939942
940943 for (ObjectFile<E> *file : ctx.objs )
941944 file->check_duplicate_symbols (ctx);
0 commit comments