--- old/src/share/vm/opto/stringopts.cpp 2013-10-10 15:06:31.000000000 -0700 +++ new/src/share/vm/opto/stringopts.cpp 2013-10-10 15:06:31.000000000 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -42,6 +42,7 @@ PhaseStringOpts* _stringopts; Node* _string_alloc; AllocateNode* _begin; // The allocation the begins the pattern + Node* _constructor; // Constructor of StringBuffer CallStaticJavaNode* _end; // The final call of the pattern. Will either be // SB.toString or or String.(SB.toString) bool _multiple; // indicates this is a fusion of two or more @@ -54,6 +55,7 @@ Node_List _control; // List of control nodes that will be deleted Node_List _uncommon_traps; // Uncommon traps that needs to be rewritten // to restart at the initial JVMState. + public: // Mode for converting arguments to Strings enum { @@ -73,6 +75,7 @@ _arguments->del_req(0); } + bool validate_mem_flow(); bool validate_control_flow(); void merge_add() { @@ -103,7 +106,9 @@ void set_allocation(AllocateNode* alloc) { _begin = alloc; } - + void set_constructor(Node* init) { + _constructor = init; + } void append(Node* value, int mode) { _arguments->add_req(value); _mode.append(mode); @@ -510,7 +515,8 @@ sc->add_control(constructor); sc->add_control(alloc); sc->set_allocation(alloc); - if (sc->validate_control_flow()) { + sc->set_constructor(constructor); + if (sc->validate_control_flow() && sc->validate_mem_flow()) { return sc; } else { return NULL; @@ -571,7 +577,8 @@ PhaseStringOpts::PhaseStringOpts(PhaseGVN* gvn, Unique_Node_List*): Phase(StringOpts), _gvn(gvn), - _visited(Thread::current()->resource_area()) { + _visited(Thread::current()->resource_area()), + _path(memory_flow_iteration_limit) { assert(OptimizeStringConcat, "shouldn't be here"); @@ -620,7 +627,7 @@ #endif StringConcat* merged = sc->merge(other, arg); - if (merged->validate_control_flow()) { + if (merged->validate_control_flow() && merged->validate_mem_flow()) { #ifndef PRODUCT if (PrintOptimizeStringConcat) { tty->print_cr("stacking would succeed"); @@ -707,6 +714,135 @@ } } +// Find all simple paths from start_mem to any node in end_set, check if any of the paths +// has side effects. +bool StringConcat::validate_mem_flow() { + Node* start_mem = _end; + Node* end_mem = _constructor; + assert(start_mem->is_Call() && end_mem->is_Call(), "precondition"); + assert(_control.contains(start_mem) && _control.contains(end_mem), "precondition"); + + Node_Stack& path = _stringopts->_path; + path.clear(); + + // Interested in simple paths, so we'll track the nodes visited + VectorSet& visited = _stringopts->_visited; + visited.Clear(); + + Compile* C = _stringopts->C; + path.push(start_mem, 0); + visited.set(start_mem->_idx); + visited.set(C->top()->_idx); + + int nodes_processed = 0; + // Do DFS up the memory graph, storing the result in path, + // use next_edge to track the last edge taken for the node. + while (path.is_nonempty()) { + if (nodes_processed++ > PhaseStringOpts::memory_flow_iteration_limit) { + // We're spending too much time here, going to be pessimistic + // and say we have found a side effect +#ifndef PRODUCT + if (PrintOptimizeStringConcat) { + tty->print_cr("Giving up on memory flow analysis due to iteration cutoff"); + } +#endif + return false; + } + + Node* curr = path.node(); + uint edge_idx = path.index(); + bool is_not_visited = (edge_idx == 0); + Node* next = NULL; + + if (curr->is_Proj()) { + assert(curr->as_Proj()->_con == TypeFunc::Memory, "must be a memory projection"); + if (is_not_visited) { + next = curr->in(0); + } + } else if (curr->is_Mem()) { + if (curr->is_Store() || curr->is_LoadStore()) { +#ifndef PRODUCT + if (PrintOptimizeStringConcat) { + ttyLocker ttyl; + tty->print("fusion has incorrect memory flow (side effects) for "); + _begin->jvms()->dump_spec(tty); tty->cr(); + for (uint i = 0; i < path.size(); i++) { + path.node_at(i)->dump(); + } + tty->cr(); + } +#endif // !PRODUCT + return false; + } + if (is_not_visited) { + next = curr->in(MemNode::Memory); + assert(next != NULL, "should have memory edge"); + } + } else if (curr->is_Call()) { + if (is_not_visited) { + next = curr->in(TypeFunc::Memory); + assert(next != NULL, "should have memory edge"); + } + if (curr == end_mem) { + // Terminate this search branch + next = NULL; + } + } else if (curr->is_MergeMem()) { + while (edge_idx + 1 < curr->req() && next == NULL) { + next = curr->in(edge_idx + 1); + bool dup = false; + if (next != NULL) { + // skip duplicate edges + for (uint i = 1; i < edge_idx + 1; i++) { + if (next == curr->in(i)) { + edge_idx++; + next = NULL; + break; + } + } + } else { + edge_idx++; + } + } + } else if (curr->is_MemBar()) { + if (edge_idx < 1) { + next = curr->in(TypeFunc::Memory); + assert(next != NULL, "should have memory edge"); + } else if (curr->is_Initialize() && edge_idx < 2) { + next = curr->as_Initialize()->allocation(); + } + } else { + assert(false, err_msg_res("Unexpected node type: %s", curr->Name())); + return false; + } + edge_idx++; + + if (next == NULL) { + // Can't go any further, need to pop off the node + visited.remove(curr->_idx); + path.pop(); + } else { + // Update the last visited edge number + path.set_index(edge_idx); + // Check if the next node was already visited + if (!visited.test_set(next->_idx)) { + // If not add it to the path + path.push(next, 0); + } + } + } + +#ifndef PRODUCT + if (PrintOptimizeStringConcat) { + tty->print("fusion has correct memory flow for "); + _begin->jvms()->dump_spec(tty); tty->cr(); + tty->cr(); + } +#endif + + // No interesing paths + return true; +} bool StringConcat::validate_control_flow() { // We found all the calls and arguments now lets see if it's @@ -936,7 +1072,7 @@ if (PrintOptimizeStringConcat && !fail) { ttyLocker ttyl; tty->cr(); - tty->print("fusion would succeed (%d %d) for ", null_check_count, _uncommon_traps.size()); + tty->print("fusion has correct control flow (%d %d) for ", null_check_count, _uncommon_traps.size()); _begin->jvms()->dump_spec(tty); tty->cr(); for (int i = 0; i < num_arguments(); i++) { argument(i)->dump();