8278518: String(byte[], int, int, Charset) constructor and String.translateEscapes() miss bounds check elimination
Co-authored-by: Sergey Tsypanov <stsypanov@openjdk.org> Reviewed-by: kvn, jrose
This commit is contained in:
parent
2ea0edf2c4
commit
0dba170791
src/hotspot/share/ci
test/micro/org/openjdk/bench/vm/compiler
@ -2202,10 +2202,10 @@ bool ciTypeFlow::can_trap(ciBytecodeStream& str) {
|
||||
// ciTypeFlow::clone_loop_heads
|
||||
//
|
||||
// Clone the loop heads
|
||||
bool ciTypeFlow::clone_loop_heads(Loop* lp, StateVector* temp_vector, JsrSet* temp_set) {
|
||||
bool ciTypeFlow::clone_loop_heads(StateVector* temp_vector, JsrSet* temp_set) {
|
||||
bool rslt = false;
|
||||
for (PreorderLoops iter(loop_tree_root()); !iter.done(); iter.next()) {
|
||||
lp = iter.current();
|
||||
Loop* lp = iter.current();
|
||||
Block* head = lp->head();
|
||||
if (lp == loop_tree_root() ||
|
||||
lp->is_irreducible() ||
|
||||
@ -2450,6 +2450,103 @@ void ciTypeFlow::PreorderLoops::next() {
|
||||
}
|
||||
}
|
||||
|
||||
// If the tail is a branch to the head, retrieve how many times that path was taken from profiling
|
||||
int ciTypeFlow::profiled_count(ciTypeFlow::Loop* loop) {
|
||||
ciMethodData* methodData = method()->method_data();
|
||||
if (!methodData->is_mature()) {
|
||||
return 0;
|
||||
}
|
||||
ciTypeFlow::Block* tail = loop->tail();
|
||||
if (tail->control() == -1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ciProfileData* data = methodData->bci_to_data(tail->control());
|
||||
assert(data != NULL, "some profile data expected at branch");
|
||||
|
||||
if (!data->is_JumpData()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ciBytecodeStream iter(method());
|
||||
iter.reset_to_bci(tail->control());
|
||||
|
||||
bool is_an_if = false;
|
||||
bool wide = false;
|
||||
Bytecodes::Code bc = iter.next();
|
||||
switch (bc) {
|
||||
case Bytecodes::_ifeq:
|
||||
case Bytecodes::_ifne:
|
||||
case Bytecodes::_iflt:
|
||||
case Bytecodes::_ifge:
|
||||
case Bytecodes::_ifgt:
|
||||
case Bytecodes::_ifle:
|
||||
case Bytecodes::_if_icmpeq:
|
||||
case Bytecodes::_if_icmpne:
|
||||
case Bytecodes::_if_icmplt:
|
||||
case Bytecodes::_if_icmpge:
|
||||
case Bytecodes::_if_icmpgt:
|
||||
case Bytecodes::_if_icmple:
|
||||
case Bytecodes::_if_acmpeq:
|
||||
case Bytecodes::_if_acmpne:
|
||||
case Bytecodes::_ifnull:
|
||||
case Bytecodes::_ifnonnull:
|
||||
is_an_if = true;
|
||||
break;
|
||||
case Bytecodes::_goto_w:
|
||||
case Bytecodes::_jsr_w:
|
||||
wide = true;
|
||||
break;
|
||||
case Bytecodes::_goto:
|
||||
case Bytecodes::_jsr:
|
||||
break;
|
||||
default:
|
||||
fatal(" invalid bytecode: %s", Bytecodes::name(iter.cur_bc()));
|
||||
}
|
||||
|
||||
GrowableArray<ciTypeFlow::Block*>* succs = tail->successors();
|
||||
|
||||
if (!is_an_if) {
|
||||
assert(((wide ? iter.get_far_dest() : iter.get_dest()) == loop->head()->start()) == (succs->at(ciTypeFlow::GOTO_TARGET) == loop->head()), "branch should lead to loop head");
|
||||
if (succs->at(ciTypeFlow::GOTO_TARGET) == loop->head()) {
|
||||
return method()->scale_count(data->as_JumpData()->taken());
|
||||
}
|
||||
} else {
|
||||
assert((iter.get_dest() == loop->head()->start()) == (succs->at(ciTypeFlow::IF_TAKEN) == loop->head()), "bytecode and CFG not consistent");
|
||||
assert((tail->limit() == loop->head()->start()) == (succs->at(ciTypeFlow::IF_NOT_TAKEN) == loop->head()), "bytecode and CFG not consistent");
|
||||
if (succs->at(ciTypeFlow::IF_TAKEN) == loop->head()) {
|
||||
return method()->scale_count(data->as_JumpData()->taken());
|
||||
} else if (succs->at(ciTypeFlow::IF_NOT_TAKEN) == loop->head()) {
|
||||
return method()->scale_count(data->as_BranchData()->not_taken());
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool ciTypeFlow::Loop::at_insertion_point(Loop* lp, Loop* current) {
|
||||
int lp_pre_order = lp->head()->pre_order();
|
||||
if (current->head()->pre_order() < lp_pre_order) {
|
||||
return true;
|
||||
} else if (current->head()->pre_order() > lp_pre_order) {
|
||||
return false;
|
||||
}
|
||||
// In the case of a shared head, make the most frequent head/tail (as reported by profiling) the inner loop
|
||||
if (current->head() == lp->head()) {
|
||||
int lp_count = outer()->profiled_count(lp);
|
||||
int current_count = outer()->profiled_count(current);
|
||||
if (current_count < lp_count) {
|
||||
return true;
|
||||
} else if (current_count > lp_count) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (current->tail()->pre_order() > lp->tail()->pre_order()) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// ciTypeFlow::Loop::sorted_merge
|
||||
//
|
||||
@ -2467,12 +2564,10 @@ ciTypeFlow::Loop* ciTypeFlow::Loop::sorted_merge(Loop* lp) {
|
||||
int lp_pre_order = lp->head()->pre_order();
|
||||
// Find insertion point for "lp"
|
||||
while (current != NULL) {
|
||||
if (current == lp)
|
||||
if (current == lp) {
|
||||
return leaf; // Already in list
|
||||
if (current->head()->pre_order() < lp_pre_order)
|
||||
break;
|
||||
if (current->head()->pre_order() == lp_pre_order &&
|
||||
current->tail()->pre_order() > lp->tail()->pre_order()) {
|
||||
}
|
||||
if (at_insertion_point(lp, current)) {
|
||||
break;
|
||||
}
|
||||
prev = current;
|
||||
@ -2732,7 +2827,7 @@ void ciTypeFlow::flow_types() {
|
||||
env()->comp_level() >= CompLevel_full_optimization) {
|
||||
// Loop optimizations are not performed on Tier1 compiles.
|
||||
|
||||
bool changed = clone_loop_heads(loop_tree_root(), temp_vector, temp_set);
|
||||
bool changed = clone_loop_heads(temp_vector, temp_set);
|
||||
|
||||
// If some loop heads were cloned, recompute postorder and loop tree
|
||||
if (changed) {
|
||||
|
@ -717,6 +717,9 @@ public:
|
||||
bool _irreducible;
|
||||
LocalSet _def_locals;
|
||||
|
||||
ciTypeFlow* outer() const { return head()->outer(); }
|
||||
bool at_insertion_point(Loop* lp, Loop* current);
|
||||
|
||||
public:
|
||||
Loop(Block* head, Block* tail) :
|
||||
_parent(NULL), _sibling(NULL), _child(NULL),
|
||||
@ -795,7 +798,7 @@ private:
|
||||
bool can_trap(ciBytecodeStream& str);
|
||||
|
||||
// Clone the loop heads. Returns true if any cloning occurred.
|
||||
bool clone_loop_heads(Loop* lp, StateVector* temp_vector, JsrSet* temp_set);
|
||||
bool clone_loop_heads(StateVector* temp_vector, JsrSet* temp_set);
|
||||
|
||||
// Clone lp's head and replace tail's successors with clone.
|
||||
Block* clone_loop_head(Loop* lp, StateVector* temp_vector, JsrSet* temp_set);
|
||||
@ -913,6 +916,8 @@ private:
|
||||
// Create the block map, which indexes blocks in pre_order.
|
||||
void map_blocks();
|
||||
|
||||
int profiled_count(ciTypeFlow::Loop* loop);
|
||||
|
||||
public:
|
||||
// Perform type inference flow analysis.
|
||||
void do_flow();
|
||||
|
@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (c) 2022, Red Hat, Inc. 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
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package org.openjdk.bench.vm.compiler;
|
||||
|
||||
import org.openjdk.jmh.annotations.Benchmark;
|
||||
import org.openjdk.jmh.annotations.Setup;
|
||||
import org.openjdk.jmh.annotations.State;
|
||||
import org.openjdk.jmh.annotations.Scope;
|
||||
import org.openjdk.jmh.annotations.BenchmarkMode;
|
||||
import org.openjdk.jmh.annotations.OutputTimeUnit;
|
||||
import org.openjdk.jmh.annotations.Mode;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@State(Scope.Thread)
|
||||
public class SharedLoopHeader {
|
||||
|
||||
private static final int size = 1000;
|
||||
private static final boolean branch[] = new boolean[size];
|
||||
private static final int count[] = new int[size];
|
||||
|
||||
@Setup
|
||||
public void setup() {
|
||||
for (int i = 0; i < branch.length; i++) {
|
||||
branch[i] = ((i % 10) != 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
@BenchmarkMode(Mode.AverageTime)
|
||||
@OutputTimeUnit(TimeUnit.NANOSECONDS)
|
||||
public void sharedHeader() {
|
||||
int i = 0;
|
||||
while (i < branch.length) {
|
||||
if (branch[i]) {
|
||||
// common branch
|
||||
count[i]++;
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
i += 2;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (c) 2022, Red Hat, Inc. 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
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package org.openjdk.bench.vm.compiler;
|
||||
|
||||
import org.openjdk.jmh.annotations.Benchmark;
|
||||
import org.openjdk.jmh.annotations.BenchmarkMode;
|
||||
import org.openjdk.jmh.annotations.OutputTimeUnit;
|
||||
import org.openjdk.jmh.annotations.State;
|
||||
import org.openjdk.jmh.annotations.Scope;
|
||||
import org.openjdk.jmh.annotations.Mode;
|
||||
import org.openjdk.jmh.annotations.Setup;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
@State(Scope.Thread)
|
||||
@BenchmarkMode(Mode.AverageTime)
|
||||
@OutputTimeUnit(TimeUnit.NANOSECONDS)
|
||||
public class StringConstructorBenchmark {
|
||||
private byte[] array;
|
||||
private String str;
|
||||
|
||||
@Setup
|
||||
public void setup() {
|
||||
str = "Quizdeltagerne spiste jordb\u00e6r med fl\u00f8de, mens cirkusklovnen. \u042f";//Latin1 ending with Russian
|
||||
array = str.getBytes(StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public String newString() {
|
||||
return new String(array, 0, array.length, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public String translateEscapes() {
|
||||
return str.translateEscapes();
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user