< prev index next >

src/share/vm/opto/library_call.cpp

Print this page

        

@@ -150,10 +150,12 @@
                                 // 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,10 +204,12 @@
 
   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,10 +899,35 @@
   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,38 +1043,28 @@
   return true;
 }
 
 //------------------------------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
   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);
+  // Range checks
+  generate_string_range_check(ba, offset, len, false);
+  if (stopped()) {
+    return true;
   }
-  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);

@@ -1122,93 +1141,112 @@
   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());
+  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);
+  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* 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);
+      phi->init_req(3, result);
+      region->init_req(3, if_lt);
   }
   if (!stopped()) {
     result = _gvn.transform(new AddINode(result, from_index));
-    phi->init_req(1, result);
-    region->init_req(1, control());
+      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,13 +1254,18 @@
   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,10 +1297,13 @@
 //   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,10 +1322,17 @@
   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,10 +1380,13 @@
 #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,23 +1401,26 @@
     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
+    // 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;
-
-    // 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)));
     newcopy = new_array(klass_node, size, 0);  // no arguments to push
     AllocateArrayNode* alloc = tightly_coupled_allocation(newcopy, NULL);

@@ -1410,41 +1469,48 @@
   }
   return true;
 }
 
 //------------------------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);
   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);
+  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
     const TypeInt* tsrc = gvn().type(src_begin)->is_int();
     const TypeInt* tdst = gvn().type(dst_begin)->is_int();
< prev index next >