changeset 20716 | 5093ad743df4 |
parent 15113 | 823590505eb4 |
child 22845 | d8812d0ff387 |
20715:be1135dc1406 | 20716:5093ad743df4 |
---|---|
1 /* |
1 /* |
2 * Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved. |
2 * Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved. |
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
4 * |
4 * |
5 * This code is free software; you can redistribute it and/or modify it |
5 * This code is free software; you can redistribute it and/or modify it |
6 * under the terms of the GNU General Public License version 2 only, as |
6 * under the terms of the GNU General Public License version 2 only, as |
7 * published by the Free Software Foundation. |
7 * published by the Free Software Foundation. |
48 // separate StringBuilders |
48 // separate StringBuilders |
49 |
49 |
50 Node* _arguments; // The list of arguments to be concatenated |
50 Node* _arguments; // The list of arguments to be concatenated |
51 GrowableArray<int> _mode; // into a String along with a mode flag |
51 GrowableArray<int> _mode; // into a String along with a mode flag |
52 // indicating how to treat the value. |
52 // indicating how to treat the value. |
53 |
53 Node_List _constructors; // List of constructors (many in case of stacked concat) |
54 Node_List _control; // List of control nodes that will be deleted |
54 Node_List _control; // List of control nodes that will be deleted |
55 Node_List _uncommon_traps; // Uncommon traps that needs to be rewritten |
55 Node_List _uncommon_traps; // Uncommon traps that needs to be rewritten |
56 // to restart at the initial JVMState. |
56 // to restart at the initial JVMState. |
57 |
|
57 public: |
58 public: |
58 // Mode for converting arguments to Strings |
59 // Mode for converting arguments to Strings |
59 enum { |
60 enum { |
60 StringMode, |
61 StringMode, |
61 IntMode, |
62 IntMode, |
71 _stringopts(stringopts) { |
72 _stringopts(stringopts) { |
72 _arguments = new (_stringopts->C) Node(1); |
73 _arguments = new (_stringopts->C) Node(1); |
73 _arguments->del_req(0); |
74 _arguments->del_req(0); |
74 } |
75 } |
75 |
76 |
77 bool validate_mem_flow(); |
|
76 bool validate_control_flow(); |
78 bool validate_control_flow(); |
77 |
79 |
78 void merge_add() { |
80 void merge_add() { |
79 #if 0 |
81 #if 0 |
80 // XXX This is place holder code for reusing an existing String |
82 // XXX This is place holder code for reusing an existing String |
187 } |
189 } |
188 void add_control(Node* ctrl) { |
190 void add_control(Node* ctrl) { |
189 assert(!_control.contains(ctrl), "only push once"); |
191 assert(!_control.contains(ctrl), "only push once"); |
190 _control.push(ctrl); |
192 _control.push(ctrl); |
191 } |
193 } |
194 void add_constructor(Node* init) { |
|
195 assert(!_constructors.contains(init), "only push once"); |
|
196 _constructors.push(init); |
|
197 } |
|
192 CallStaticJavaNode* end() { return _end; } |
198 CallStaticJavaNode* end() { return _end; } |
193 AllocateNode* begin() { return _begin; } |
199 AllocateNode* begin() { return _begin; } |
194 Node* string_alloc() { return _string_alloc; } |
200 Node* string_alloc() { return _string_alloc; } |
195 |
201 |
196 void eliminate_unneeded_control(); |
202 void eliminate_unneeded_control(); |
299 } else { |
305 } else { |
300 result->append(argx, mode(x)); |
306 result->append(argx, mode(x)); |
301 } |
307 } |
302 } |
308 } |
303 result->set_allocation(other->_begin); |
309 result->set_allocation(other->_begin); |
310 for (uint i = 0; i < _constructors.size(); i++) { |
|
311 result->add_constructor(_constructors.at(i)); |
|
312 } |
|
313 for (uint i = 0; i < other->_constructors.size(); i++) { |
|
314 result->add_constructor(other->_constructors.at(i)); |
|
315 } |
|
304 result->_multiple = true; |
316 result->_multiple = true; |
305 return result; |
317 return result; |
306 } |
318 } |
307 |
319 |
308 |
320 |
508 // if this call converted into a direct string concatenation. |
520 // if this call converted into a direct string concatenation. |
509 sc->add_control(call); |
521 sc->add_control(call); |
510 sc->add_control(constructor); |
522 sc->add_control(constructor); |
511 sc->add_control(alloc); |
523 sc->add_control(alloc); |
512 sc->set_allocation(alloc); |
524 sc->set_allocation(alloc); |
513 if (sc->validate_control_flow()) { |
525 sc->add_constructor(constructor); |
526 if (sc->validate_control_flow() && sc->validate_mem_flow()) { |
|
514 return sc; |
527 return sc; |
515 } else { |
528 } else { |
516 return NULL; |
529 return NULL; |
517 } |
530 } |
518 } else if (cnode->method() == NULL) { |
531 } else if (cnode->method() == NULL) { |
618 tty->print_cr("considering stacked concats"); |
631 tty->print_cr("considering stacked concats"); |
619 } |
632 } |
620 #endif |
633 #endif |
621 |
634 |
622 StringConcat* merged = sc->merge(other, arg); |
635 StringConcat* merged = sc->merge(other, arg); |
623 if (merged->validate_control_flow()) { |
636 if (merged->validate_control_flow() && merged->validate_mem_flow()) { |
624 #ifndef PRODUCT |
637 #ifndef PRODUCT |
625 if (PrintOptimizeStringConcat) { |
638 if (PrintOptimizeStringConcat) { |
626 tty->print_cr("stacking would succeed"); |
639 tty->print_cr("stacking would succeed"); |
627 } |
640 } |
628 #endif |
641 #endif |
706 } |
719 } |
707 } |
720 } |
708 } |
721 } |
709 |
722 |
710 |
723 |
724 bool StringConcat::validate_mem_flow() { |
|
725 Compile* C = _stringopts->C; |
|
726 |
|
727 for (uint i = 0; i < _control.size(); i++) { |
|
728 #ifndef PRODUCT |
|
729 Node_List path; |
|
730 #endif |
|
731 Node* curr = _control.at(i); |
|
732 if (curr->is_Call() && curr != _begin) { // For all calls except the first allocation |
|
733 // Now here's the main invariant in our case: |
|
734 // For memory between the constructor, and appends, and toString we should only see bottom memory, |
|
735 // produced by the previous call we know about. |
|
736 if (!_constructors.contains(curr)) { |
|
737 NOT_PRODUCT(path.push(curr);) |
|
738 Node* mem = curr->in(TypeFunc::Memory); |
|
739 assert(mem != NULL, "calls should have memory edge"); |
|
740 assert(!mem->is_Phi(), "should be handled by control flow validation"); |
|
741 NOT_PRODUCT(path.push(mem);) |
|
742 while (mem->is_MergeMem()) { |
|
743 for (uint i = 1; i < mem->req(); i++) { |
|
744 if (i != Compile::AliasIdxBot && mem->in(i) != C->top()) { |
|
745 #ifndef PRODUCT |
|
746 if (PrintOptimizeStringConcat) { |
|
747 tty->print("fusion has incorrect memory flow (side effects) for "); |
|
748 _begin->jvms()->dump_spec(tty); tty->cr(); |
|
749 path.dump(); |
|
750 } |
|
751 #endif |
|
752 return false; |
|
753 } |
|
754 } |
|
755 // skip through a potential MergeMem chain, linked through Bot |
|
756 mem = mem->in(Compile::AliasIdxBot); |
|
757 NOT_PRODUCT(path.push(mem);) |
|
758 } |
|
759 // now let it fall through, and see if we have a projection |
|
760 if (mem->is_Proj()) { |
|
761 // Should point to a previous known call |
|
762 Node *prev = mem->in(0); |
|
763 NOT_PRODUCT(path.push(prev);) |
|
764 if (!prev->is_Call() || !_control.contains(prev)) { |
|
765 #ifndef PRODUCT |
|
766 if (PrintOptimizeStringConcat) { |
|
767 tty->print("fusion has incorrect memory flow (unknown call) for "); |
|
768 _begin->jvms()->dump_spec(tty); tty->cr(); |
|
769 path.dump(); |
|
770 } |
|
771 #endif |
|
772 return false; |
|
773 } |
|
774 } else { |
|
775 assert(mem->is_Store() || mem->is_LoadStore(), err_msg_res("unexpected node type: %s", mem->Name())); |
|
776 #ifndef PRODUCT |
|
777 if (PrintOptimizeStringConcat) { |
|
778 tty->print("fusion has incorrect memory flow (unexpected source) for "); |
|
779 _begin->jvms()->dump_spec(tty); tty->cr(); |
|
780 path.dump(); |
|
781 } |
|
782 #endif |
|
783 return false; |
|
784 } |
|
785 } else { |
|
786 // For memory that feeds into constructors it's more complicated. |
|
787 // However the advantage is that any side effect that happens between the Allocate/Initialize and |
|
788 // the constructor will have to be control-dependent on Initialize. |
|
789 // So we actually don't have to do anything, since it's going to be caught by the control flow |
|
790 // analysis. |
|
791 #ifdef ASSERT |
|
792 // Do a quick verification of the control pattern between the constructor and the initialize node |
|
793 assert(curr->is_Call(), "constructor should be a call"); |
|
794 // Go up the control starting from the constructor call |
|
795 Node* ctrl = curr->in(0); |
|
796 IfNode* iff = NULL; |
|
797 RegionNode* copy = NULL; |
|
798 |
|
799 while (true) { |
|
800 // skip known check patterns |
|
801 if (ctrl->is_Region()) { |
|
802 if (ctrl->as_Region()->is_copy()) { |
|
803 copy = ctrl->as_Region(); |
|
804 ctrl = copy->is_copy(); |
|
805 } else { // a cast |
|
806 assert(ctrl->req() == 3 && |
|
807 ctrl->in(1) != NULL && ctrl->in(1)->is_Proj() && |
|
808 ctrl->in(2) != NULL && ctrl->in(2)->is_Proj() && |
|
809 ctrl->in(1)->in(0) == ctrl->in(2)->in(0) && |
|
810 ctrl->in(1)->in(0) != NULL && ctrl->in(1)->in(0)->is_If(), |
|
811 "must be a simple diamond"); |
|
812 Node* true_proj = ctrl->in(1)->is_IfTrue() ? ctrl->in(1) : ctrl->in(2); |
|
813 for (SimpleDUIterator i(true_proj); i.has_next(); i.next()) { |
|
814 Node* use = i.get(); |
|
815 assert(use == ctrl || use->is_ConstraintCast(), |
|
816 err_msg_res("unexpected user: %s", use->Name())); |
|
817 } |
|
818 |
|
819 iff = ctrl->in(1)->in(0)->as_If(); |
|
820 ctrl = iff->in(0); |
|
821 } |
|
822 } else if (ctrl->is_IfTrue()) { // null checks, class checks |
|
823 iff = ctrl->in(0)->as_If(); |
|
824 assert(iff->is_If(), "must be if"); |
|
825 // Verify that the other arm is an uncommon trap |
|
826 Node* otherproj = iff->proj_out(1 - ctrl->as_Proj()->_con); |
|
827 CallStaticJavaNode* call = otherproj->unique_out()->isa_CallStaticJava(); |
|
828 assert(strcmp(call->_name, "uncommon_trap") == 0, "must be uncommond trap"); |
|
829 ctrl = iff->in(0); |
|
830 } else { |
|
831 break; |
|
832 } |
|
833 } |
|
834 |
|
835 assert(ctrl->is_Proj(), "must be a projection"); |
|
836 assert(ctrl->in(0)->is_Initialize(), "should be initialize"); |
|
837 for (SimpleDUIterator i(ctrl); i.has_next(); i.next()) { |
|
838 Node* use = i.get(); |
|
839 assert(use == copy || use == iff || use == curr || use->is_CheckCastPP() || use->is_Load(), |
|
840 err_msg_res("unexpected user: %s", use->Name())); |
|
841 } |
|
842 #endif // ASSERT |
|
843 } |
|
844 } |
|
845 } |
|
846 |
|
847 #ifndef PRODUCT |
|
848 if (PrintOptimizeStringConcat) { |
|
849 tty->print("fusion has correct memory flow for "); |
|
850 _begin->jvms()->dump_spec(tty); tty->cr(); |
|
851 tty->cr(); |
|
852 } |
|
853 #endif |
|
854 return true; |
|
855 } |
|
856 |
|
711 bool StringConcat::validate_control_flow() { |
857 bool StringConcat::validate_control_flow() { |
712 // We found all the calls and arguments now lets see if it's |
858 // We found all the calls and arguments now lets see if it's |
713 // safe to transform the graph as we would expect. |
859 // safe to transform the graph as we would expect. |
714 |
860 |
715 // Check to see if this resulted in too many uncommon traps previously |
861 // Check to see if this resulted in too many uncommon traps previously |
751 } else { |
897 } else { |
752 ShouldNotReachHere(); |
898 ShouldNotReachHere(); |
753 } |
899 } |
754 } |
900 } |
755 |
901 |
756 // Skip backwards through the control checking for unexpected contro flow |
902 // Skip backwards through the control checking for unexpected control flow |
757 Node* ptr = _end; |
903 Node* ptr = _end; |
758 bool fail = false; |
904 bool fail = false; |
759 while (ptr != _begin) { |
905 while (ptr != _begin) { |
760 if (ptr->is_Call() && ctrl_path.member(ptr)) { |
906 if (ptr->is_Call() && ctrl_path.member(ptr)) { |
761 ptr = ptr->in(0); |
907 ptr = ptr->in(0); |
934 |
1080 |
935 #ifndef PRODUCT |
1081 #ifndef PRODUCT |
936 if (PrintOptimizeStringConcat && !fail) { |
1082 if (PrintOptimizeStringConcat && !fail) { |
937 ttyLocker ttyl; |
1083 ttyLocker ttyl; |
938 tty->cr(); |
1084 tty->cr(); |
939 tty->print("fusion would succeed (%d %d) for ", null_check_count, _uncommon_traps.size()); |
1085 tty->print("fusion has correct control flow (%d %d) for ", null_check_count, _uncommon_traps.size()); |
940 _begin->jvms()->dump_spec(tty); tty->cr(); |
1086 _begin->jvms()->dump_spec(tty); tty->cr(); |
941 for (int i = 0; i < num_arguments(); i++) { |
1087 for (int i = 0; i < num_arguments(); i++) { |
942 argument(i)->dump(); |
1088 argument(i)->dump(); |
943 } |
1089 } |
944 _control.dump(); |
1090 _control.dump(); |