8142303: C2 compilation fails with "bad AD file"
Move range checks into intrinsics for String methods. Reviewed-by: kvn, aph
This commit is contained in:
parent
b66410f7b7
commit
039050a9f7
@ -152,6 +152,8 @@ class LibraryCallKit : public GraphKit {
|
||||
Node* generate_limit_guard(Node* offset, Node* subseq_length,
|
||||
Node* array_length,
|
||||
RegionNode* region);
|
||||
void generate_string_range_check(Node* array, Node* offset,
|
||||
Node* length, bool char_count);
|
||||
Node* generate_current_thread(Node* &tls_output);
|
||||
Node* load_mirror_from_klass(Node* klass);
|
||||
Node* load_klass_from_mirror_common(Node* mirror, bool never_see_null,
|
||||
@ -204,6 +206,8 @@ class LibraryCallKit : public GraphKit {
|
||||
bool inline_string_compareTo(StrIntrinsicNode::ArgEnc ae);
|
||||
bool inline_string_indexOf(StrIntrinsicNode::ArgEnc ae);
|
||||
bool inline_string_indexOfI(StrIntrinsicNode::ArgEnc ae);
|
||||
Node* make_indexOf_node(Node* src_start, Node* src_count, Node* tgt_start, Node* tgt_count,
|
||||
RegionNode* region, Node* phi, StrIntrinsicNode::ArgEnc ae);
|
||||
bool inline_string_indexOfChar();
|
||||
bool inline_string_equals(StrIntrinsicNode::ArgEnc ae);
|
||||
bool inline_string_toBytesU();
|
||||
@ -897,6 +901,31 @@ inline Node* LibraryCallKit::generate_limit_guard(Node* offset,
|
||||
return is_over;
|
||||
}
|
||||
|
||||
// Emit range checks for the given String.value byte array
|
||||
void LibraryCallKit::generate_string_range_check(Node* array, Node* offset, Node* count, bool char_count) {
|
||||
if (stopped()) {
|
||||
return; // already stopped
|
||||
}
|
||||
RegionNode* bailout = new RegionNode(1);
|
||||
record_for_igvn(bailout);
|
||||
if (char_count) {
|
||||
// Convert char count to byte count
|
||||
count = _gvn.transform(new LShiftINode(count, intcon(1)));
|
||||
}
|
||||
|
||||
// Offset and count must not be negative
|
||||
generate_negative_guard(offset, bailout);
|
||||
generate_negative_guard(count, bailout);
|
||||
// Offset + count must not exceed length of array
|
||||
generate_limit_guard(offset, count, load_array_length(array), bailout);
|
||||
|
||||
if (bailout->req() > 1) {
|
||||
PreserveJVMState pjvms(this);
|
||||
set_control(_gvn.transform(bailout));
|
||||
uncommon_trap(Deoptimization::Reason_intrinsic,
|
||||
Deoptimization::Action_maybe_recompile);
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------generate_current_thread--------------------
|
||||
Node* LibraryCallKit::generate_current_thread(Node* &tls_output) {
|
||||
@ -1016,7 +1045,9 @@ bool LibraryCallKit::inline_array_equals(StrIntrinsicNode::ArgEnc ae) {
|
||||
|
||||
//------------------------------inline_hasNegatives------------------------------
|
||||
bool LibraryCallKit::inline_hasNegatives() {
|
||||
if (too_many_traps(Deoptimization::Reason_intrinsic)) return false;
|
||||
if (too_many_traps(Deoptimization::Reason_intrinsic)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
assert(callee()->signature()->size() == 3, "hasNegatives has 3 parameters");
|
||||
// no receiver since it is static method
|
||||
@ -1024,26 +1055,14 @@ bool LibraryCallKit::inline_hasNegatives() {
|
||||
Node* offset = argument(1);
|
||||
Node* len = argument(2);
|
||||
|
||||
RegionNode* bailout = new RegionNode(1);
|
||||
record_for_igvn(bailout);
|
||||
|
||||
// offset must not be negative.
|
||||
generate_negative_guard(offset, bailout);
|
||||
|
||||
// offset + length must not exceed length of ba.
|
||||
generate_limit_guard(offset, len, load_array_length(ba), bailout);
|
||||
|
||||
if (bailout->req() > 1) {
|
||||
PreserveJVMState pjvms(this);
|
||||
set_control(_gvn.transform(bailout));
|
||||
uncommon_trap(Deoptimization::Reason_intrinsic,
|
||||
Deoptimization::Action_maybe_recompile);
|
||||
}
|
||||
if (!stopped()) {
|
||||
Node* ba_start = array_element_address(ba, offset, T_BYTE);
|
||||
Node* result = new HasNegativesNode(control(), memory(TypeAryPtr::BYTES), ba_start, len);
|
||||
set_result(_gvn.transform(result));
|
||||
// Range checks
|
||||
generate_string_range_check(ba, offset, len, false);
|
||||
if (stopped()) {
|
||||
return true;
|
||||
}
|
||||
Node* ba_start = array_element_address(ba, offset, T_BYTE);
|
||||
Node* result = new HasNegativesNode(control(), memory(TypeAryPtr::BYTES), ba_start, len);
|
||||
set_result(_gvn.transform(result));
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1124,30 +1143,10 @@ bool LibraryCallKit::inline_string_indexOf(StrIntrinsicNode::ArgEnc ae) {
|
||||
tgt_count = _gvn.transform(new RShiftINode(tgt_count, intcon(1)));
|
||||
}
|
||||
|
||||
// Check for substr count > string count
|
||||
Node* cmp = _gvn.transform(new CmpINode(tgt_count, src_count));
|
||||
Node* bol = _gvn.transform(new BoolNode(cmp, BoolTest::gt));
|
||||
Node* if_gt = generate_slow_guard(bol, NULL);
|
||||
if (if_gt != NULL) {
|
||||
result_phi->init_req(2, intcon(-1));
|
||||
result_rgn->init_req(2, if_gt);
|
||||
}
|
||||
|
||||
if (!stopped()) {
|
||||
// Check for substr count == 0
|
||||
cmp = _gvn.transform(new CmpINode(tgt_count, intcon(0)));
|
||||
bol = _gvn.transform(new BoolNode(cmp, BoolTest::eq));
|
||||
Node* if_zero = generate_slow_guard(bol, NULL);
|
||||
if (if_zero != NULL) {
|
||||
result_phi->init_req(3, intcon(0));
|
||||
result_rgn->init_req(3, if_zero);
|
||||
}
|
||||
}
|
||||
|
||||
if (!stopped()) {
|
||||
Node* result = make_string_method_node(Op_StrIndexOf, src_start, src_count, tgt_start, tgt_count, ae);
|
||||
result_phi->init_req(1, result);
|
||||
result_rgn->init_req(1, control());
|
||||
Node* result = make_indexOf_node(src_start, src_count, tgt_start, tgt_count, result_rgn, result_phi, ae);
|
||||
if (result != NULL) {
|
||||
result_phi->init_req(3, result);
|
||||
result_rgn->init_req(3, control());
|
||||
}
|
||||
set_control(_gvn.transform(result_rgn));
|
||||
record_for_igvn(result_rgn);
|
||||
@ -1158,44 +1157,53 @@ bool LibraryCallKit::inline_string_indexOf(StrIntrinsicNode::ArgEnc ae) {
|
||||
|
||||
//-----------------------------inline_string_indexOf-----------------------
|
||||
bool LibraryCallKit::inline_string_indexOfI(StrIntrinsicNode::ArgEnc ae) {
|
||||
if (too_many_traps(Deoptimization::Reason_intrinsic)) {
|
||||
return false;
|
||||
}
|
||||
if (!Matcher::has_match_rule(Op_StrIndexOf) || !UseSSE42Intrinsics) {
|
||||
return false;
|
||||
}
|
||||
assert(callee()->signature()->size() == 5, "String.indexOf() has 5 arguments");
|
||||
Node* src = argument(0); // byte[]
|
||||
Node* src_count = argument(1);
|
||||
Node* src_count = argument(1); // char count
|
||||
Node* tgt = argument(2); // byte[]
|
||||
Node* tgt_count = argument(3);
|
||||
Node* from_index = argument(4);
|
||||
|
||||
// Java code which calls this method has range checks for from_index value.
|
||||
src_count = _gvn.transform(new SubINode(src_count, from_index));
|
||||
Node* tgt_count = argument(3); // char count
|
||||
Node* from_index = argument(4); // char index
|
||||
|
||||
// Multiply byte array index by 2 if String is UTF16 encoded
|
||||
Node* src_offset = (ae == StrIntrinsicNode::LL) ? from_index : _gvn.transform(new LShiftINode(from_index, intcon(1)));
|
||||
src_count = _gvn.transform(new SubINode(src_count, from_index));
|
||||
Node* src_start = array_element_address(src, src_offset, T_BYTE);
|
||||
Node* tgt_start = array_element_address(tgt, intcon(0), T_BYTE);
|
||||
|
||||
Node* result = make_string_method_node(Op_StrIndexOf, src_start, src_count, tgt_start, tgt_count, ae);
|
||||
// Range checks
|
||||
generate_string_range_check(src, src_offset, src_count, ae != StrIntrinsicNode::LL);
|
||||
generate_string_range_check(tgt, intcon(0), tgt_count, ae == StrIntrinsicNode::UU);
|
||||
if (stopped()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// The result is index relative to from_index if substring was found, -1 otherwise.
|
||||
// Generate code which will fold into cmove.
|
||||
RegionNode* region = new RegionNode(3);
|
||||
RegionNode* region = new RegionNode(5);
|
||||
Node* phi = new PhiNode(region, TypeInt::INT);
|
||||
|
||||
Node* cmp = _gvn.transform(new CmpINode(result, intcon(0)));
|
||||
Node* bol = _gvn.transform(new BoolNode(cmp, BoolTest::lt));
|
||||
Node* result = make_indexOf_node(src_start, src_count, tgt_start, tgt_count, region, phi, ae);
|
||||
if (result != NULL) {
|
||||
// The result is index relative to from_index if substring was found, -1 otherwise.
|
||||
// Generate code which will fold into cmove.
|
||||
Node* cmp = _gvn.transform(new CmpINode(result, intcon(0)));
|
||||
Node* bol = _gvn.transform(new BoolNode(cmp, BoolTest::lt));
|
||||
|
||||
Node* if_lt = generate_slow_guard(bol, NULL);
|
||||
if (if_lt != NULL) {
|
||||
// result == -1
|
||||
phi->init_req(2, result);
|
||||
region->init_req(2, if_lt);
|
||||
}
|
||||
if (!stopped()) {
|
||||
result = _gvn.transform(new AddINode(result, from_index));
|
||||
phi->init_req(1, result);
|
||||
region->init_req(1, control());
|
||||
Node* if_lt = generate_slow_guard(bol, NULL);
|
||||
if (if_lt != NULL) {
|
||||
// result == -1
|
||||
phi->init_req(3, result);
|
||||
region->init_req(3, if_lt);
|
||||
}
|
||||
if (!stopped()) {
|
||||
result = _gvn.transform(new AddINode(result, from_index));
|
||||
phi->init_req(4, result);
|
||||
region->init_req(4, control());
|
||||
}
|
||||
}
|
||||
|
||||
set_control(_gvn.transform(region));
|
||||
@ -1205,8 +1213,38 @@ bool LibraryCallKit::inline_string_indexOfI(StrIntrinsicNode::ArgEnc ae) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Create StrIndexOfNode with fast path checks
|
||||
Node* LibraryCallKit::make_indexOf_node(Node* src_start, Node* src_count, Node* tgt_start, Node* tgt_count,
|
||||
RegionNode* region, Node* phi, StrIntrinsicNode::ArgEnc ae) {
|
||||
// Check for substr count > string count
|
||||
Node* cmp = _gvn.transform(new CmpINode(tgt_count, src_count));
|
||||
Node* bol = _gvn.transform(new BoolNode(cmp, BoolTest::gt));
|
||||
Node* if_gt = generate_slow_guard(bol, NULL);
|
||||
if (if_gt != NULL) {
|
||||
phi->init_req(1, intcon(-1));
|
||||
region->init_req(1, if_gt);
|
||||
}
|
||||
if (!stopped()) {
|
||||
// Check for substr count == 0
|
||||
cmp = _gvn.transform(new CmpINode(tgt_count, intcon(0)));
|
||||
bol = _gvn.transform(new BoolNode(cmp, BoolTest::eq));
|
||||
Node* if_zero = generate_slow_guard(bol, NULL);
|
||||
if (if_zero != NULL) {
|
||||
phi->init_req(2, intcon(0));
|
||||
region->init_req(2, if_zero);
|
||||
}
|
||||
}
|
||||
if (!stopped()) {
|
||||
return make_string_method_node(Op_StrIndexOf, src_start, src_count, tgt_start, tgt_count, ae);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//-----------------------------inline_string_indexOfChar-----------------------
|
||||
bool LibraryCallKit::inline_string_indexOfChar() {
|
||||
if (too_many_traps(Deoptimization::Reason_intrinsic)) {
|
||||
return false;
|
||||
}
|
||||
if (!Matcher::has_match_rule(Op_StrIndexOfChar) || !(UseSSE > 4)) {
|
||||
return false;
|
||||
}
|
||||
@ -1218,9 +1256,14 @@ bool LibraryCallKit::inline_string_indexOfChar() {
|
||||
|
||||
Node* src_offset = _gvn.transform(new LShiftINode(from_index, intcon(1)));
|
||||
Node* src_start = array_element_address(src, src_offset, T_BYTE);
|
||||
|
||||
Node* src_count = _gvn.transform(new SubINode(max, from_index));
|
||||
|
||||
// Range checks
|
||||
generate_string_range_check(src, src_offset, src_count, true);
|
||||
if (stopped()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
RegionNode* region = new RegionNode(3);
|
||||
Node* phi = new PhiNode(region, TypeInt::INT);
|
||||
|
||||
@ -1256,6 +1299,9 @@ bool LibraryCallKit::inline_string_indexOfChar() {
|
||||
// void StringLatin1.inflate(byte[] src, int srcOff, char[] dst, int dstOff, int len)
|
||||
// void StringLatin1.inflate(byte[] src, int srcOff, byte[] dst, int dstOff, int len)
|
||||
bool LibraryCallKit::inline_string_copy(bool compress) {
|
||||
if (too_many_traps(Deoptimization::Reason_intrinsic)) {
|
||||
return false;
|
||||
}
|
||||
int nargs = 5; // 2 oops, 3 ints
|
||||
assert(callee()->signature()->size() == nargs, "string copy has 5 arguments");
|
||||
|
||||
@ -1278,6 +1324,13 @@ bool LibraryCallKit::inline_string_copy(bool compress) {
|
||||
(!compress && src_elem == T_BYTE && (dst_elem == T_BYTE || dst_elem == T_CHAR)),
|
||||
"Unsupported array types for inline_string_copy");
|
||||
|
||||
// Range checks
|
||||
generate_string_range_check(src, src_offset, length, compress && src_elem == T_BYTE);
|
||||
generate_string_range_check(dst, dst_offset, length, !compress && dst_elem == T_BYTE);
|
||||
if (stopped()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Convert char[] offsets to byte[] offsets
|
||||
if (compress && src_elem == T_BYTE) {
|
||||
src_offset = _gvn.transform(new LShiftINode(src_offset, intcon(1)));
|
||||
@ -1329,6 +1382,9 @@ bool LibraryCallKit::inline_string_copy(bool compress) {
|
||||
//------------------------inline_string_toBytesU--------------------------
|
||||
// public static byte[] StringUTF16.toBytes(char[] value, int off, int len)
|
||||
bool LibraryCallKit::inline_string_toBytesU() {
|
||||
if (too_many_traps(Deoptimization::Reason_intrinsic)) {
|
||||
return false;
|
||||
}
|
||||
// Get the arguments.
|
||||
Node* value = argument(0);
|
||||
Node* offset = argument(1);
|
||||
@ -1347,8 +1403,11 @@ bool LibraryCallKit::inline_string_toBytesU() {
|
||||
RegionNode* bailout = new RegionNode(1);
|
||||
record_for_igvn(bailout);
|
||||
|
||||
// Make sure that resulting byte[] length does not overflow Integer.MAX_VALUE
|
||||
// Range checks
|
||||
generate_negative_guard(offset, bailout);
|
||||
generate_negative_guard(length, bailout);
|
||||
generate_limit_guard(offset, length, load_array_length(value), bailout);
|
||||
// Make sure that resulting byte[] length does not overflow Integer.MAX_VALUE
|
||||
generate_limit_guard(length, intcon(0), intcon(max_jint/2), bailout);
|
||||
|
||||
if (bailout->req() > 1) {
|
||||
@ -1357,9 +1416,9 @@ bool LibraryCallKit::inline_string_toBytesU() {
|
||||
uncommon_trap(Deoptimization::Reason_intrinsic,
|
||||
Deoptimization::Action_maybe_recompile);
|
||||
}
|
||||
if (stopped()) return true;
|
||||
|
||||
// Range checks are done by caller.
|
||||
if (stopped()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Node* size = _gvn.transform(new LShiftINode(length, intcon(1)));
|
||||
Node* klass_node = makecon(TypeKlassPtr::make(ciTypeArrayKlass::make(T_BYTE)));
|
||||
@ -1412,12 +1471,14 @@ bool LibraryCallKit::inline_string_toBytesU() {
|
||||
}
|
||||
|
||||
//------------------------inline_string_getCharsU--------------------------
|
||||
// public void StringUTF16.getChars(byte[] value, int srcBegin, int srcEnd, char dst[], int dstBegin)
|
||||
// public void StringUTF16.getChars(byte[] src, int srcBegin, int srcEnd, char dst[], int dstBegin)
|
||||
bool LibraryCallKit::inline_string_getCharsU() {
|
||||
if (too_many_traps(Deoptimization::Reason_intrinsic)) return false;
|
||||
if (too_many_traps(Deoptimization::Reason_intrinsic)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the arguments.
|
||||
Node* value = argument(0);
|
||||
Node* src = argument(0);
|
||||
Node* src_begin = argument(1);
|
||||
Node* src_end = argument(2); // exclusive offset (i < src_end)
|
||||
Node* dst = argument(3);
|
||||
@ -1428,21 +1489,26 @@ bool LibraryCallKit::inline_string_getCharsU() {
|
||||
AllocateArrayNode* alloc = tightly_coupled_allocation(dst, NULL);
|
||||
|
||||
// Check if a null path was taken unconditionally.
|
||||
value = null_check(value);
|
||||
src = null_check(src);
|
||||
dst = null_check(dst);
|
||||
if (stopped()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Range checks are done by caller.
|
||||
|
||||
// Get length and convert char[] offset to byte[] offset
|
||||
Node* length = _gvn.transform(new SubINode(src_end, src_begin));
|
||||
src_begin = _gvn.transform(new LShiftINode(src_begin, intcon(1)));
|
||||
|
||||
// Range checks
|
||||
generate_string_range_check(src, src_begin, length, true);
|
||||
generate_string_range_check(dst, dst_begin, length, false);
|
||||
if (stopped()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!stopped()) {
|
||||
// Calculate starting addresses.
|
||||
Node* src_start = array_element_address(value, src_begin, T_BYTE);
|
||||
Node* src_start = array_element_address(src, src_begin, T_BYTE);
|
||||
Node* dst_start = array_element_address(dst, dst_begin, T_CHAR);
|
||||
|
||||
// Check if array addresses are aligned to HeapWordSize
|
||||
|
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 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
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8142303
|
||||
* @summary Tests handling of invalid array indices in C2 intrinsic if explicit range check in Java code is not inlined.
|
||||
* @run main/othervm -XX:CompileCommand=inline,java.lang.String::* -XX:CompileCommand=inline,java.lang.StringUTF16::* -XX:CompileCommand=exclude,java.lang.String::checkBoundsOffCount TestStringConstruction
|
||||
*/
|
||||
public class TestStringConstruction {
|
||||
|
||||
public static void main(String[] args) {
|
||||
char[] chars = new char[42];
|
||||
for (int i = 0; i < 10_000; ++i) {
|
||||
test(chars);
|
||||
}
|
||||
}
|
||||
|
||||
private static String test(char[] chars) {
|
||||
try {
|
||||
// The constructor calls String::checkBoundsOffCount(-1, 42) to perform
|
||||
// range checks on offset and count. If this method is not inlined, C2
|
||||
// does not know about the explicit range checks and does not cut off the
|
||||
// dead code. As a result, -1 is fed as offset into the StringUTF16.compress
|
||||
// intrinsic which is replaced by TOP and causes a failure in the matcher.
|
||||
return new String(chars, -1 , 42);
|
||||
} catch (Exception e) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user