Mercurial > hg > truffle
annotate src/share/vm/opto/stringopts.cpp @ 4710:41406797186b
7113012: G1: rename not-fully-young GCs as "mixed"
Summary: Renamed partially-young GCs as mixed and fully-young GCs as young. Change all external output that includes those terms (GC log and GC ergo log) as well as any comments, fields, methods, etc. The changeset also includes very minor code tidying up (added some curly brackets).
Reviewed-by: johnc, brutisso
author | tonyp |
---|---|
date | Fri, 16 Dec 2011 02:14:27 -0500 |
parents | 2b27ef5c2173 |
children | df3d4a91f7f6 |
rev | line source |
---|---|
1080 | 1 /* |
2426
1d1603768966
7010070: Update all 2010 Oracle-changed OpenJDK files to have the proper copyright dates - second pass
trims
parents:
2385
diff
changeset
|
2 * Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved. |
1080 | 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
4 * | |
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 | |
7 * published by the Free Software Foundation. | |
8 * | |
9 * This code is distributed in the hope that it will be useful, but WITHOUT | |
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
12 * version 2 for more details (a copy is included in the LICENSE file that | |
13 * accompanied this code). | |
14 * | |
15 * You should have received a copy of the GNU General Public License version | |
16 * 2 along with this work; if not, write to the Free Software Foundation, | |
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
18 * | |
1552
c18cbe5936b8
6941466: Oracle rebranding changes for Hotspot repositories
trims
parents:
1250
diff
changeset
|
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
c18cbe5936b8
6941466: Oracle rebranding changes for Hotspot repositories
trims
parents:
1250
diff
changeset
|
20 * or visit www.oracle.com if you need additional information or have any |
c18cbe5936b8
6941466: Oracle rebranding changes for Hotspot repositories
trims
parents:
1250
diff
changeset
|
21 * questions. |
1080 | 22 * |
23 */ | |
24 | |
1972 | 25 #include "precompiled.hpp" |
26 #include "compiler/compileLog.hpp" | |
27 #include "opto/addnode.hpp" | |
28 #include "opto/callGenerator.hpp" | |
29 #include "opto/callnode.hpp" | |
30 #include "opto/divnode.hpp" | |
31 #include "opto/graphKit.hpp" | |
32 #include "opto/idealKit.hpp" | |
33 #include "opto/rootnode.hpp" | |
34 #include "opto/runtime.hpp" | |
35 #include "opto/stringopts.hpp" | |
36 #include "opto/subnode.hpp" | |
1080 | 37 |
38 #define __ kit. | |
39 | |
40 class StringConcat : public ResourceObj { | |
41 private: | |
42 PhaseStringOpts* _stringopts; | |
43 Node* _string_alloc; | |
44 AllocateNode* _begin; // The allocation the begins the pattern | |
45 CallStaticJavaNode* _end; // The final call of the pattern. Will either be | |
46 // SB.toString or or String.<init>(SB.toString) | |
47 bool _multiple; // indicates this is a fusion of two or more | |
48 // separate StringBuilders | |
49 | |
50 Node* _arguments; // The list of arguments to be concatenated | |
51 GrowableArray<int> _mode; // into a String along with a mode flag | |
52 // indicating how to treat the value. | |
53 | |
54 Node_List _control; // List of control nodes that will be deleted | |
55 Node_List _uncommon_traps; // Uncommon traps that needs to be rewritten | |
56 // to restart at the initial JVMState. | |
57 public: | |
58 // Mode for converting arguments to Strings | |
59 enum { | |
60 StringMode, | |
61 IntMode, | |
2090
2ddb2fab82cb
7009359: HS with -XX:+AggressiveOpts optimize new StringBuffer(null) so it does not throw NPE as expected
kvn
parents:
1972
diff
changeset
|
62 CharMode, |
2ddb2fab82cb
7009359: HS with -XX:+AggressiveOpts optimize new StringBuffer(null) so it does not throw NPE as expected
kvn
parents:
1972
diff
changeset
|
63 StringNullCheckMode |
1080 | 64 }; |
65 | |
66 StringConcat(PhaseStringOpts* stringopts, CallStaticJavaNode* end): | |
67 _end(end), | |
68 _begin(NULL), | |
69 _multiple(false), | |
70 _string_alloc(NULL), | |
71 _stringopts(stringopts) { | |
72 _arguments = new (_stringopts->C, 1) Node(1); | |
73 _arguments->del_req(0); | |
74 } | |
75 | |
76 bool validate_control_flow(); | |
77 | |
78 void merge_add() { | |
79 #if 0 | |
80 // XXX This is place holder code for reusing an existing String | |
81 // allocation but the logic for checking the state safety is | |
82 // probably inadequate at the moment. | |
83 CallProjections endprojs; | |
84 sc->end()->extract_projections(&endprojs, false); | |
85 if (endprojs.resproj != NULL) { | |
86 for (SimpleDUIterator i(endprojs.resproj); i.has_next(); i.next()) { | |
87 CallStaticJavaNode *use = i.get()->isa_CallStaticJava(); | |
88 if (use != NULL && use->method() != NULL && | |
1817
c40600e85311
6986028: assert(_base == Int) failed: Not an Int in CmpINode::sub
never
parents:
1748
diff
changeset
|
89 use->method()->intrinsic_id() == vmIntrinsics::_String_String && |
1080 | 90 use->in(TypeFunc::Parms + 1) == endprojs.resproj) { |
91 // Found useless new String(sb.toString()) so reuse the newly allocated String | |
92 // when creating the result instead of allocating a new one. | |
93 sc->set_string_alloc(use->in(TypeFunc::Parms)); | |
94 sc->set_end(use); | |
95 } | |
96 } | |
97 } | |
98 #endif | |
99 } | |
100 | |
101 StringConcat* merge(StringConcat* other, Node* arg); | |
102 | |
103 void set_allocation(AllocateNode* alloc) { | |
104 _begin = alloc; | |
105 } | |
106 | |
107 void append(Node* value, int mode) { | |
108 _arguments->add_req(value); | |
109 _mode.append(mode); | |
110 } | |
111 void push(Node* value, int mode) { | |
112 _arguments->ins_req(0, value); | |
113 _mode.insert_before(0, mode); | |
114 } | |
115 void push_string(Node* value) { | |
116 push(value, StringMode); | |
117 } | |
2090
2ddb2fab82cb
7009359: HS with -XX:+AggressiveOpts optimize new StringBuffer(null) so it does not throw NPE as expected
kvn
parents:
1972
diff
changeset
|
118 void push_string_null_check(Node* value) { |
2ddb2fab82cb
7009359: HS with -XX:+AggressiveOpts optimize new StringBuffer(null) so it does not throw NPE as expected
kvn
parents:
1972
diff
changeset
|
119 push(value, StringNullCheckMode); |
2ddb2fab82cb
7009359: HS with -XX:+AggressiveOpts optimize new StringBuffer(null) so it does not throw NPE as expected
kvn
parents:
1972
diff
changeset
|
120 } |
1080 | 121 void push_int(Node* value) { |
122 push(value, IntMode); | |
123 } | |
124 void push_char(Node* value) { | |
125 push(value, CharMode); | |
126 } | |
127 | |
128 Node* argument(int i) { | |
129 return _arguments->in(i); | |
130 } | |
131 void set_argument(int i, Node* value) { | |
132 _arguments->set_req(i, value); | |
133 } | |
134 int num_arguments() { | |
135 return _mode.length(); | |
136 } | |
137 int mode(int i) { | |
138 return _mode.at(i); | |
139 } | |
140 void add_control(Node* ctrl) { | |
141 assert(!_control.contains(ctrl), "only push once"); | |
142 _control.push(ctrl); | |
143 } | |
144 CallStaticJavaNode* end() { return _end; } | |
145 AllocateNode* begin() { return _begin; } | |
146 Node* string_alloc() { return _string_alloc; } | |
147 | |
148 void eliminate_unneeded_control(); | |
149 void eliminate_initialize(InitializeNode* init); | |
150 void eliminate_call(CallNode* call); | |
151 | |
152 void maybe_log_transform() { | |
153 CompileLog* log = _stringopts->C->log(); | |
154 if (log != NULL) { | |
155 log->head("replace_string_concat arguments='%d' string_alloc='%d' multiple='%d'", | |
156 num_arguments(), | |
157 _string_alloc != NULL, | |
158 _multiple); | |
159 JVMState* p = _begin->jvms(); | |
160 while (p != NULL) { | |
161 log->elem("jvms bci='%d' method='%d'", p->bci(), log->identify(p->method())); | |
162 p = p->caller(); | |
163 } | |
164 log->tail("replace_string_concat"); | |
165 } | |
166 } | |
167 | |
168 void convert_uncommon_traps(GraphKit& kit, const JVMState* jvms) { | |
169 for (uint u = 0; u < _uncommon_traps.size(); u++) { | |
170 Node* uct = _uncommon_traps.at(u); | |
171 | |
172 // Build a new call using the jvms state of the allocate | |
1748 | 173 address call_addr = SharedRuntime::uncommon_trap_blob()->entry_point(); |
1080 | 174 const TypeFunc* call_type = OptoRuntime::uncommon_trap_Type(); |
175 int size = call_type->domain()->cnt(); | |
176 const TypePtr* no_memory_effects = NULL; | |
177 Compile* C = _stringopts->C; | |
178 CallStaticJavaNode* call = new (C, size) CallStaticJavaNode(call_type, call_addr, "uncommon_trap", | |
179 jvms->bci(), no_memory_effects); | |
180 for (int e = 0; e < TypeFunc::Parms; e++) { | |
181 call->init_req(e, uct->in(e)); | |
182 } | |
183 // Set the trap request to record intrinsic failure if this trap | |
184 // is taken too many times. Ideally we would handle then traps by | |
185 // doing the original bookkeeping in the MDO so that if it caused | |
186 // the code to be thrown out we could still recompile and use the | |
187 // optimization. Failing the uncommon traps doesn't really mean | |
188 // that the optimization is a bad idea but there's no other way to | |
189 // do the MDO updates currently. | |
190 int trap_request = Deoptimization::make_trap_request(Deoptimization::Reason_intrinsic, | |
191 Deoptimization::Action_make_not_entrant); | |
192 call->init_req(TypeFunc::Parms, __ intcon(trap_request)); | |
193 kit.add_safepoint_edges(call); | |
194 | |
195 _stringopts->gvn()->transform(call); | |
196 C->gvn_replace_by(uct, call); | |
197 uct->disconnect_inputs(NULL); | |
198 } | |
199 } | |
200 | |
201 void cleanup() { | |
202 // disconnect the hook node | |
203 _arguments->disconnect_inputs(NULL); | |
204 } | |
205 }; | |
206 | |
207 | |
208 void StringConcat::eliminate_unneeded_control() { | |
209 eliminate_initialize(begin()->initialization()); | |
210 for (uint i = 0; i < _control.size(); i++) { | |
211 Node* n = _control.at(i); | |
212 if (n->is_Call()) { | |
213 if (n != _end) { | |
214 eliminate_call(n->as_Call()); | |
215 } | |
216 } else if (n->is_IfTrue()) { | |
217 Compile* C = _stringopts->C; | |
218 C->gvn_replace_by(n, n->in(0)->in(0)); | |
219 C->gvn_replace_by(n->in(0), C->top()); | |
220 } | |
221 } | |
222 } | |
223 | |
224 | |
225 StringConcat* StringConcat::merge(StringConcat* other, Node* arg) { | |
226 StringConcat* result = new StringConcat(_stringopts, _end); | |
227 for (uint x = 0; x < _control.size(); x++) { | |
228 Node* n = _control.at(x); | |
229 if (n->is_Call()) { | |
230 result->_control.push(n); | |
231 } | |
232 } | |
233 for (uint x = 0; x < other->_control.size(); x++) { | |
234 Node* n = other->_control.at(x); | |
235 if (n->is_Call()) { | |
236 result->_control.push(n); | |
237 } | |
238 } | |
239 assert(result->_control.contains(other->_end), "what?"); | |
240 assert(result->_control.contains(_begin), "what?"); | |
241 for (int x = 0; x < num_arguments(); x++) { | |
242 if (argument(x) == arg) { | |
243 // replace the toString result with the all the arguments that | |
244 // made up the other StringConcat | |
245 for (int y = 0; y < other->num_arguments(); y++) { | |
246 result->append(other->argument(y), other->mode(y)); | |
247 } | |
248 } else { | |
249 result->append(argument(x), mode(x)); | |
250 } | |
251 } | |
252 result->set_allocation(other->_begin); | |
253 result->_multiple = true; | |
254 return result; | |
255 } | |
256 | |
257 | |
258 void StringConcat::eliminate_call(CallNode* call) { | |
259 Compile* C = _stringopts->C; | |
260 CallProjections projs; | |
261 call->extract_projections(&projs, false); | |
262 if (projs.fallthrough_catchproj != NULL) { | |
263 C->gvn_replace_by(projs.fallthrough_catchproj, call->in(TypeFunc::Control)); | |
264 } | |
265 if (projs.fallthrough_memproj != NULL) { | |
266 C->gvn_replace_by(projs.fallthrough_memproj, call->in(TypeFunc::Memory)); | |
267 } | |
268 if (projs.catchall_memproj != NULL) { | |
269 C->gvn_replace_by(projs.catchall_memproj, C->top()); | |
270 } | |
271 if (projs.fallthrough_ioproj != NULL) { | |
272 C->gvn_replace_by(projs.fallthrough_ioproj, call->in(TypeFunc::I_O)); | |
273 } | |
274 if (projs.catchall_ioproj != NULL) { | |
275 C->gvn_replace_by(projs.catchall_ioproj, C->top()); | |
276 } | |
277 if (projs.catchall_catchproj != NULL) { | |
278 // EA can't cope with the partially collapsed graph this | |
279 // creates so put it on the worklist to be collapsed later. | |
280 for (SimpleDUIterator i(projs.catchall_catchproj); i.has_next(); i.next()) { | |
281 Node *use = i.get(); | |
282 int opc = use->Opcode(); | |
283 if (opc == Op_CreateEx || opc == Op_Region) { | |
284 _stringopts->record_dead_node(use); | |
285 } | |
286 } | |
287 C->gvn_replace_by(projs.catchall_catchproj, C->top()); | |
288 } | |
289 if (projs.resproj != NULL) { | |
290 C->gvn_replace_by(projs.resproj, C->top()); | |
291 } | |
292 C->gvn_replace_by(call, C->top()); | |
293 } | |
294 | |
295 void StringConcat::eliminate_initialize(InitializeNode* init) { | |
296 Compile* C = _stringopts->C; | |
297 | |
298 // Eliminate Initialize node. | |
299 assert(init->outcnt() <= 2, "only a control and memory projection expected"); | |
300 assert(init->req() <= InitializeNode::RawStores, "no pending inits"); | |
301 Node *ctrl_proj = init->proj_out(TypeFunc::Control); | |
302 if (ctrl_proj != NULL) { | |
303 C->gvn_replace_by(ctrl_proj, init->in(TypeFunc::Control)); | |
304 } | |
305 Node *mem_proj = init->proj_out(TypeFunc::Memory); | |
306 if (mem_proj != NULL) { | |
307 Node *mem = init->in(TypeFunc::Memory); | |
308 C->gvn_replace_by(mem_proj, mem); | |
309 } | |
310 C->gvn_replace_by(init, C->top()); | |
311 init->disconnect_inputs(NULL); | |
312 } | |
313 | |
314 Node_List PhaseStringOpts::collect_toString_calls() { | |
315 Node_List string_calls; | |
316 Node_List worklist; | |
317 | |
318 _visited.Clear(); | |
319 | |
320 // Prime the worklist | |
321 for (uint i = 1; i < C->root()->len(); i++) { | |
322 Node* n = C->root()->in(i); | |
323 if (n != NULL && !_visited.test_set(n->_idx)) { | |
324 worklist.push(n); | |
325 } | |
326 } | |
327 | |
328 while (worklist.size() > 0) { | |
329 Node* ctrl = worklist.pop(); | |
330 if (ctrl->is_CallStaticJava()) { | |
331 CallStaticJavaNode* csj = ctrl->as_CallStaticJava(); | |
332 ciMethod* m = csj->method(); | |
333 if (m != NULL && | |
334 (m->intrinsic_id() == vmIntrinsics::_StringBuffer_toString || | |
335 m->intrinsic_id() == vmIntrinsics::_StringBuilder_toString)) { | |
336 string_calls.push(csj); | |
337 } | |
338 } | |
339 if (ctrl->in(0) != NULL && !_visited.test_set(ctrl->in(0)->_idx)) { | |
340 worklist.push(ctrl->in(0)); | |
341 } | |
342 if (ctrl->is_Region()) { | |
343 for (uint i = 1; i < ctrl->len(); i++) { | |
344 if (ctrl->in(i) != NULL && !_visited.test_set(ctrl->in(i)->_idx)) { | |
345 worklist.push(ctrl->in(i)); | |
346 } | |
347 } | |
348 } | |
349 } | |
350 return string_calls; | |
351 } | |
352 | |
353 | |
354 StringConcat* PhaseStringOpts::build_candidate(CallStaticJavaNode* call) { | |
355 ciMethod* m = call->method(); | |
356 ciSymbol* string_sig; | |
357 ciSymbol* int_sig; | |
358 ciSymbol* char_sig; | |
359 if (m->holder() == C->env()->StringBuilder_klass()) { | |
360 string_sig = ciSymbol::String_StringBuilder_signature(); | |
361 int_sig = ciSymbol::int_StringBuilder_signature(); | |
362 char_sig = ciSymbol::char_StringBuilder_signature(); | |
363 } else if (m->holder() == C->env()->StringBuffer_klass()) { | |
364 string_sig = ciSymbol::String_StringBuffer_signature(); | |
365 int_sig = ciSymbol::int_StringBuffer_signature(); | |
366 char_sig = ciSymbol::char_StringBuffer_signature(); | |
367 } else { | |
368 return NULL; | |
369 } | |
370 #ifndef PRODUCT | |
371 if (PrintOptimizeStringConcat) { | |
372 tty->print("considering toString call in "); | |
373 call->jvms()->dump_spec(tty); tty->cr(); | |
374 } | |
375 #endif | |
376 | |
377 StringConcat* sc = new StringConcat(this, call); | |
378 | |
379 AllocateNode* alloc = NULL; | |
380 InitializeNode* init = NULL; | |
381 | |
382 // possible opportunity for StringBuilder fusion | |
383 CallStaticJavaNode* cnode = call; | |
384 while (cnode) { | |
385 Node* recv = cnode->in(TypeFunc::Parms)->uncast(); | |
386 if (recv->is_Proj()) { | |
387 recv = recv->in(0); | |
388 } | |
389 cnode = recv->isa_CallStaticJava(); | |
390 if (cnode == NULL) { | |
391 alloc = recv->isa_Allocate(); | |
392 if (alloc == NULL) { | |
393 break; | |
394 } | |
395 // Find the constructor call | |
396 Node* result = alloc->result_cast(); | |
397 if (result == NULL || !result->is_CheckCastPP()) { | |
398 // strange looking allocation | |
399 #ifndef PRODUCT | |
400 if (PrintOptimizeStringConcat) { | |
401 tty->print("giving up because allocation looks strange "); | |
402 alloc->jvms()->dump_spec(tty); tty->cr(); | |
403 } | |
404 #endif | |
405 break; | |
406 } | |
407 Node* constructor = NULL; | |
408 for (SimpleDUIterator i(result); i.has_next(); i.next()) { | |
409 CallStaticJavaNode *use = i.get()->isa_CallStaticJava(); | |
1817
c40600e85311
6986028: assert(_base == Int) failed: Not an Int in CmpINode::sub
never
parents:
1748
diff
changeset
|
410 if (use != NULL && |
c40600e85311
6986028: assert(_base == Int) failed: Not an Int in CmpINode::sub
never
parents:
1748
diff
changeset
|
411 use->method() != NULL && |
c40600e85311
6986028: assert(_base == Int) failed: Not an Int in CmpINode::sub
never
parents:
1748
diff
changeset
|
412 !use->method()->is_static() && |
1080 | 413 use->method()->name() == ciSymbol::object_initializer_name() && |
414 use->method()->holder() == m->holder()) { | |
415 // Matched the constructor. | |
416 ciSymbol* sig = use->method()->signature()->as_symbol(); | |
417 if (sig == ciSymbol::void_method_signature() || | |
418 sig == ciSymbol::int_void_signature() || | |
419 sig == ciSymbol::string_void_signature()) { | |
420 if (sig == ciSymbol::string_void_signature()) { | |
421 // StringBuilder(String) so pick this up as the first argument | |
422 assert(use->in(TypeFunc::Parms + 1) != NULL, "what?"); | |
2090
2ddb2fab82cb
7009359: HS with -XX:+AggressiveOpts optimize new StringBuffer(null) so it does not throw NPE as expected
kvn
parents:
1972
diff
changeset
|
423 const Type* type = _gvn->type(use->in(TypeFunc::Parms + 1)); |
2ddb2fab82cb
7009359: HS with -XX:+AggressiveOpts optimize new StringBuffer(null) so it does not throw NPE as expected
kvn
parents:
1972
diff
changeset
|
424 if (type == TypePtr::NULL_PTR) { |
2ddb2fab82cb
7009359: HS with -XX:+AggressiveOpts optimize new StringBuffer(null) so it does not throw NPE as expected
kvn
parents:
1972
diff
changeset
|
425 // StringBuilder(null) throws exception. |
2ddb2fab82cb
7009359: HS with -XX:+AggressiveOpts optimize new StringBuffer(null) so it does not throw NPE as expected
kvn
parents:
1972
diff
changeset
|
426 #ifndef PRODUCT |
2ddb2fab82cb
7009359: HS with -XX:+AggressiveOpts optimize new StringBuffer(null) so it does not throw NPE as expected
kvn
parents:
1972
diff
changeset
|
427 if (PrintOptimizeStringConcat) { |
2ddb2fab82cb
7009359: HS with -XX:+AggressiveOpts optimize new StringBuffer(null) so it does not throw NPE as expected
kvn
parents:
1972
diff
changeset
|
428 tty->print("giving up because StringBuilder(null) throws exception"); |
2ddb2fab82cb
7009359: HS with -XX:+AggressiveOpts optimize new StringBuffer(null) so it does not throw NPE as expected
kvn
parents:
1972
diff
changeset
|
429 alloc->jvms()->dump_spec(tty); tty->cr(); |
2ddb2fab82cb
7009359: HS with -XX:+AggressiveOpts optimize new StringBuffer(null) so it does not throw NPE as expected
kvn
parents:
1972
diff
changeset
|
430 } |
2ddb2fab82cb
7009359: HS with -XX:+AggressiveOpts optimize new StringBuffer(null) so it does not throw NPE as expected
kvn
parents:
1972
diff
changeset
|
431 #endif |
2ddb2fab82cb
7009359: HS with -XX:+AggressiveOpts optimize new StringBuffer(null) so it does not throw NPE as expected
kvn
parents:
1972
diff
changeset
|
432 return NULL; |
2ddb2fab82cb
7009359: HS with -XX:+AggressiveOpts optimize new StringBuffer(null) so it does not throw NPE as expected
kvn
parents:
1972
diff
changeset
|
433 } |
2ddb2fab82cb
7009359: HS with -XX:+AggressiveOpts optimize new StringBuffer(null) so it does not throw NPE as expected
kvn
parents:
1972
diff
changeset
|
434 // StringBuilder(str) argument needs null check. |
2ddb2fab82cb
7009359: HS with -XX:+AggressiveOpts optimize new StringBuffer(null) so it does not throw NPE as expected
kvn
parents:
1972
diff
changeset
|
435 sc->push_string_null_check(use->in(TypeFunc::Parms + 1)); |
1080 | 436 } |
437 // The int variant takes an initial size for the backing | |
438 // array so just treat it like the void version. | |
439 constructor = use; | |
440 } else { | |
441 #ifndef PRODUCT | |
442 if (PrintOptimizeStringConcat) { | |
443 tty->print("unexpected constructor signature: %s", sig->as_utf8()); | |
444 } | |
445 #endif | |
446 } | |
447 break; | |
448 } | |
449 } | |
450 if (constructor == NULL) { | |
451 // couldn't find constructor | |
452 #ifndef PRODUCT | |
453 if (PrintOptimizeStringConcat) { | |
454 tty->print("giving up because couldn't find constructor "); | |
2090
2ddb2fab82cb
7009359: HS with -XX:+AggressiveOpts optimize new StringBuffer(null) so it does not throw NPE as expected
kvn
parents:
1972
diff
changeset
|
455 alloc->jvms()->dump_spec(tty); tty->cr(); |
1080 | 456 } |
457 #endif | |
458 break; | |
459 } | |
460 | |
461 // Walked all the way back and found the constructor call so see | |
462 // if this call converted into a direct string concatenation. | |
463 sc->add_control(call); | |
464 sc->add_control(constructor); | |
465 sc->add_control(alloc); | |
466 sc->set_allocation(alloc); | |
467 if (sc->validate_control_flow()) { | |
468 return sc; | |
469 } else { | |
470 return NULL; | |
471 } | |
472 } else if (cnode->method() == NULL) { | |
473 break; | |
1817
c40600e85311
6986028: assert(_base == Int) failed: Not an Int in CmpINode::sub
never
parents:
1748
diff
changeset
|
474 } else if (!cnode->method()->is_static() && |
c40600e85311
6986028: assert(_base == Int) failed: Not an Int in CmpINode::sub
never
parents:
1748
diff
changeset
|
475 cnode->method()->holder() == m->holder() && |
1080 | 476 cnode->method()->name() == ciSymbol::append_name() && |
477 (cnode->method()->signature()->as_symbol() == string_sig || | |
478 cnode->method()->signature()->as_symbol() == char_sig || | |
479 cnode->method()->signature()->as_symbol() == int_sig)) { | |
480 sc->add_control(cnode); | |
481 Node* arg = cnode->in(TypeFunc::Parms + 1); | |
482 if (cnode->method()->signature()->as_symbol() == int_sig) { | |
483 sc->push_int(arg); | |
484 } else if (cnode->method()->signature()->as_symbol() == char_sig) { | |
485 sc->push_char(arg); | |
486 } else { | |
487 if (arg->is_Proj() && arg->in(0)->is_CallStaticJava()) { | |
488 CallStaticJavaNode* csj = arg->in(0)->as_CallStaticJava(); | |
489 if (csj->method() != NULL && | |
1817
c40600e85311
6986028: assert(_base == Int) failed: Not an Int in CmpINode::sub
never
parents:
1748
diff
changeset
|
490 csj->method()->intrinsic_id() == vmIntrinsics::_Integer_toString) { |
1080 | 491 sc->add_control(csj); |
492 sc->push_int(csj->in(TypeFunc::Parms)); | |
493 continue; | |
494 } | |
495 } | |
496 sc->push_string(arg); | |
497 } | |
498 continue; | |
499 } else { | |
500 // some unhandled signature | |
501 #ifndef PRODUCT | |
502 if (PrintOptimizeStringConcat) { | |
503 tty->print("giving up because encountered unexpected signature "); | |
504 cnode->tf()->dump(); tty->cr(); | |
505 cnode->in(TypeFunc::Parms + 1)->dump(); | |
506 } | |
507 #endif | |
508 break; | |
509 } | |
510 } | |
511 return NULL; | |
512 } | |
513 | |
514 | |
515 PhaseStringOpts::PhaseStringOpts(PhaseGVN* gvn, Unique_Node_List*): | |
516 Phase(StringOpts), | |
517 _gvn(gvn), | |
518 _visited(Thread::current()->resource_area()) { | |
519 | |
520 assert(OptimizeStringConcat, "shouldn't be here"); | |
521 | |
522 size_table_field = C->env()->Integer_klass()->get_field_by_name(ciSymbol::make("sizeTable"), | |
523 ciSymbol::make("[I"), true); | |
524 if (size_table_field == NULL) { | |
525 // Something wrong so give up. | |
526 assert(false, "why can't we find Integer.sizeTable?"); | |
527 return; | |
528 } | |
529 | |
530 // Collect the types needed to talk about the various slices of memory | |
531 const TypeInstPtr* string_type = TypeInstPtr::make(TypePtr::NotNull, C->env()->String_klass(), | |
532 false, NULL, 0); | |
533 | |
534 const TypePtr* value_field_type = string_type->add_offset(java_lang_String::value_offset_in_bytes()); | |
535 const TypePtr* offset_field_type = string_type->add_offset(java_lang_String::offset_offset_in_bytes()); | |
536 const TypePtr* count_field_type = string_type->add_offset(java_lang_String::count_offset_in_bytes()); | |
537 | |
538 value_field_idx = C->get_alias_index(value_field_type); | |
539 count_field_idx = C->get_alias_index(count_field_type); | |
540 offset_field_idx = C->get_alias_index(offset_field_type); | |
541 char_adr_idx = C->get_alias_index(TypeAryPtr::CHARS); | |
542 | |
543 // For each locally allocated StringBuffer see if the usages can be | |
544 // collapsed into a single String construction. | |
545 | |
546 // Run through the list of allocation looking for SB.toString to see | |
547 // if it's possible to fuse the usage of the SB into a single String | |
548 // construction. | |
549 GrowableArray<StringConcat*> concats; | |
550 Node_List toStrings = collect_toString_calls(); | |
551 while (toStrings.size() > 0) { | |
552 StringConcat* sc = build_candidate(toStrings.pop()->as_CallStaticJava()); | |
553 if (sc != NULL) { | |
554 concats.push(sc); | |
555 } | |
556 } | |
557 | |
558 // try to coalesce separate concats | |
559 restart: | |
560 for (int c = 0; c < concats.length(); c++) { | |
561 StringConcat* sc = concats.at(c); | |
562 for (int i = 0; i < sc->num_arguments(); i++) { | |
563 Node* arg = sc->argument(i); | |
564 if (arg->is_Proj() && arg->in(0)->is_CallStaticJava()) { | |
565 CallStaticJavaNode* csj = arg->in(0)->as_CallStaticJava(); | |
566 if (csj->method() != NULL && | |
1817
c40600e85311
6986028: assert(_base == Int) failed: Not an Int in CmpINode::sub
never
parents:
1748
diff
changeset
|
567 (csj->method()->intrinsic_id() == vmIntrinsics::_StringBuilder_toString || |
c40600e85311
6986028: assert(_base == Int) failed: Not an Int in CmpINode::sub
never
parents:
1748
diff
changeset
|
568 csj->method()->intrinsic_id() == vmIntrinsics::_StringBuffer_toString)) { |
1080 | 569 for (int o = 0; o < concats.length(); o++) { |
570 if (c == o) continue; | |
571 StringConcat* other = concats.at(o); | |
572 if (other->end() == csj) { | |
573 #ifndef PRODUCT | |
574 if (PrintOptimizeStringConcat) { | |
575 tty->print_cr("considering stacked concats"); | |
576 } | |
577 #endif | |
578 | |
579 StringConcat* merged = sc->merge(other, arg); | |
580 if (merged->validate_control_flow()) { | |
581 #ifndef PRODUCT | |
582 if (PrintOptimizeStringConcat) { | |
583 tty->print_cr("stacking would succeed"); | |
584 } | |
585 #endif | |
586 if (c < o) { | |
587 concats.remove_at(o); | |
588 concats.at_put(c, merged); | |
589 } else { | |
590 concats.remove_at(c); | |
591 concats.at_put(o, merged); | |
592 } | |
593 goto restart; | |
594 } else { | |
595 #ifndef PRODUCT | |
596 if (PrintOptimizeStringConcat) { | |
597 tty->print_cr("stacking would fail"); | |
598 } | |
599 #endif | |
600 } | |
601 } | |
602 } | |
603 } | |
604 } | |
605 } | |
606 } | |
607 | |
608 | |
609 for (int c = 0; c < concats.length(); c++) { | |
610 StringConcat* sc = concats.at(c); | |
611 replace_string_concat(sc); | |
612 } | |
613 | |
614 remove_dead_nodes(); | |
615 } | |
616 | |
617 void PhaseStringOpts::record_dead_node(Node* dead) { | |
618 dead_worklist.push(dead); | |
619 } | |
620 | |
621 void PhaseStringOpts::remove_dead_nodes() { | |
622 // Delete any dead nodes to make things clean enough that escape | |
623 // analysis doesn't get unhappy. | |
624 while (dead_worklist.size() > 0) { | |
625 Node* use = dead_worklist.pop(); | |
626 int opc = use->Opcode(); | |
627 switch (opc) { | |
628 case Op_Region: { | |
629 uint i = 1; | |
630 for (i = 1; i < use->req(); i++) { | |
631 if (use->in(i) != C->top()) { | |
632 break; | |
633 } | |
634 } | |
635 if (i >= use->req()) { | |
636 for (SimpleDUIterator i(use); i.has_next(); i.next()) { | |
637 Node* m = i.get(); | |
638 if (m->is_Phi()) { | |
639 dead_worklist.push(m); | |
640 } | |
641 } | |
642 C->gvn_replace_by(use, C->top()); | |
643 } | |
644 break; | |
645 } | |
646 case Op_AddP: | |
647 case Op_CreateEx: { | |
648 // Recurisvely clean up references to CreateEx so EA doesn't | |
649 // get unhappy about the partially collapsed graph. | |
650 for (SimpleDUIterator i(use); i.has_next(); i.next()) { | |
651 Node* m = i.get(); | |
652 if (m->is_AddP()) { | |
653 dead_worklist.push(m); | |
654 } | |
655 } | |
656 C->gvn_replace_by(use, C->top()); | |
657 break; | |
658 } | |
659 case Op_Phi: | |
660 if (use->in(0) == C->top()) { | |
661 C->gvn_replace_by(use, C->top()); | |
662 } | |
663 break; | |
664 } | |
665 } | |
666 } | |
667 | |
668 | |
669 bool StringConcat::validate_control_flow() { | |
670 // We found all the calls and arguments now lets see if it's | |
671 // safe to transform the graph as we would expect. | |
672 | |
673 // Check to see if this resulted in too many uncommon traps previously | |
674 if (Compile::current()->too_many_traps(_begin->jvms()->method(), _begin->jvms()->bci(), | |
675 Deoptimization::Reason_intrinsic)) { | |
676 return false; | |
677 } | |
678 | |
679 // Walk backwards over the control flow from toString to the | |
680 // allocation and make sure all the control flow is ok. This | |
681 // means it's either going to be eliminated once the calls are | |
682 // removed or it can safely be transformed into an uncommon | |
683 // trap. | |
684 | |
685 int null_check_count = 0; | |
686 Unique_Node_List ctrl_path; | |
687 | |
688 assert(_control.contains(_begin), "missing"); | |
689 assert(_control.contains(_end), "missing"); | |
690 | |
691 // Collect the nodes that we know about and will eliminate into ctrl_path | |
692 for (uint i = 0; i < _control.size(); i++) { | |
693 // Push the call and it's control projection | |
694 Node* n = _control.at(i); | |
695 if (n->is_Allocate()) { | |
696 AllocateNode* an = n->as_Allocate(); | |
697 InitializeNode* init = an->initialization(); | |
698 ctrl_path.push(init); | |
699 ctrl_path.push(init->as_Multi()->proj_out(0)); | |
700 } | |
701 if (n->is_Call()) { | |
702 CallNode* cn = n->as_Call(); | |
703 ctrl_path.push(cn); | |
704 ctrl_path.push(cn->proj_out(0)); | |
705 ctrl_path.push(cn->proj_out(0)->unique_out()); | |
706 ctrl_path.push(cn->proj_out(0)->unique_out()->as_Catch()->proj_out(0)); | |
707 } else { | |
708 ShouldNotReachHere(); | |
709 } | |
710 } | |
711 | |
712 // Skip backwards through the control checking for unexpected contro flow | |
713 Node* ptr = _end; | |
714 bool fail = false; | |
715 while (ptr != _begin) { | |
716 if (ptr->is_Call() && ctrl_path.member(ptr)) { | |
717 ptr = ptr->in(0); | |
718 } else if (ptr->is_CatchProj() && ctrl_path.member(ptr)) { | |
719 ptr = ptr->in(0)->in(0)->in(0); | |
720 assert(ctrl_path.member(ptr), "should be a known piece of control"); | |
721 } else if (ptr->is_IfTrue()) { | |
722 IfNode* iff = ptr->in(0)->as_If(); | |
723 BoolNode* b = iff->in(1)->isa_Bool(); | |
724 Node* cmp = b->in(1); | |
725 Node* v1 = cmp->in(1); | |
726 Node* v2 = cmp->in(2); | |
727 Node* otherproj = iff->proj_out(1 - ptr->as_Proj()->_con); | |
728 | |
729 // Null check of the return of append which can simply be eliminated | |
730 if (b->_test._test == BoolTest::ne && | |
731 v2->bottom_type() == TypePtr::NULL_PTR && | |
732 v1->is_Proj() && ctrl_path.member(v1->in(0))) { | |
733 // NULL check of the return value of the append | |
734 null_check_count++; | |
735 if (otherproj->outcnt() == 1) { | |
736 CallStaticJavaNode* call = otherproj->unique_out()->isa_CallStaticJava(); | |
737 if (call != NULL && call->_name != NULL && strcmp(call->_name, "uncommon_trap") == 0) { | |
738 ctrl_path.push(call); | |
739 } | |
740 } | |
741 _control.push(ptr); | |
742 ptr = ptr->in(0)->in(0); | |
743 continue; | |
744 } | |
745 | |
746 // A test which leads to an uncommon trap which should be safe. | |
747 // Later this trap will be converted into a trap that restarts | |
748 // at the beginning. | |
749 if (otherproj->outcnt() == 1) { | |
750 CallStaticJavaNode* call = otherproj->unique_out()->isa_CallStaticJava(); | |
751 if (call != NULL && call->_name != NULL && strcmp(call->_name, "uncommon_trap") == 0) { | |
752 // control flow leads to uct so should be ok | |
753 _uncommon_traps.push(call); | |
754 ctrl_path.push(call); | |
755 ptr = ptr->in(0)->in(0); | |
756 continue; | |
757 } | |
758 } | |
759 | |
760 #ifndef PRODUCT | |
761 // Some unexpected control flow we don't know how to handle. | |
762 if (PrintOptimizeStringConcat) { | |
763 tty->print_cr("failing with unknown test"); | |
764 b->dump(); | |
765 cmp->dump(); | |
766 v1->dump(); | |
767 v2->dump(); | |
768 tty->cr(); | |
769 } | |
770 #endif | |
3396 | 771 fail = true; |
1080 | 772 break; |
773 } else if (ptr->is_Proj() && ptr->in(0)->is_Initialize()) { | |
774 ptr = ptr->in(0)->in(0); | |
775 } else if (ptr->is_Region()) { | |
776 Node* copy = ptr->as_Region()->is_copy(); | |
777 if (copy != NULL) { | |
778 ptr = copy; | |
779 continue; | |
780 } | |
781 if (ptr->req() == 3 && | |
782 ptr->in(1) != NULL && ptr->in(1)->is_Proj() && | |
783 ptr->in(2) != NULL && ptr->in(2)->is_Proj() && | |
784 ptr->in(1)->in(0) == ptr->in(2)->in(0) && | |
785 ptr->in(1)->in(0) != NULL && ptr->in(1)->in(0)->is_If()) { | |
786 // Simple diamond. | |
787 // XXX should check for possibly merging stores. simple data merges are ok. | |
788 ptr = ptr->in(1)->in(0)->in(0); | |
789 continue; | |
790 } | |
791 #ifndef PRODUCT | |
792 if (PrintOptimizeStringConcat) { | |
793 tty->print_cr("fusion would fail for region"); | |
794 _begin->dump(); | |
795 ptr->dump(2); | |
796 } | |
797 #endif | |
798 fail = true; | |
799 break; | |
800 } else { | |
801 // other unknown control | |
802 if (!fail) { | |
803 #ifndef PRODUCT | |
804 if (PrintOptimizeStringConcat) { | |
805 tty->print_cr("fusion would fail for"); | |
806 _begin->dump(); | |
807 } | |
808 #endif | |
809 fail = true; | |
810 } | |
811 #ifndef PRODUCT | |
812 if (PrintOptimizeStringConcat) { | |
813 ptr->dump(); | |
814 } | |
815 #endif | |
816 ptr = ptr->in(0); | |
817 } | |
818 } | |
819 #ifndef PRODUCT | |
820 if (PrintOptimizeStringConcat && fail) { | |
821 tty->cr(); | |
822 } | |
823 #endif | |
824 if (fail) return !fail; | |
825 | |
826 // Validate that all these results produced are contained within | |
827 // this cluster of objects. First collect all the results produced | |
828 // by calls in the region. | |
829 _stringopts->_visited.Clear(); | |
830 Node_List worklist; | |
831 Node* final_result = _end->proj_out(TypeFunc::Parms); | |
832 for (uint i = 0; i < _control.size(); i++) { | |
833 CallNode* cnode = _control.at(i)->isa_Call(); | |
834 if (cnode != NULL) { | |
835 _stringopts->_visited.test_set(cnode->_idx); | |
836 } | |
837 Node* result = cnode != NULL ? cnode->proj_out(TypeFunc::Parms) : NULL; | |
838 if (result != NULL && result != final_result) { | |
839 worklist.push(result); | |
840 } | |
841 } | |
842 | |
843 Node* last_result = NULL; | |
844 while (worklist.size() > 0) { | |
845 Node* result = worklist.pop(); | |
846 if (_stringopts->_visited.test_set(result->_idx)) | |
847 continue; | |
848 for (SimpleDUIterator i(result); i.has_next(); i.next()) { | |
849 Node *use = i.get(); | |
850 if (ctrl_path.member(use)) { | |
851 // already checked this | |
852 continue; | |
853 } | |
854 int opc = use->Opcode(); | |
855 if (opc == Op_CmpP || opc == Op_Node) { | |
856 ctrl_path.push(use); | |
857 continue; | |
858 } | |
859 if (opc == Op_CastPP || opc == Op_CheckCastPP) { | |
860 for (SimpleDUIterator j(use); j.has_next(); j.next()) { | |
861 worklist.push(j.get()); | |
862 } | |
863 worklist.push(use->in(1)); | |
864 ctrl_path.push(use); | |
865 continue; | |
866 } | |
867 #ifndef PRODUCT | |
868 if (PrintOptimizeStringConcat) { | |
869 if (result != last_result) { | |
870 last_result = result; | |
871 tty->print_cr("extra uses for result:"); | |
872 last_result->dump(); | |
873 } | |
874 use->dump(); | |
875 } | |
876 #endif | |
877 fail = true; | |
878 break; | |
879 } | |
880 } | |
881 | |
882 #ifndef PRODUCT | |
883 if (PrintOptimizeStringConcat && !fail) { | |
884 ttyLocker ttyl; | |
885 tty->cr(); | |
886 tty->print("fusion would succeed (%d %d) for ", null_check_count, _uncommon_traps.size()); | |
887 _begin->jvms()->dump_spec(tty); tty->cr(); | |
888 for (int i = 0; i < num_arguments(); i++) { | |
889 argument(i)->dump(); | |
890 } | |
891 _control.dump(); | |
892 tty->cr(); | |
893 } | |
894 #endif | |
895 | |
896 return !fail; | |
897 } | |
898 | |
899 Node* PhaseStringOpts::fetch_static_field(GraphKit& kit, ciField* field) { | |
900 const TypeKlassPtr* klass_type = TypeKlassPtr::make(field->holder()); | |
901 Node* klass_node = __ makecon(klass_type); | |
902 BasicType bt = field->layout_type(); | |
903 ciType* field_klass = field->type(); | |
904 | |
905 const Type *type; | |
906 if( bt == T_OBJECT ) { | |
907 if (!field->type()->is_loaded()) { | |
908 type = TypeInstPtr::BOTTOM; | |
909 } else if (field->is_constant()) { | |
910 // This can happen if the constant oop is non-perm. | |
911 ciObject* con = field->constant_value().as_object(); | |
912 // Do not "join" in the previous type; it doesn't add value, | |
913 // and may yield a vacuous result if the field is of interface type. | |
2379
b099aaf51bf8
6962931: move interned strings out of the perm gen
jcoomes
parents:
2090
diff
changeset
|
914 type = TypeOopPtr::make_from_constant(con, true)->isa_oopptr(); |
1080 | 915 assert(type != NULL, "field singleton type must be consistent"); |
916 } else { | |
917 type = TypeOopPtr::make_from_klass(field_klass->as_klass()); | |
918 } | |
919 } else { | |
920 type = Type::get_const_basic_type(bt); | |
921 } | |
922 | |
923 return kit.make_load(NULL, kit.basic_plus_adr(klass_node, field->offset_in_bytes()), | |
924 type, T_OBJECT, | |
925 C->get_alias_index(klass_type->add_offset(field->offset_in_bytes()))); | |
926 } | |
927 | |
928 Node* PhaseStringOpts::int_stringSize(GraphKit& kit, Node* arg) { | |
929 RegionNode *final_merge = new (C, 3) RegionNode(3); | |
930 kit.gvn().set_type(final_merge, Type::CONTROL); | |
931 Node* final_size = new (C, 3) PhiNode(final_merge, TypeInt::INT); | |
932 kit.gvn().set_type(final_size, TypeInt::INT); | |
933 | |
934 IfNode* iff = kit.create_and_map_if(kit.control(), | |
935 __ Bool(__ CmpI(arg, __ intcon(0x80000000)), BoolTest::ne), | |
936 PROB_FAIR, COUNT_UNKNOWN); | |
937 Node* is_min = __ IfFalse(iff); | |
938 final_merge->init_req(1, is_min); | |
939 final_size->init_req(1, __ intcon(11)); | |
940 | |
941 kit.set_control(__ IfTrue(iff)); | |
942 if (kit.stopped()) { | |
943 final_merge->init_req(2, C->top()); | |
944 final_size->init_req(2, C->top()); | |
945 } else { | |
946 | |
947 // int size = (i < 0) ? stringSize(-i) + 1 : stringSize(i); | |
948 RegionNode *r = new (C, 3) RegionNode(3); | |
949 kit.gvn().set_type(r, Type::CONTROL); | |
950 Node *phi = new (C, 3) PhiNode(r, TypeInt::INT); | |
951 kit.gvn().set_type(phi, TypeInt::INT); | |
952 Node *size = new (C, 3) PhiNode(r, TypeInt::INT); | |
953 kit.gvn().set_type(size, TypeInt::INT); | |
954 Node* chk = __ CmpI(arg, __ intcon(0)); | |
955 Node* p = __ Bool(chk, BoolTest::lt); | |
956 IfNode* iff = kit.create_and_map_if(kit.control(), p, PROB_FAIR, COUNT_UNKNOWN); | |
957 Node* lessthan = __ IfTrue(iff); | |
958 Node* greaterequal = __ IfFalse(iff); | |
959 r->init_req(1, lessthan); | |
960 phi->init_req(1, __ SubI(__ intcon(0), arg)); | |
961 size->init_req(1, __ intcon(1)); | |
962 r->init_req(2, greaterequal); | |
963 phi->init_req(2, arg); | |
964 size->init_req(2, __ intcon(0)); | |
965 kit.set_control(r); | |
966 C->record_for_igvn(r); | |
967 C->record_for_igvn(phi); | |
968 C->record_for_igvn(size); | |
969 | |
970 // for (int i=0; ; i++) | |
971 // if (x <= sizeTable[i]) | |
972 // return i+1; | |
2383
9dc311b8473e
7008866: Missing loop predicate for loop with multiple entries
kvn
parents:
2090
diff
changeset
|
973 |
9dc311b8473e
7008866: Missing loop predicate for loop with multiple entries
kvn
parents:
2090
diff
changeset
|
974 // Add loop predicate first. |
9dc311b8473e
7008866: Missing loop predicate for loop with multiple entries
kvn
parents:
2090
diff
changeset
|
975 kit.add_predicate(); |
9dc311b8473e
7008866: Missing loop predicate for loop with multiple entries
kvn
parents:
2090
diff
changeset
|
976 |
1080 | 977 RegionNode *loop = new (C, 3) RegionNode(3); |
978 loop->init_req(1, kit.control()); | |
979 kit.gvn().set_type(loop, Type::CONTROL); | |
980 | |
981 Node *index = new (C, 3) PhiNode(loop, TypeInt::INT); | |
982 index->init_req(1, __ intcon(0)); | |
983 kit.gvn().set_type(index, TypeInt::INT); | |
984 kit.set_control(loop); | |
985 Node* sizeTable = fetch_static_field(kit, size_table_field); | |
986 | |
987 Node* value = kit.load_array_element(NULL, sizeTable, index, TypeAryPtr::INTS); | |
988 C->record_for_igvn(value); | |
989 Node* limit = __ CmpI(phi, value); | |
990 Node* limitb = __ Bool(limit, BoolTest::le); | |
991 IfNode* iff2 = kit.create_and_map_if(kit.control(), limitb, PROB_MIN, COUNT_UNKNOWN); | |
992 Node* lessEqual = __ IfTrue(iff2); | |
993 Node* greater = __ IfFalse(iff2); | |
994 | |
995 loop->init_req(2, greater); | |
996 index->init_req(2, __ AddI(index, __ intcon(1))); | |
997 | |
998 kit.set_control(lessEqual); | |
999 C->record_for_igvn(loop); | |
1000 C->record_for_igvn(index); | |
1001 | |
1002 final_merge->init_req(2, kit.control()); | |
1003 final_size->init_req(2, __ AddI(__ AddI(index, size), __ intcon(1))); | |
1004 } | |
1005 | |
1006 kit.set_control(final_merge); | |
1007 C->record_for_igvn(final_merge); | |
1008 C->record_for_igvn(final_size); | |
1009 | |
1010 return final_size; | |
1011 } | |
1012 | |
1013 void PhaseStringOpts::int_getChars(GraphKit& kit, Node* arg, Node* char_array, Node* start, Node* end) { | |
1014 RegionNode *final_merge = new (C, 4) RegionNode(4); | |
1015 kit.gvn().set_type(final_merge, Type::CONTROL); | |
1016 Node *final_mem = PhiNode::make(final_merge, kit.memory(char_adr_idx), Type::MEMORY, TypeAryPtr::CHARS); | |
1017 kit.gvn().set_type(final_mem, Type::MEMORY); | |
1018 | |
1019 // need to handle Integer.MIN_VALUE specially because negating doesn't make it positive | |
1020 { | |
1021 // i == MIN_VALUE | |
1022 IfNode* iff = kit.create_and_map_if(kit.control(), | |
1023 __ Bool(__ CmpI(arg, __ intcon(0x80000000)), BoolTest::ne), | |
1024 PROB_FAIR, COUNT_UNKNOWN); | |
1025 | |
1026 Node* old_mem = kit.memory(char_adr_idx); | |
1027 | |
1028 kit.set_control(__ IfFalse(iff)); | |
1029 if (kit.stopped()) { | |
1030 // Statically not equal to MIN_VALUE so this path is dead | |
1031 final_merge->init_req(3, kit.control()); | |
1032 } else { | |
1033 copy_string(kit, __ makecon(TypeInstPtr::make(C->env()->the_min_jint_string())), | |
1034 char_array, start); | |
1035 final_merge->init_req(3, kit.control()); | |
1036 final_mem->init_req(3, kit.memory(char_adr_idx)); | |
1037 } | |
1038 | |
1039 kit.set_control(__ IfTrue(iff)); | |
1040 kit.set_memory(old_mem, char_adr_idx); | |
1041 } | |
1042 | |
1043 | |
1044 // Simplified version of Integer.getChars | |
1045 | |
1046 // int q, r; | |
1047 // int charPos = index; | |
1048 Node* charPos = end; | |
1049 | |
1050 // char sign = 0; | |
1051 | |
1052 Node* i = arg; | |
1053 Node* sign = __ intcon(0); | |
1054 | |
1055 // if (i < 0) { | |
1056 // sign = '-'; | |
1057 // i = -i; | |
1058 // } | |
1059 { | |
1060 IfNode* iff = kit.create_and_map_if(kit.control(), | |
1061 __ Bool(__ CmpI(arg, __ intcon(0)), BoolTest::lt), | |
1062 PROB_FAIR, COUNT_UNKNOWN); | |
1063 | |
1064 RegionNode *merge = new (C, 3) RegionNode(3); | |
1065 kit.gvn().set_type(merge, Type::CONTROL); | |
1066 i = new (C, 3) PhiNode(merge, TypeInt::INT); | |
1067 kit.gvn().set_type(i, TypeInt::INT); | |
1068 sign = new (C, 3) PhiNode(merge, TypeInt::INT); | |
1069 kit.gvn().set_type(sign, TypeInt::INT); | |
1070 | |
1071 merge->init_req(1, __ IfTrue(iff)); | |
1072 i->init_req(1, __ SubI(__ intcon(0), arg)); | |
1073 sign->init_req(1, __ intcon('-')); | |
1074 merge->init_req(2, __ IfFalse(iff)); | |
1075 i->init_req(2, arg); | |
1076 sign->init_req(2, __ intcon(0)); | |
1077 | |
1078 kit.set_control(merge); | |
1079 | |
1080 C->record_for_igvn(merge); | |
1081 C->record_for_igvn(i); | |
1082 C->record_for_igvn(sign); | |
1083 } | |
1084 | |
1085 // for (;;) { | |
1086 // q = i / 10; | |
1087 // r = i - ((q << 3) + (q << 1)); // r = i-(q*10) ... | |
1088 // buf [--charPos] = digits [r]; | |
1089 // i = q; | |
1090 // if (i == 0) break; | |
1091 // } | |
1092 | |
1093 { | |
2383
9dc311b8473e
7008866: Missing loop predicate for loop with multiple entries
kvn
parents:
2090
diff
changeset
|
1094 // Add loop predicate first. |
9dc311b8473e
7008866: Missing loop predicate for loop with multiple entries
kvn
parents:
2090
diff
changeset
|
1095 kit.add_predicate(); |
9dc311b8473e
7008866: Missing loop predicate for loop with multiple entries
kvn
parents:
2090
diff
changeset
|
1096 |
1080 | 1097 RegionNode *head = new (C, 3) RegionNode(3); |
1098 head->init_req(1, kit.control()); | |
1099 kit.gvn().set_type(head, Type::CONTROL); | |
1100 Node *i_phi = new (C, 3) PhiNode(head, TypeInt::INT); | |
1101 i_phi->init_req(1, i); | |
1102 kit.gvn().set_type(i_phi, TypeInt::INT); | |
1103 charPos = PhiNode::make(head, charPos); | |
1104 kit.gvn().set_type(charPos, TypeInt::INT); | |
1105 Node *mem = PhiNode::make(head, kit.memory(char_adr_idx), Type::MEMORY, TypeAryPtr::CHARS); | |
1106 kit.gvn().set_type(mem, Type::MEMORY); | |
1107 kit.set_control(head); | |
1108 kit.set_memory(mem, char_adr_idx); | |
1109 | |
1250 | 1110 Node* q = __ DivI(NULL, i_phi, __ intcon(10)); |
1080 | 1111 Node* r = __ SubI(i_phi, __ AddI(__ LShiftI(q, __ intcon(3)), |
1112 __ LShiftI(q, __ intcon(1)))); | |
1113 Node* m1 = __ SubI(charPos, __ intcon(1)); | |
1114 Node* ch = __ AddI(r, __ intcon('0')); | |
1115 | |
1116 Node* st = __ store_to_memory(kit.control(), kit.array_element_address(char_array, m1, T_CHAR), | |
1117 ch, T_CHAR, char_adr_idx); | |
1118 | |
1119 | |
1120 IfNode* iff = kit.create_and_map_if(head, __ Bool(__ CmpI(q, __ intcon(0)), BoolTest::ne), | |
1121 PROB_FAIR, COUNT_UNKNOWN); | |
1122 Node* ne = __ IfTrue(iff); | |
1123 Node* eq = __ IfFalse(iff); | |
1124 | |
1125 head->init_req(2, ne); | |
1126 mem->init_req(2, st); | |
1127 i_phi->init_req(2, q); | |
1128 charPos->init_req(2, m1); | |
1129 | |
1130 charPos = m1; | |
1131 | |
1132 kit.set_control(eq); | |
1133 kit.set_memory(st, char_adr_idx); | |
1134 | |
1135 C->record_for_igvn(head); | |
1136 C->record_for_igvn(mem); | |
1137 C->record_for_igvn(i_phi); | |
1138 C->record_for_igvn(charPos); | |
1139 } | |
1140 | |
1141 { | |
1142 // if (sign != 0) { | |
1143 // buf [--charPos] = sign; | |
1144 // } | |
1145 IfNode* iff = kit.create_and_map_if(kit.control(), | |
1146 __ Bool(__ CmpI(sign, __ intcon(0)), BoolTest::ne), | |
1147 PROB_FAIR, COUNT_UNKNOWN); | |
1148 | |
1149 final_merge->init_req(2, __ IfFalse(iff)); | |
1150 final_mem->init_req(2, kit.memory(char_adr_idx)); | |
1151 | |
1152 kit.set_control(__ IfTrue(iff)); | |
1153 if (kit.stopped()) { | |
1154 final_merge->init_req(1, C->top()); | |
1155 final_mem->init_req(1, C->top()); | |
1156 } else { | |
1157 Node* m1 = __ SubI(charPos, __ intcon(1)); | |
1158 Node* st = __ store_to_memory(kit.control(), kit.array_element_address(char_array, m1, T_CHAR), | |
1159 sign, T_CHAR, char_adr_idx); | |
1160 | |
1161 final_merge->init_req(1, kit.control()); | |
1162 final_mem->init_req(1, st); | |
1163 } | |
1164 | |
1165 kit.set_control(final_merge); | |
1166 kit.set_memory(final_mem, char_adr_idx); | |
1167 | |
1168 C->record_for_igvn(final_merge); | |
1169 C->record_for_igvn(final_mem); | |
1170 } | |
1171 } | |
1172 | |
1173 | |
1174 Node* PhaseStringOpts::copy_string(GraphKit& kit, Node* str, Node* char_array, Node* start) { | |
1175 Node* string = str; | |
3283
01fd6090fdd8
7032162: assert(flat != TypePtr::BOTTOM) failed: cannot alias-analyze an untyped ptr
never
parents:
2426
diff
changeset
|
1176 Node* offset = kit.make_load(kit.control(), |
1080 | 1177 kit.basic_plus_adr(string, string, java_lang_String::offset_offset_in_bytes()), |
1178 TypeInt::INT, T_INT, offset_field_idx); | |
3283
01fd6090fdd8
7032162: assert(flat != TypePtr::BOTTOM) failed: cannot alias-analyze an untyped ptr
never
parents:
2426
diff
changeset
|
1179 Node* count = kit.make_load(kit.control(), |
1080 | 1180 kit.basic_plus_adr(string, string, java_lang_String::count_offset_in_bytes()), |
1181 TypeInt::INT, T_INT, count_field_idx); | |
1182 const TypeAryPtr* value_type = TypeAryPtr::make(TypePtr::NotNull, | |
1183 TypeAry::make(TypeInt::CHAR,TypeInt::POS), | |
1184 ciTypeArrayKlass::make(T_CHAR), true, 0); | |
3283
01fd6090fdd8
7032162: assert(flat != TypePtr::BOTTOM) failed: cannot alias-analyze an untyped ptr
never
parents:
2426
diff
changeset
|
1185 Node* value = kit.make_load(kit.control(), |
1080 | 1186 kit.basic_plus_adr(string, string, java_lang_String::value_offset_in_bytes()), |
1187 value_type, T_OBJECT, value_field_idx); | |
1188 | |
1189 // copy the contents | |
1190 if (offset->is_Con() && count->is_Con() && value->is_Con() && count->get_int() < unroll_string_copy_length) { | |
1191 // For small constant strings just emit individual stores. | |
1192 // A length of 6 seems like a good space/speed tradeof. | |
1193 int c = count->get_int(); | |
1194 int o = offset->get_int(); | |
1195 const TypeOopPtr* t = kit.gvn().type(value)->isa_oopptr(); | |
1196 ciTypeArray* value_array = t->const_oop()->as_type_array(); | |
1197 for (int e = 0; e < c; e++) { | |
1198 __ store_to_memory(kit.control(), kit.array_element_address(char_array, start, T_CHAR), | |
1199 __ intcon(value_array->char_at(o + e)), T_CHAR, char_adr_idx); | |
1200 start = __ AddI(start, __ intcon(1)); | |
1201 } | |
1202 } else { | |
1203 Node* src_ptr = kit.array_element_address(value, offset, T_CHAR); | |
1204 Node* dst_ptr = kit.array_element_address(char_array, start, T_CHAR); | |
1205 Node* c = count; | |
1206 Node* extra = NULL; | |
1207 #ifdef _LP64 | |
1208 c = __ ConvI2L(c); | |
1209 extra = C->top(); | |
1210 #endif | |
1211 Node* call = kit.make_runtime_call(GraphKit::RC_LEAF|GraphKit::RC_NO_FP, | |
1212 OptoRuntime::fast_arraycopy_Type(), | |
1213 CAST_FROM_FN_PTR(address, StubRoutines::jshort_disjoint_arraycopy()), | |
1214 "jshort_disjoint_arraycopy", TypeAryPtr::CHARS, | |
1215 src_ptr, dst_ptr, c, extra); | |
1216 start = __ AddI(start, count); | |
1217 } | |
1218 return start; | |
1219 } | |
1220 | |
1221 | |
1222 void PhaseStringOpts::replace_string_concat(StringConcat* sc) { | |
1223 // Log a little info about the transformation | |
1224 sc->maybe_log_transform(); | |
1225 | |
1226 // pull the JVMState of the allocation into a SafePointNode to serve as | |
1227 // as a shim for the insertion of the new code. | |
1228 JVMState* jvms = sc->begin()->jvms()->clone_shallow(C); | |
1229 uint size = sc->begin()->req(); | |
1230 SafePointNode* map = new (C, size) SafePointNode(size, jvms); | |
1231 | |
1232 // copy the control and memory state from the final call into our | |
1233 // new starting state. This allows any preceeding tests to feed | |
1234 // into the new section of code. | |
1235 for (uint i1 = 0; i1 < TypeFunc::Parms; i1++) { | |
1236 map->init_req(i1, sc->end()->in(i1)); | |
1237 } | |
1238 // blow away old allocation arguments | |
1239 for (uint i1 = TypeFunc::Parms; i1 < jvms->debug_start(); i1++) { | |
1240 map->init_req(i1, C->top()); | |
1241 } | |
1242 // Copy the rest of the inputs for the JVMState | |
1243 for (uint i1 = jvms->debug_start(); i1 < sc->begin()->req(); i1++) { | |
1244 map->init_req(i1, sc->begin()->in(i1)); | |
1245 } | |
1246 // Make sure the memory state is a MergeMem for parsing. | |
1247 if (!map->in(TypeFunc::Memory)->is_MergeMem()) { | |
1248 map->set_req(TypeFunc::Memory, MergeMemNode::make(C, map->in(TypeFunc::Memory))); | |
1249 } | |
1250 | |
1251 jvms->set_map(map); | |
1252 map->ensure_stack(jvms, jvms->method()->max_stack()); | |
1253 | |
1254 | |
1255 // disconnect all the old StringBuilder calls from the graph | |
1256 sc->eliminate_unneeded_control(); | |
1257 | |
1258 // At this point all the old work has been completely removed from | |
1259 // the graph and the saved JVMState exists at the point where the | |
1260 // final toString call used to be. | |
1261 GraphKit kit(jvms); | |
1262 | |
1263 // There may be uncommon traps which are still using the | |
1264 // intermediate states and these need to be rewritten to point at | |
1265 // the JVMState at the beginning of the transformation. | |
1266 sc->convert_uncommon_traps(kit, jvms); | |
1267 | |
1268 // Now insert the logic to compute the size of the string followed | |
1269 // by all the logic to construct array and resulting string. | |
1270 | |
1271 Node* null_string = __ makecon(TypeInstPtr::make(C->env()->the_null_string())); | |
1272 | |
1273 // Create a region for the overflow checks to merge into. | |
1274 int args = MAX2(sc->num_arguments(), 1); | |
1275 RegionNode* overflow = new (C, args) RegionNode(args); | |
1276 kit.gvn().set_type(overflow, Type::CONTROL); | |
1277 | |
1278 // Create a hook node to hold onto the individual sizes since they | |
1279 // are need for the copying phase. | |
1280 Node* string_sizes = new (C, args) Node(args); | |
1281 | |
1282 Node* length = __ intcon(0); | |
1283 for (int argi = 0; argi < sc->num_arguments(); argi++) { | |
1284 Node* arg = sc->argument(argi); | |
1285 switch (sc->mode(argi)) { | |
1286 case StringConcat::IntMode: { | |
1287 Node* string_size = int_stringSize(kit, arg); | |
1288 | |
1289 // accumulate total | |
1290 length = __ AddI(length, string_size); | |
1291 | |
1292 // Cache this value for the use by int_toString | |
1293 string_sizes->init_req(argi, string_size); | |
1294 break; | |
1295 } | |
2090
2ddb2fab82cb
7009359: HS with -XX:+AggressiveOpts optimize new StringBuffer(null) so it does not throw NPE as expected
kvn
parents:
1972
diff
changeset
|
1296 case StringConcat::StringNullCheckMode: { |
2ddb2fab82cb
7009359: HS with -XX:+AggressiveOpts optimize new StringBuffer(null) so it does not throw NPE as expected
kvn
parents:
1972
diff
changeset
|
1297 const Type* type = kit.gvn().type(arg); |
2ddb2fab82cb
7009359: HS with -XX:+AggressiveOpts optimize new StringBuffer(null) so it does not throw NPE as expected
kvn
parents:
1972
diff
changeset
|
1298 assert(type != TypePtr::NULL_PTR, "missing check"); |
2ddb2fab82cb
7009359: HS with -XX:+AggressiveOpts optimize new StringBuffer(null) so it does not throw NPE as expected
kvn
parents:
1972
diff
changeset
|
1299 if (!type->higher_equal(TypeInstPtr::NOTNULL)) { |
2ddb2fab82cb
7009359: HS with -XX:+AggressiveOpts optimize new StringBuffer(null) so it does not throw NPE as expected
kvn
parents:
1972
diff
changeset
|
1300 // Null check with uncommont trap since |
2ddb2fab82cb
7009359: HS with -XX:+AggressiveOpts optimize new StringBuffer(null) so it does not throw NPE as expected
kvn
parents:
1972
diff
changeset
|
1301 // StringBuilder(null) throws exception. |
2ddb2fab82cb
7009359: HS with -XX:+AggressiveOpts optimize new StringBuffer(null) so it does not throw NPE as expected
kvn
parents:
1972
diff
changeset
|
1302 // Use special uncommon trap instead of |
2ddb2fab82cb
7009359: HS with -XX:+AggressiveOpts optimize new StringBuffer(null) so it does not throw NPE as expected
kvn
parents:
1972
diff
changeset
|
1303 // calling normal do_null_check(). |
2ddb2fab82cb
7009359: HS with -XX:+AggressiveOpts optimize new StringBuffer(null) so it does not throw NPE as expected
kvn
parents:
1972
diff
changeset
|
1304 Node* p = __ Bool(__ CmpP(arg, kit.null()), BoolTest::ne); |
2ddb2fab82cb
7009359: HS with -XX:+AggressiveOpts optimize new StringBuffer(null) so it does not throw NPE as expected
kvn
parents:
1972
diff
changeset
|
1305 IfNode* iff = kit.create_and_map_if(kit.control(), p, PROB_MIN, COUNT_UNKNOWN); |
2ddb2fab82cb
7009359: HS with -XX:+AggressiveOpts optimize new StringBuffer(null) so it does not throw NPE as expected
kvn
parents:
1972
diff
changeset
|
1306 overflow->add_req(__ IfFalse(iff)); |
2ddb2fab82cb
7009359: HS with -XX:+AggressiveOpts optimize new StringBuffer(null) so it does not throw NPE as expected
kvn
parents:
1972
diff
changeset
|
1307 Node* notnull = __ IfTrue(iff); |
2ddb2fab82cb
7009359: HS with -XX:+AggressiveOpts optimize new StringBuffer(null) so it does not throw NPE as expected
kvn
parents:
1972
diff
changeset
|
1308 kit.set_control(notnull); // set control for the cast_not_null |
2ddb2fab82cb
7009359: HS with -XX:+AggressiveOpts optimize new StringBuffer(null) so it does not throw NPE as expected
kvn
parents:
1972
diff
changeset
|
1309 arg = kit.cast_not_null(arg, false); |
2ddb2fab82cb
7009359: HS with -XX:+AggressiveOpts optimize new StringBuffer(null) so it does not throw NPE as expected
kvn
parents:
1972
diff
changeset
|
1310 sc->set_argument(argi, arg); |
2ddb2fab82cb
7009359: HS with -XX:+AggressiveOpts optimize new StringBuffer(null) so it does not throw NPE as expected
kvn
parents:
1972
diff
changeset
|
1311 } |
2ddb2fab82cb
7009359: HS with -XX:+AggressiveOpts optimize new StringBuffer(null) so it does not throw NPE as expected
kvn
parents:
1972
diff
changeset
|
1312 assert(kit.gvn().type(arg)->higher_equal(TypeInstPtr::NOTNULL), "sanity"); |
2ddb2fab82cb
7009359: HS with -XX:+AggressiveOpts optimize new StringBuffer(null) so it does not throw NPE as expected
kvn
parents:
1972
diff
changeset
|
1313 // Fallthrough to add string length. |
2ddb2fab82cb
7009359: HS with -XX:+AggressiveOpts optimize new StringBuffer(null) so it does not throw NPE as expected
kvn
parents:
1972
diff
changeset
|
1314 } |
1080 | 1315 case StringConcat::StringMode: { |
1316 const Type* type = kit.gvn().type(arg); | |
1317 if (type == TypePtr::NULL_PTR) { | |
1318 // replace the argument with the null checked version | |
1319 arg = null_string; | |
1320 sc->set_argument(argi, arg); | |
1321 } else if (!type->higher_equal(TypeInstPtr::NOTNULL)) { | |
1322 // s = s != null ? s : "null"; | |
1323 // length = length + (s.count - s.offset); | |
1324 RegionNode *r = new (C, 3) RegionNode(3); | |
1325 kit.gvn().set_type(r, Type::CONTROL); | |
1250 | 1326 Node *phi = new (C, 3) PhiNode(r, type); |
1080 | 1327 kit.gvn().set_type(phi, phi->bottom_type()); |
1328 Node* p = __ Bool(__ CmpP(arg, kit.null()), BoolTest::ne); | |
1329 IfNode* iff = kit.create_and_map_if(kit.control(), p, PROB_MIN, COUNT_UNKNOWN); | |
1330 Node* notnull = __ IfTrue(iff); | |
1331 Node* isnull = __ IfFalse(iff); | |
1250 | 1332 kit.set_control(notnull); // set control for the cast_not_null |
1080 | 1333 r->init_req(1, notnull); |
1250 | 1334 phi->init_req(1, kit.cast_not_null(arg, false)); |
1080 | 1335 r->init_req(2, isnull); |
1336 phi->init_req(2, null_string); | |
1337 kit.set_control(r); | |
1338 C->record_for_igvn(r); | |
1339 C->record_for_igvn(phi); | |
1340 // replace the argument with the null checked version | |
1341 arg = phi; | |
1342 sc->set_argument(argi, arg); | |
1343 } | |
1344 // Node* offset = kit.make_load(NULL, kit.basic_plus_adr(arg, arg, offset_offset), | |
1345 // TypeInt::INT, T_INT, offset_field_idx); | |
3283
01fd6090fdd8
7032162: assert(flat != TypePtr::BOTTOM) failed: cannot alias-analyze an untyped ptr
never
parents:
2426
diff
changeset
|
1346 Node* count = kit.make_load(kit.control(), kit.basic_plus_adr(arg, arg, java_lang_String::count_offset_in_bytes()), |
1080 | 1347 TypeInt::INT, T_INT, count_field_idx); |
1348 length = __ AddI(length, count); | |
1349 string_sizes->init_req(argi, NULL); | |
1350 break; | |
1351 } | |
1352 case StringConcat::CharMode: { | |
1353 // one character only | |
1354 length = __ AddI(length, __ intcon(1)); | |
1355 break; | |
1356 } | |
1357 default: | |
1358 ShouldNotReachHere(); | |
1359 } | |
1360 if (argi > 0) { | |
1361 // Check that the sum hasn't overflowed | |
1362 IfNode* iff = kit.create_and_map_if(kit.control(), | |
1363 __ Bool(__ CmpI(length, __ intcon(0)), BoolTest::lt), | |
1364 PROB_MIN, COUNT_UNKNOWN); | |
1365 kit.set_control(__ IfFalse(iff)); | |
1366 overflow->set_req(argi, __ IfTrue(iff)); | |
1367 } | |
1368 } | |
1369 | |
1370 { | |
1371 // Hook | |
1372 PreserveJVMState pjvms(&kit); | |
1373 kit.set_control(overflow); | |
2090
2ddb2fab82cb
7009359: HS with -XX:+AggressiveOpts optimize new StringBuffer(null) so it does not throw NPE as expected
kvn
parents:
1972
diff
changeset
|
1374 C->record_for_igvn(overflow); |
1080 | 1375 kit.uncommon_trap(Deoptimization::Reason_intrinsic, |
1376 Deoptimization::Action_make_not_entrant); | |
1377 } | |
1378 | |
1379 // length now contains the number of characters needed for the | |
1380 // char[] so create a new AllocateArray for the char[] | |
1381 Node* char_array = NULL; | |
1382 { | |
1383 PreserveReexecuteState preexecs(&kit); | |
1384 // The original jvms is for an allocation of either a String or | |
1385 // StringBuffer so no stack adjustment is necessary for proper | |
1386 // reexecution. If we deoptimize in the slow path the bytecode | |
1387 // will be reexecuted and the char[] allocation will be thrown away. | |
1388 kit.jvms()->set_should_reexecute(true); | |
1389 char_array = kit.new_array(__ makecon(TypeKlassPtr::make(ciTypeArrayKlass::make(T_CHAR))), | |
1390 length, 1); | |
1391 } | |
1392 | |
1393 // Mark the allocation so that zeroing is skipped since the code | |
1394 // below will overwrite the entire array | |
1395 AllocateArrayNode* char_alloc = AllocateArrayNode::Ideal_array_allocation(char_array, _gvn); | |
1396 char_alloc->maybe_set_complete(_gvn); | |
1397 | |
1398 // Now copy the string representations into the final char[] | |
1399 Node* start = __ intcon(0); | |
1400 for (int argi = 0; argi < sc->num_arguments(); argi++) { | |
1401 Node* arg = sc->argument(argi); | |
1402 switch (sc->mode(argi)) { | |
1403 case StringConcat::IntMode: { | |
1404 Node* end = __ AddI(start, string_sizes->in(argi)); | |
1405 // getChars words backwards so pass the ending point as well as the start | |
1406 int_getChars(kit, arg, char_array, start, end); | |
1407 start = end; | |
1408 break; | |
1409 } | |
2090
2ddb2fab82cb
7009359: HS with -XX:+AggressiveOpts optimize new StringBuffer(null) so it does not throw NPE as expected
kvn
parents:
1972
diff
changeset
|
1410 case StringConcat::StringNullCheckMode: |
1080 | 1411 case StringConcat::StringMode: { |
1412 start = copy_string(kit, arg, char_array, start); | |
1413 break; | |
1414 } | |
1415 case StringConcat::CharMode: { | |
1416 __ store_to_memory(kit.control(), kit.array_element_address(char_array, start, T_CHAR), | |
1417 arg, T_CHAR, char_adr_idx); | |
1418 start = __ AddI(start, __ intcon(1)); | |
1419 break; | |
1420 } | |
1421 default: | |
1422 ShouldNotReachHere(); | |
1423 } | |
1424 } | |
1425 | |
1426 // If we're not reusing an existing String allocation then allocate one here. | |
1427 Node* result = sc->string_alloc(); | |
1428 if (result == NULL) { | |
1429 PreserveReexecuteState preexecs(&kit); | |
1430 // The original jvms is for an allocation of either a String or | |
1431 // StringBuffer so no stack adjustment is necessary for proper | |
1432 // reexecution. | |
1433 kit.jvms()->set_should_reexecute(true); | |
1434 result = kit.new_instance(__ makecon(TypeKlassPtr::make(C->env()->String_klass()))); | |
1435 } | |
1436 | |
1437 // Intialize the string | |
1438 kit.store_to_memory(kit.control(), kit.basic_plus_adr(result, java_lang_String::offset_offset_in_bytes()), | |
1439 __ intcon(0), T_INT, offset_field_idx); | |
1440 kit.store_to_memory(kit.control(), kit.basic_plus_adr(result, java_lang_String::count_offset_in_bytes()), | |
1441 length, T_INT, count_field_idx); | |
1442 kit.store_to_memory(kit.control(), kit.basic_plus_adr(result, java_lang_String::value_offset_in_bytes()), | |
1443 char_array, T_OBJECT, value_field_idx); | |
1444 | |
1445 // hook up the outgoing control and result | |
1446 kit.replace_call(sc->end(), result); | |
1447 | |
1448 // Unhook any hook nodes | |
1449 string_sizes->disconnect_inputs(NULL); | |
1450 sc->cleanup(); | |
1451 } |