@@ -173,6 +173,9 @@ class StringConcat : public ResourceObj {
173
173
assert (!_control.contains (ctrl), " only push once" );
174
174
_control.push (ctrl);
175
175
}
176
+ bool has_control (Node* ctrl) {
177
+ return _control.contains (ctrl);
178
+ }
176
179
void add_constructor (Node* init) {
177
180
assert (!_constructors.contains (init), " only push once" );
178
181
_constructors.push (init);
@@ -407,7 +410,66 @@ Node_List PhaseStringOpts::collect_toString_calls() {
407
410
return string_calls;
408
411
}
409
412
410
- // Recognize a fluent-chain of StringBuilder/Buffer. They are either explicit usages
413
+ PhaseStringOpts::ProcessAppendResult PhaseStringOpts::process_append_candidate (CallStaticJavaNode* cnode,
414
+ StringConcat* sc,
415
+ ciMethod* m,
416
+ ciSymbol* string_sig,
417
+ ciSymbol* int_sig,
418
+ ciSymbol* char_sig) {
419
+ if (cnode->method () != nullptr && !cnode->method ()->is_static () &&
420
+ cnode->method ()->holder () == m->holder () &&
421
+ cnode->method ()->name () == ciSymbols::append_name () &&
422
+ (cnode->method ()->signature ()->as_symbol () == string_sig ||
423
+ cnode->method ()->signature ()->as_symbol () == char_sig ||
424
+ cnode->method ()->signature ()->as_symbol () == int_sig)) {
425
+ if (sc->has_control (cnode)) {
426
+ return ProcessAppendResult::AppendWasAdded;
427
+ }
428
+ sc->add_control (cnode);
429
+ Node* arg = cnode->in (TypeFunc::Parms + 1 );
430
+ if (arg == nullptr || arg->is_top ()) {
431
+ #ifndef PRODUCT
432
+ if (PrintOptimizeStringConcat) {
433
+ tty->print (" giving up because the call is effectively dead" );
434
+ cnode->jvms ()->dump_spec (tty);
435
+ tty->cr ();
436
+ }
437
+ #endif
438
+ return ProcessAppendResult::AbortOptimization;
439
+ }
440
+
441
+ if (cnode->method ()->signature ()->as_symbol () == int_sig) {
442
+ sc->push_int (arg);
443
+ } else if (cnode->method ()->signature ()->as_symbol () == char_sig) {
444
+ sc->push_char (arg);
445
+ } else if (arg->is_Proj () && arg->in (0 )->is_CallStaticJava ()) {
446
+ CallStaticJavaNode* csj = arg->in (0 )->as_CallStaticJava ();
447
+ if (csj->method () != nullptr &&
448
+ csj->method ()->intrinsic_id () == vmIntrinsics::_Integer_toString &&
449
+ arg->outcnt () == 1 ) {
450
+ // _control is the list of StringBuilder calls nodes which
451
+ // will be replaced by new String code after this optimization.
452
+ // Integer::toString() call is not part of StringBuilder calls
453
+ // chain. It could be eliminated only if its result is used
454
+ // only by this SB calls chain.
455
+ // Another limitation: it should be used only once because
456
+ // it is unknown that it is used only by this SB calls chain
457
+ // until all related SB calls nodes are collected.
458
+ assert (arg->unique_out () == cnode, " sanity" );
459
+ sc->add_control (csj);
460
+ sc->push_int (csj->in (TypeFunc::Parms));
461
+ } else {
462
+ sc->push_string (arg);
463
+ }
464
+ } else {
465
+ sc->push_string (arg);
466
+ }
467
+ return ProcessAppendResult::AppendWasAdded;
468
+ }
469
+ return ProcessAppendResult::CandidateIsNotAppend;
470
+ }
471
+
472
+ // Recognize fluent-chain and non-fluent uses of StringBuilder/Buffer. They are either explicit usages
411
473
// of them or the legacy bytecodes of string concatenation prior to JEP-280. eg.
412
474
//
413
475
// String result = new StringBuilder()
@@ -416,18 +478,17 @@ Node_List PhaseStringOpts::collect_toString_calls() {
416
478
// .append(123)
417
479
// .toString(); // "foobar123"
418
480
//
419
- // PS: Only a certain subset of constructor and append methods are acceptable .
420
- // The criterion is that the length of argument is easy to work out in this phrase.
421
- // It will drop complex cases such as Object.
481
+ // Fluent-chains are recognized by walking upwards along the receivers, starting from toString() .
482
+ // Once the allocation of the StringBuilder has been reached, DU pairs are examined to find the
483
+ // constructor and non-fluent uses of the StringBuilder such as in this example:
422
484
//
423
- // Since it walks along the receivers of fluent-chain, it will give up if the codeshape is
424
- // not "fluent" enough. eg.
425
485
// StringBuilder sb = new StringBuilder();
426
486
// sb.append("foo");
427
487
// sb.toString();
428
488
//
429
- // The receiver of toString method is the result of Allocation Node(CheckCastPP).
430
- // The append method is overlooked. It will fail at validate_control_flow() test.
489
+ // PS: Only a certain subset of constructor and append methods are acceptable.
490
+ // The criterion is that the length of argument is easy to work out in this phrase.
491
+ // It will drop complex cases such as Object.
431
492
//
432
493
StringConcat* PhaseStringOpts::build_candidate (CallStaticJavaNode* call) {
433
494
ciMethod* m = call->method ();
@@ -466,7 +527,7 @@ StringConcat* PhaseStringOpts::build_candidate(CallStaticJavaNode* call) {
466
527
if (cnode == nullptr ) {
467
528
alloc = recv->isa_Allocate ();
468
529
if (alloc == nullptr ) {
469
- break ;
530
+ return nullptr ;
470
531
}
471
532
// Find the constructor call
472
533
Node* result = alloc->result_cast ();
@@ -478,7 +539,7 @@ StringConcat* PhaseStringOpts::build_candidate(CallStaticJavaNode* call) {
478
539
alloc->jvms ()->dump_spec (tty); tty->cr ();
479
540
}
480
541
#endif
481
- break ;
542
+ return nullptr ;
482
543
}
483
544
Node* constructor = nullptr ;
484
545
for (SimpleDUIterator i (result); i.has_next (); i.next ()) {
@@ -489,6 +550,10 @@ StringConcat* PhaseStringOpts::build_candidate(CallStaticJavaNode* call) {
489
550
use->method ()->name () == ciSymbols::object_initializer_name () &&
490
551
use->method ()->holder () == m->holder ()) {
491
552
// Matched the constructor.
553
+ if (constructor != nullptr ) {
554
+ // The constructor again. We must only process it once.
555
+ continue ;
556
+ }
492
557
ciSymbol* sig = use->method ()->signature ()->as_symbol ();
493
558
if (sig == ciSymbols::void_method_signature () ||
494
559
sig == ciSymbols::int_void_signature () ||
@@ -542,7 +607,16 @@ StringConcat* PhaseStringOpts::build_candidate(CallStaticJavaNode* call) {
542
607
}
543
608
#endif
544
609
}
545
- break ;
610
+ } else if (use != nullptr ) {
611
+ if (process_append_candidate (use, sc, m, string_sig, int_sig, char_sig) == ProcessAppendResult::AbortOptimization) {
612
+ // We must abort if process_append_candidate tells us to...
613
+ return nullptr ;
614
+ }
615
+ // ...but we do not care if we really found an append or not:
616
+ // - If we found an append, that's perfect. Nothing further to do.
617
+ // - If this is a call to an unrelated method, validate_mem_flow() (and validate_control_flow())
618
+ // will later check if this call prevents the optimization. So nothing to do here.
619
+ // We will continue to look for the constructor (if not found already) and appends.
546
620
}
547
621
}
548
622
if (constructor == nullptr ) {
@@ -553,7 +627,7 @@ StringConcat* PhaseStringOpts::build_candidate(CallStaticJavaNode* call) {
553
627
alloc->jvms ()->dump_spec (tty); tty->cr ();
554
628
}
555
629
#endif
556
- break ;
630
+ return nullptr ;
557
631
}
558
632
559
633
// Walked all the way back and found the constructor call so see
@@ -568,62 +642,23 @@ StringConcat* PhaseStringOpts::build_candidate(CallStaticJavaNode* call) {
568
642
} else {
569
643
return nullptr ;
570
644
}
571
- } else if (cnode->method () == nullptr ) {
572
- break ;
573
- } else if (!cnode->method ()->is_static () &&
574
- cnode->method ()->holder () == m->holder () &&
575
- cnode->method ()->name () == ciSymbols::append_name () &&
576
- (cnode->method ()->signature ()->as_symbol () == string_sig ||
577
- cnode->method ()->signature ()->as_symbol () == char_sig ||
578
- cnode->method ()->signature ()->as_symbol () == int_sig)) {
579
- sc->add_control (cnode);
580
- Node* arg = cnode->in (TypeFunc::Parms + 1 );
581
- if (arg == nullptr || arg->is_top ()) {
645
+ } else {
646
+ ProcessAppendResult result = process_append_candidate (cnode, sc, m, string_sig, int_sig, char_sig);
647
+
648
+ if (result == ProcessAppendResult::AbortOptimization) {
649
+ return nullptr ;
650
+ } else if (result == ProcessAppendResult::CandidateIsNotAppend) {
651
+ // some unhandled signature
582
652
#ifndef PRODUCT
583
653
if (PrintOptimizeStringConcat) {
584
- tty->print (" giving up because the call is effectively dead" );
585
- cnode->jvms ()->dump_spec (tty); tty->cr ();
654
+ tty->print (" giving up because encountered unexpected signature " );
655
+ cnode->tf ()->dump ();
656
+ tty->cr ();
657
+ cnode->in (TypeFunc::Parms + 1 )->dump ();
586
658
}
587
659
#endif
588
- break ;
589
- }
590
- if (cnode->method ()->signature ()->as_symbol () == int_sig) {
591
- sc->push_int (arg);
592
- } else if (cnode->method ()->signature ()->as_symbol () == char_sig) {
593
- sc->push_char (arg);
594
- } else {
595
- if (arg->is_Proj () && arg->in (0 )->is_CallStaticJava ()) {
596
- CallStaticJavaNode* csj = arg->in (0 )->as_CallStaticJava ();
597
- if (csj->method () != nullptr &&
598
- csj->method ()->intrinsic_id () == vmIntrinsics::_Integer_toString &&
599
- arg->outcnt () == 1 ) {
600
- // _control is the list of StringBuilder calls nodes which
601
- // will be replaced by new String code after this optimization.
602
- // Integer::toString() call is not part of StringBuilder calls
603
- // chain. It could be eliminated only if its result is used
604
- // only by this SB calls chain.
605
- // Another limitation: it should be used only once because
606
- // it is unknown that it is used only by this SB calls chain
607
- // until all related SB calls nodes are collected.
608
- assert (arg->unique_out () == cnode, " sanity" );
609
- sc->add_control (csj);
610
- sc->push_int (csj->in (TypeFunc::Parms));
611
- continue ;
612
- }
613
- }
614
- sc->push_string (arg);
615
- }
616
- continue ;
617
- } else {
618
- // some unhandled signature
619
- #ifndef PRODUCT
620
- if (PrintOptimizeStringConcat) {
621
- tty->print (" giving up because encountered unexpected signature " );
622
- cnode->tf ()->dump (); tty->cr ();
623
- cnode->in (TypeFunc::Parms + 1 )->dump ();
660
+ return nullptr ;
624
661
}
625
- #endif
626
- break ;
627
662
}
628
663
}
629
664
return nullptr ;
0 commit comments