< prev index next >
src/share/vm/opto/library_call.cpp
Print this page
*** 150,159 ****
--- 150,161 ----
// resulting CastII of index:
Node* *pos_index = NULL);
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,
RegionNode* region, int null_path,
int offset);
*** 202,211 ****
--- 204,215 ----
Node* make_string_method_node(int opcode, Node* str1_start, Node* cnt1, Node* str2_start, Node* cnt2, StrIntrinsicNode::ArgEnc ae);
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();
bool inline_string_getCharsU();
bool inline_string_copy(bool compress);
*** 895,904 ****
--- 899,933 ----
Node* bol_lt = _gvn.transform(new BoolNode(cmp_lt, BoolTest::lt));
Node* is_over = generate_guard(bol_lt, region, PROB_MIN);
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) {
ciKlass* thread_klass = env()->Thread_klass();
const Type* thread_type = TypeOopPtr::make_from_klass(thread_klass)->cast_to_ptr_type(TypePtr::NotNull);
*** 1014,1051 ****
return true;
}
//------------------------------inline_hasNegatives------------------------------
bool LibraryCallKit::inline_hasNegatives() {
! 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
Node* ba = argument(0);
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));
- }
return true;
}
bool LibraryCallKit::inline_objects_checkIndex() {
Node* index = argument(0);
--- 1043,1070 ----
return true;
}
//------------------------------inline_hasNegatives------------------------------
bool LibraryCallKit::inline_hasNegatives() {
! 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
Node* ba = argument(0);
Node* offset = argument(1);
Node* len = argument(2);
! // 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;
}
bool LibraryCallKit::inline_objects_checkIndex() {
Node* index = argument(0);
*** 1122,1214 ****
if (ae == StrIntrinsicNode::UU) {
// Divide substring size by 2 if String is UTF16 encoded
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());
}
set_control(_gvn.transform(result_rgn));
record_for_igvn(result_rgn);
set_result(_gvn.transform(result_phi));
return true;
}
//-----------------------------inline_string_indexOf-----------------------
bool LibraryCallKit::inline_string_indexOfI(StrIntrinsicNode::ArgEnc ae) {
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* 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));
// 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)));
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);
! // 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);
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());
}
set_control(_gvn.transform(region));
record_for_igvn(region);
set_result(_gvn.transform(phi));
return true;
}
//-----------------------------inline_string_indexOfChar-----------------------
bool LibraryCallKit::inline_string_indexOfChar() {
if (!Matcher::has_match_rule(Op_StrIndexOfChar) || !(UseSSE > 4)) {
return false;
}
assert(callee()->signature()->size() == 4, "String.indexOfChar() has 4 arguments");
Node* src = argument(0); // byte[]
--- 1141,1252 ----
if (ae == StrIntrinsicNode::UU) {
// Divide substring size by 2 if String is UTF16 encoded
tgt_count = _gvn.transform(new RShiftINode(tgt_count, intcon(1)));
}
! 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);
set_result(_gvn.transform(result_phi));
return true;
}
//-----------------------------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); // char count
Node* tgt = argument(2); // byte[]
! 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);
! // 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;
! }
! RegionNode* region = new RegionNode(5);
Node* phi = new PhiNode(region, TypeInt::INT);
+ 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));
record_for_igvn(region);
set_result(_gvn.transform(phi));
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;
}
assert(callee()->signature()->size() == 4, "String.indexOfChar() has 4 arguments");
Node* src = argument(0); // byte[]
*** 1216,1228 ****
Node* from_index = argument(2);
Node* max = argument(3);
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));
RegionNode* region = new RegionNode(3);
Node* phi = new PhiNode(region, TypeInt::INT);
Node* result = new StrIndexOfCharNode(control(), memory(TypeAryPtr::BYTES), src_start, src_count, tgt, StrIntrinsicNode::none);
C->set_has_split_ifs(true); // Has chance for split-if optimization
--- 1254,1271 ----
Node* from_index = argument(2);
Node* max = argument(3);
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);
Node* result = new StrIndexOfCharNode(control(), memory(TypeAryPtr::BYTES), src_start, src_count, tgt, StrIntrinsicNode::none);
C->set_has_split_ifs(true); // Has chance for split-if optimization
*** 1254,1263 ****
--- 1297,1309 ----
// int StringUTF16.compress(byte[] src, int srcOff, byte[] dst, int dstOff, int len)
// compressIt == false --> generate an inflated copy operation (inflate byte[] to char[]/byte[])
// 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");
Node* src = argument(0);
Node* src_offset = argument(1);
*** 1276,1285 ****
--- 1322,1338 ----
BasicType dst_elem = dst_type->isa_aryptr()->klass()->as_array_klass()->element_type()->basic_type();
assert((compress && dst_elem == T_BYTE && (src_elem == T_BYTE || src_elem == T_CHAR)) ||
(!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)));
} else if (!compress && dst_elem == T_BYTE) {
dst_offset = _gvn.transform(new LShiftINode(dst_offset, intcon(1)));
*** 1327,1336 ****
--- 1380,1392 ----
#endif //_LP64
//------------------------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);
Node* length = argument(2);
*** 1345,1367 ****
value = null_check(value);
RegionNode* bailout = new RegionNode(1);
record_for_igvn(bailout);
! // Make sure that resulting byte[] length does not overflow Integer.MAX_VALUE
generate_negative_guard(length, bailout);
generate_limit_guard(length, intcon(0), intcon(max_jint/2), bailout);
if (bailout->req() > 1) {
PreserveJVMState pjvms(this);
set_control(_gvn.transform(bailout));
uncommon_trap(Deoptimization::Reason_intrinsic,
Deoptimization::Action_maybe_recompile);
}
! if (stopped()) return true;
!
! // Range checks are done by caller.
Node* size = _gvn.transform(new LShiftINode(length, intcon(1)));
Node* klass_node = makecon(TypeKlassPtr::make(ciTypeArrayKlass::make(T_BYTE)));
newcopy = new_array(klass_node, size, 0); // no arguments to push
AllocateArrayNode* alloc = tightly_coupled_allocation(newcopy, NULL);
--- 1401,1426 ----
value = null_check(value);
RegionNode* bailout = new RegionNode(1);
record_for_igvn(bailout);
! // 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) {
PreserveJVMState pjvms(this);
set_control(_gvn.transform(bailout));
uncommon_trap(Deoptimization::Reason_intrinsic,
Deoptimization::Action_maybe_recompile);
}
! if (stopped()) {
! return true;
! }
Node* size = _gvn.transform(new LShiftINode(length, intcon(1)));
Node* klass_node = makecon(TypeKlassPtr::make(ciTypeArrayKlass::make(T_BYTE)));
newcopy = new_array(klass_node, size, 0); // no arguments to push
AllocateArrayNode* alloc = tightly_coupled_allocation(newcopy, NULL);
*** 1410,1450 ****
}
return true;
}
//------------------------inline_string_getCharsU--------------------------
! // public void StringUTF16.getChars(byte[] value, int srcBegin, int srcEnd, char dst[], int dstBegin)
bool LibraryCallKit::inline_string_getCharsU() {
! if (too_many_traps(Deoptimization::Reason_intrinsic)) return false;
// Get the arguments.
! Node* value = argument(0);
Node* src_begin = argument(1);
Node* src_end = argument(2); // exclusive offset (i < src_end)
Node* dst = argument(3);
Node* dst_begin = argument(4);
// Check for allocation before we add nodes that would confuse
// tightly_coupled_allocation()
AllocateArrayNode* alloc = tightly_coupled_allocation(dst, NULL);
// Check if a null path was taken unconditionally.
! value = null_check(value);
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)));
if (!stopped()) {
// Calculate starting addresses.
! Node* src_start = array_element_address(value, src_begin, T_BYTE);
Node* dst_start = array_element_address(dst, dst_begin, T_CHAR);
// Check if array addresses are aligned to HeapWordSize
const TypeInt* tsrc = gvn().type(src_begin)->is_int();
const TypeInt* tdst = gvn().type(dst_begin)->is_int();
--- 1469,1516 ----
}
return true;
}
//------------------------inline_string_getCharsU--------------------------
! // 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;
! }
// Get the arguments.
! Node* src = argument(0);
Node* src_begin = argument(1);
Node* src_end = argument(2); // exclusive offset (i < src_end)
Node* dst = argument(3);
Node* dst_begin = argument(4);
// Check for allocation before we add nodes that would confuse
// tightly_coupled_allocation()
AllocateArrayNode* alloc = tightly_coupled_allocation(dst, NULL);
// Check if a null path was taken unconditionally.
! src = null_check(src);
dst = null_check(dst);
if (stopped()) {
return true;
}
// 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(src, src_begin, T_BYTE);
Node* dst_start = array_element_address(dst, dst_begin, T_CHAR);
// Check if array addresses are aligned to HeapWordSize
const TypeInt* tsrc = gvn().type(src_begin)->is_int();
const TypeInt* tdst = gvn().type(dst_begin)->is_int();
< prev index next >