--- old/src/share/vm/opto/library_call.cpp 2015-11-19 18:00:08.332045312 +0100 +++ new/src/share/vm/opto/library_call.cpp 2015-11-19 18:00:08.264045315 +0100 @@ -152,6 +152,8 @@ 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 @@ 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 @@ 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 @@ //------------------------------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 @@ 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 @@ 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 @@ //-----------------------------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* 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* 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(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 @@ 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 @@ 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 @@ // 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 @@ (!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 @@ //------------------------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 @@ 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 @@ 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 @@ } //------------------------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 @@ 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