@@ -287,105 +287,6 @@ static void claim_unresolved_symbols(Context<E> &ctx) {
287
287
});
288
288
}
289
289
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
-
389
290
template <typename E>
390
291
static Chunk<E> *find_section (Context<E> &ctx, std::string_view segname,
391
292
std::string_view sectname) {
@@ -446,6 +347,109 @@ static void create_synthetic_chunks(Context<E> &ctx) {
446
347
sort (seg->chunks , compare_chunks<E>);
447
348
}
448
349
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
+ auto replace = [&](InputFile<E> *file) {
438
+ for (Symbol<E> *sym : file->syms )
439
+ if (sym && sym->subsec && sym->subsec ->is_coalesced )
440
+ sym->subsec = sym->subsec ->replacer ;
441
+ };
442
+
443
+ tbb::parallel_for_each (ctx.objs , replace);
444
+ tbb::parallel_for_each (ctx.dylibs , replace);
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
+
449
453
template <typename E>
450
454
static void scan_unwind_info (Context<E> &ctx) {
451
455
tbb::parallel_for_each (ctx.objs , [&](ObjectFile<E> *file) {
@@ -930,12 +934,11 @@ static int do_main(int argc, char **argv) {
930
934
931
935
claim_unresolved_symbols (ctx);
932
936
933
- merge_cstring_sections (ctx);
934
-
935
937
if (ctx.arg .dead_strip )
936
938
dead_strip (ctx);
937
939
938
940
create_synthetic_chunks (ctx);
941
+ merge_cstring_sections (ctx);
939
942
940
943
for (ObjectFile<E> *file : ctx.objs )
941
944
file->check_duplicate_symbols (ctx);
0 commit comments