/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
#include "precompiled.hpp"
#include "compiler/compileLog.hpp"
#include "opto/addnode.hpp"
#include "opto/callGenerator.hpp"
#include "opto/callnode.hpp"
#include "opto/divnode.hpp"
#include "opto/graphKit.hpp"
#include "opto/idealKit.hpp"
#include "opto/rootnode.hpp"
#include "opto/runtime.hpp"
#include "opto/stringopts.hpp"
#include "opto/subnode.hpp"
private:
// separate StringBuilders
// indicating how to treat the value.
// to restart at the initial JVMState.
public:
// Mode for converting arguments to Strings
enum {
};
_multiple(false),
_arguments->del_req(0);
}
bool validate_control_flow();
void merge_add() {
#if 0
// XXX This is place holder code for reusing an existing String
// allocation but the logic for checking the state safety is
// probably inadequate at the moment.
// Found useless new String(sb.toString()) so reuse the newly allocated String
// when creating the result instead of allocating a new one.
}
}
}
#endif
}
}
}
}
}
}
}
}
if (call->is_CallStaticJava()) {
if (m != NULL &&
return true;
}
}
return false;
}
// Look for a diamond shaped Null check of toString() result
// (could be code from String.valueOf()):
// (Proj == NULL) ? "null":"CastPP(Proj)#NotNULL
if (true_path != 0) {
// phi->region->if_proj->ifnode->bool
// Null check of the return of toString which can simply be skipped.
return v1;
}
}
}
return value;
}
return _arguments->in(i);
}
}
return arg;
}
}
int num_arguments() {
}
int mode(int i) {
}
}
void eliminate_unneeded_control();
void maybe_log_transform() {
_string_alloc != NULL,
while (p != NULL) {
p = p->caller();
}
}
}
// Build a new call using the jvms state of the allocate
Compile* C = _stringopts->C;
}
// Set the trap request to record intrinsic failure if this trap
// is taken too many times. Ideally we would handle then traps by
// doing the original bookkeeping in the MDO so that if it caused
// the code to be thrown out we could still recompile and use the
// optimization. Failing the uncommon traps doesn't really mean
// that the optimization is a bad idea but there's no other way to
// do the MDO updates currently.
}
}
void cleanup() {
// disconnect the hook node
}
};
if (n->is_Allocate()) {
}
if (n->is_Call()) {
if (n != _end) {
eliminate_call(n->as_Call());
}
} else if (n->is_IfTrue()) {
Compile* C = _stringopts->C;
// get rid of the other projection
}
}
}
if (n->is_Call()) {
}
}
if (n->is_Call()) {
}
}
for (int x = 0; x < num_arguments(); x++) {
// replace the toString result with the all the arguments that
// made up the other StringConcat
for (int y = 0; y < other->num_arguments(); y++) {
}
} else {
}
}
return result;
}
Compile* C = _stringopts->C;
}
}
}
}
}
// EA can't cope with the partially collapsed graph this
// creates so put it on the worklist to be collapsed later.
}
}
}
}
}
Compile* C = _stringopts->C;
// Eliminate Initialize node.
}
}
}
// Prime the worklist
}
}
}
}
}
}
}
}
return string_calls;
}
} else {
return NULL;
}
#ifndef PRODUCT
if (PrintOptimizeStringConcat) {
}
#endif
// possible opportunity for StringBuilder fusion
while (cnode) {
}
break;
}
// Find the constructor call
// strange looking allocation
#ifndef PRODUCT
if (PrintOptimizeStringConcat) {
}
#endif
break;
}
// Matched the constructor.
// StringBuilder(String) so pick this up as the first argument
// StringBuilder(null) throws exception.
#ifndef PRODUCT
if (PrintOptimizeStringConcat) {
}
#endif
return NULL;
}
// StringBuilder(str) argument needs null check.
}
// The int variant takes an initial size for the backing
// array so just treat it like the void version.
constructor = use;
} else {
#ifndef PRODUCT
if (PrintOptimizeStringConcat) {
}
#endif
}
break;
}
}
if (constructor == NULL) {
// couldn't find constructor
#ifndef PRODUCT
if (PrintOptimizeStringConcat) {
}
#endif
break;
}
// Walked all the way back and found the constructor call so see
// if this call converted into a direct string concatenation.
if (sc->validate_control_flow()) {
return sc;
} else {
return NULL;
}
break;
} else {
// _control is the list of StringBuilder calls nodes which
// will be replaced by new String code after this optimization.
// Integer::toString() call is not part of StringBuilder calls
// chain. It could be eliminated only if its result is used
// only by this SB calls chain.
// Another limitation: it should be used only once because
// it is unknown that it is used only by this SB calls chain
// until all related SB calls nodes are collected.
continue;
}
}
}
continue;
} else {
// some unhandled signature
#ifndef PRODUCT
if (PrintOptimizeStringConcat) {
}
#endif
break;
}
}
return NULL;
}
if (size_table_field == NULL) {
// Something wrong so give up.
return;
}
// Collect the types needed to talk about the various slices of memory
// For each locally allocated StringBuffer see if the usages can be
// collapsed into a single String construction.
// Run through the list of allocation looking for SB.toString to see
// if it's possible to fuse the usage of the SB into a single String
// construction.
}
}
// try to coalesce separate concats
for (int i = 0; i < sc->num_arguments(); i++) {
if (c == o) continue;
#ifndef PRODUCT
if (PrintOptimizeStringConcat) {
}
#endif
if (merged->validate_control_flow()) {
#ifndef PRODUCT
if (PrintOptimizeStringConcat) {
}
#endif
if (c < o) {
} else {
}
goto restart;
} else {
#ifndef PRODUCT
if (PrintOptimizeStringConcat) {
}
#endif
}
}
}
}
}
}
}
}
}
// Delete any dead nodes to make things clean enough that escape
// analysis doesn't get unhappy.
while (dead_worklist.size() > 0) {
switch (opc) {
case Op_Region: {
uint i = 1;
break;
}
}
if (m->is_Phi()) {
dead_worklist.push(m);
}
}
}
break;
}
case Op_AddP:
case Op_CreateEx: {
// Recurisvely clean up references to CreateEx so EA doesn't
// get unhappy about the partially collapsed graph.
if (m->is_AddP()) {
dead_worklist.push(m);
}
}
break;
}
case Op_Phi:
}
break;
}
}
}
// We found all the calls and arguments now lets see if it's
// safe to transform the graph as we would expect.
// Check to see if this resulted in too many uncommon traps previously
return false;
}
// Walk backwards over the control flow from toString to the
// allocation and make sure all the control flow is ok. This
// means it's either going to be eliminated once the calls are
// removed or it can safely be transformed into an uncommon
// trap.
int null_check_count = 0;
// Collect the nodes that we know about and will eliminate into ctrl_path
// Push the call and it's control projection
if (n->is_Allocate()) {
}
if (n->is_Call()) {
}
} else {
}
}
// Skip backwards through the control checking for unexpected contro flow
bool fail = false;
if (b == NULL) {
fail = true;
break;
}
// Null check of the return of append which can simply be eliminated
// NULL check of the return value of the append
}
}
continue;
}
// A test which leads to an uncommon trap which should be safe.
// Later this trap will be converted into a trap that restarts
// at the beginning.
// control flow leads to uct so should be ok
continue;
}
}
#ifndef PRODUCT
// Some unexpected control flow we don't know how to handle.
if (PrintOptimizeStringConcat) {
b->dump();
}
#endif
fail = true;
break;
continue;
}
// Simple diamond.
// XXX should check for possibly merging stores. simple data merges are ok.
// The IGVN will make this simple diamond go away when it
// transforms the Region. Make sure it sees it.
continue;
}
#ifndef PRODUCT
if (PrintOptimizeStringConcat) {
}
#endif
fail = true;
break;
} else {
// other unknown control
if (!fail) {
#ifndef PRODUCT
if (PrintOptimizeStringConcat) {
}
#endif
fail = true;
}
#ifndef PRODUCT
if (PrintOptimizeStringConcat) {
}
#endif
}
}
#ifndef PRODUCT
if (PrintOptimizeStringConcat && fail) {
}
#endif
// Validate that all these results produced are contained within
// this cluster of objects. First collect all the results produced
// by calls in the region.
}
}
}
continue;
// already checked this
continue;
}
continue;
}
}
continue;
}
#ifndef PRODUCT
if (PrintOptimizeStringConcat) {
if (result != last_result) {
last_result->dump();
}
}
#endif
fail = true;
break;
}
}
#ifndef PRODUCT
if (PrintOptimizeStringConcat && !fail) {
for (int i = 0; i < num_arguments(); i++) {
}
}
#endif
return !fail;
}
} else if (field->is_constant()) {
// This can happen if the constant oop is non-perm.
// Do not "join" in the previous type; it doesn't add value,
// and may yield a vacuous result if the field is of interface type.
} else {
}
} else {
}
}
} else {
// int size = (i < 0) ? stringSize(-i) + 1 : stringSize(i);
kit.set_control(r);
C->record_for_igvn(r);
C->record_for_igvn(phi);
C->record_for_igvn(size);
// for (int i=0; ; i++)
// if (x <= sizeTable[i])
// return i+1;
// Add loop predicate first.
kit.add_predicate();
C->record_for_igvn(value);
C->record_for_igvn(loop);
C->record_for_igvn(index);
}
return final_size;
}
void PhaseStringOpts::int_getChars(GraphKit& kit, Node* arg, Node* char_array, Node* start, Node* end) {
Node *final_mem = PhiNode::make(final_merge, kit.memory(char_adr_idx), Type::MEMORY, TypeAryPtr::CHARS);
// need to handle Integer.MIN_VALUE specially because negating doesn't make it positive
{
// i == MIN_VALUE
// Statically not equal to MIN_VALUE so this path is dead
} else {
char_array, start);
}
}
// Simplified version of Integer.getChars
// int q, r;
// int charPos = index;
// char sign = 0;
// if (i < 0) {
// sign = '-';
// i = -i;
// }
{
C->record_for_igvn(merge);
C->record_for_igvn(i);
C->record_for_igvn(sign);
}
// for (;;) {
// q = i / 10;
// r = i - ((q << 3) + (q << 1)); // r = i-(q*10) ...
// buf [--charPos] = digits [r];
// i = q;
// if (i == 0) break;
// }
{
// Add loop predicate first.
kit.add_predicate();
C->record_for_igvn(head);
C->record_for_igvn(mem);
C->record_for_igvn(i_phi);
C->record_for_igvn(charPos);
}
{
// if (sign != 0) {
// buf [--charPos] = sign;
// }
} else {
}
C->record_for_igvn(final_mem);
}
}
// copy the contents
if (offset->is_Con() && count->is_Con() && value->is_Con() && count->get_int() < unroll_string_copy_length) {
// For small constant strings just emit individual stores.
for (int e = 0; e < c; e++) {
}
} else {
#ifdef _LP64
#endif
}
return start;
}
// Log a little info about the transformation
// pull the JVMState of the allocation into a SafePointNode to serve as
// as a shim for the insertion of the new code.
// copy the control and memory state from the final call into our
// new starting state. This allows any preceeding tests to feed
// into the new section of code.
}
// blow away old allocation arguments
}
// Copy the rest of the inputs for the JVMState
}
// Make sure the memory state is a MergeMem for parsing.
}
// disconnect all the old StringBuilder calls from the graph
// At this point all the old work has been completely removed from
// the graph and the saved JVMState exists at the point where the
// final toString call used to be.
// There may be uncommon traps which are still using the
// intermediate states and these need to be rewritten to point at
// the JVMState at the beginning of the transformation.
// Now insert the logic to compute the size of the string followed
// by all the logic to construct array and resulting string.
// Create a region for the overflow checks to merge into.
// Create a hook node to hold onto the individual sizes since they
// are need for the copying phase.
case StringConcat::IntMode: {
// accumulate total
// Cache this value for the use by int_toString
break;
}
case StringConcat::StringNullCheckMode: {
// Null check with uncommont trap since
// StringBuilder(null) throws exception.
// Use special uncommon trap instead of
// calling normal do_null_check().
}
// Fallthrough to add string length.
}
case StringConcat::StringMode: {
// replace the argument with the null checked version
arg = null_string;
// s = s != null ? s : "null";
// length = length + (s.count - s.offset);
kit.set_control(r);
C->record_for_igvn(r);
C->record_for_igvn(phi);
// replace the argument with the null checked version
}
break;
}
case StringConcat::CharMode: {
// one character only
break;
}
default:
}
if (argi > 0) {
// Check that the sum hasn't overflowed
}
}
{
// Hook
C->record_for_igvn(overflow);
}
// length now contains the number of characters needed for the
// char[] so create a new AllocateArray for the char[]
{
// The original jvms is for an allocation of either a String or
// StringBuffer so no stack adjustment is necessary for proper
// reexecution. If we deoptimize in the slow path the bytecode
// will be reexecuted and the char[] allocation will be thrown away.
length, 1);
}
// Mark the allocation so that zeroing is skipped since the code
// below will overwrite the entire array
// Now copy the string representations into the final char[]
case StringConcat::IntMode: {
// getChars words backwards so pass the ending point as well as the start
break;
}
case StringConcat::StringNullCheckMode:
case StringConcat::StringMode: {
break;
}
case StringConcat::CharMode: {
break;
}
default:
}
}
// If we're not reusing an existing String allocation then allocate one here.
// The original jvms is for an allocation of either a String or
// StringBuffer so no stack adjustment is necessary for proper
// reexecution.
}
// Intialize the string
if (java_lang_String::has_offset_field()) {
}
} else {
}
// hook up the outgoing control and result
// Unhook any hook nodes
}