Mercurial > hg > graal-jvmci-8
comparison src/share/vm/opto/stringopts.cpp @ 12889:90abdd727e64
8009303: Tiered: incorrect results in VM tests stringconcat with -Xcomp -XX:+DeoptimizeALot on solaris-amd64
Summary: Do memory flow analysis in string concat optimizier to exclude cases when computation of arguments to StringBuffer::append has side effects
Reviewed-by: kvn, twisti
author | iveresov |
---|---|
date | Wed, 16 Oct 2013 11:13:15 -0700 |
parents | d092d1b31229 |
children | 2113136690bc |
comparison
equal
deleted
inserted
replaced
12888:4a2acfb16e97 | 12889:90abdd727e64 |
---|---|
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(); |