Merge ihse-manpages-branch
authorihse
Tue, 30 Oct 2018 13:50:05 +0100
branchihse-manpages-branch
changeset 57010 026ac5eacc7c
parent 57009 41a309ceb98c (current diff)
parent 52327 6fe18b0c0e88 (diff)
child 57033 f54811c07f2a
Merge
--- a/src/hotspot/share/c1/c1_Compiler.cpp	Tue Oct 30 11:46:26 2018 +0100
+++ b/src/hotspot/share/c1/c1_Compiler.cpp	Tue Oct 30 13:50:05 2018 +0100
@@ -79,7 +79,6 @@
 }
 
 int Compiler::code_buffer_size() {
-  assert(SegmentedCodeCache, "Should be only used with a segmented code cache");
   return Compilation::desired_max_code_buffer_size() + Compilation::desired_max_constant_size();
 }
 
@@ -90,10 +89,7 @@
 
   // setup CodeBuffer.  Preallocate a BufferBlob of size
   // NMethodSizeLimit plus some extra space for constants.
-  int code_buffer_size = Compilation::desired_max_code_buffer_size() +
-    Compilation::desired_max_constant_size();
-
-  BufferBlob* buffer_blob = BufferBlob::create("C1 temporary CodeBuffer", code_buffer_size);
+  BufferBlob* buffer_blob = BufferBlob::create("C1 temporary CodeBuffer", code_buffer_size());
   if (buffer_blob != NULL) {
     CompilerThread::current()->set_buffer_blob(buffer_blob);
   }
--- a/src/hotspot/share/code/codeCache.cpp	Tue Oct 30 11:46:26 2018 +0100
+++ b/src/hotspot/share/code/codeCache.cpp	Tue Oct 30 13:50:05 2018 +0100
@@ -274,10 +274,10 @@
   }
   // Make sure we have enough space for VM internal code
   uint min_code_cache_size = CodeCacheMinimumUseSpace DEBUG_ONLY(* 3);
-  if (non_nmethod_size < (min_code_cache_size + code_buffers_size)) {
+  if (non_nmethod_size < min_code_cache_size) {
     vm_exit_during_initialization(err_msg(
         "Not enough space in non-nmethod code heap to run VM: " SIZE_FORMAT "K < " SIZE_FORMAT "K",
-        non_nmethod_size/K, (min_code_cache_size + code_buffers_size)/K));
+        non_nmethod_size/K, min_code_cache_size/K));
   }
 
   // Verify sizes and update flag values
--- a/src/hotspot/share/opto/c2compiler.cpp	Tue Oct 30 11:46:26 2018 +0100
+++ b/src/hotspot/share/opto/c2compiler.cpp	Tue Oct 30 13:50:05 2018 +0100
@@ -604,7 +604,9 @@
   return true;
 }
 
-int C2Compiler::initial_code_buffer_size() {
-  assert(SegmentedCodeCache, "Should be only used with a segmented code cache");
-  return Compile::MAX_inst_size + Compile::MAX_locs_size + initial_const_capacity;
+int C2Compiler::initial_code_buffer_size(int const_size) {
+  // See Compile::init_scratch_buffer_blob
+  int locs_size = sizeof(relocInfo) * Compile::MAX_locs_size;
+  int slop = 2 * CodeSection::end_slop(); // space between sections
+  return Compile::MAX_inst_size + Compile::MAX_stubs_size + const_size + slop + locs_size;
 }
--- a/src/hotspot/share/opto/c2compiler.hpp	Tue Oct 30 11:46:26 2018 +0100
+++ b/src/hotspot/share/opto/c2compiler.hpp	Tue Oct 30 13:50:05 2018 +0100
@@ -26,6 +26,7 @@
 #define SHARE_VM_OPTO_C2COMPILER_HPP
 
 #include "compiler/abstractCompiler.hpp"
+#include "opto/output.hpp"
 
 class C2Compiler : public AbstractCompiler {
  private:
@@ -66,7 +67,7 @@
   virtual bool is_intrinsic_supported(const methodHandle& method, bool is_virtual);
 
   // Initial size of the code buffer (may be increased at runtime)
-  static int initial_code_buffer_size();
+  static int initial_code_buffer_size(int const_size = initial_const_capacity);
 };
 
 #endif // SHARE_VM_OPTO_C2COMPILER_HPP
--- a/src/hotspot/share/opto/compile.cpp	Tue Oct 30 11:46:26 2018 +0100
+++ b/src/hotspot/share/opto/compile.cpp	Tue Oct 30 13:50:05 2018 +0100
@@ -544,9 +544,7 @@
 
     ResourceMark rm;
     _scratch_const_size = const_size;
-    int locs_size = sizeof(relocInfo) * MAX_locs_size;
-    int slop = 2 * CodeSection::end_slop(); // space between sections
-    int size = (MAX_inst_size + MAX_stubs_size + _scratch_const_size + slop + locs_size);
+    int size = C2Compiler::initial_code_buffer_size(const_size);
     blob = BufferBlob::create("Compile::scratch_buffer", size);
     // Record the buffer blob for next time.
     set_scratch_buffer_blob(blob);
--- a/src/hotspot/share/runtime/compilationPolicy.cpp	Tue Oct 30 11:46:26 2018 +0100
+++ b/src/hotspot/share/runtime/compilationPolicy.cpp	Tue Oct 30 13:50:05 2018 +0100
@@ -46,6 +46,13 @@
 #include "utilities/events.hpp"
 #include "utilities/globalDefinitions.hpp"
 
+#ifdef COMPILER1
+#include "c1/c1_Compiler.hpp"
+#endif
+#ifdef COMPILER2
+#include "opto/c2compiler.hpp"
+#endif
+
 CompilationPolicy* CompilationPolicy::_policy;
 elapsedTimer       CompilationPolicy::_accumulated_time;
 bool               CompilationPolicy::_in_vm_startup;
@@ -222,6 +229,19 @@
     // max(log2(8)-1,1) = 2 compiler threads on an 8-way machine.
     // May help big-app startup time.
     _compiler_count = MAX2(log2_intptr(os::active_processor_count())-1,1);
+    // Make sure there is enough space in the code cache to hold all the compiler buffers
+    size_t buffer_size = 1;
+#ifdef COMPILER1
+    buffer_size = is_client_compilation_mode_vm() ? Compiler::code_buffer_size() : buffer_size;
+#endif
+#ifdef COMPILER2
+    buffer_size = is_server_compilation_mode_vm() ? C2Compiler::initial_code_buffer_size() : buffer_size;
+#endif
+    int max_count = (ReservedCodeCacheSize - (CodeCacheMinimumUseSpace DEBUG_ONLY(* 3))) / (int)buffer_size;
+    if (_compiler_count > max_count) {
+      // Lower the compiler count such that all buffers fit into the code cache
+      _compiler_count = MAX2(max_count, 1);
+    }
     FLAG_SET_ERGO(intx, CICompilerCount, _compiler_count);
   } else {
     _compiler_count = CICompilerCount;
--- a/src/hotspot/share/runtime/tieredThresholdPolicy.cpp	Tue Oct 30 11:46:26 2018 +0100
+++ b/src/hotspot/share/runtime/tieredThresholdPolicy.cpp	Tue Oct 30 13:50:05 2018 +0100
@@ -38,6 +38,9 @@
 
 #ifdef TIERED
 
+#include "c1/c1_Compiler.hpp"
+#include "opto/c2compiler.hpp"
+
 template<CompLevel level>
 bool TieredThresholdPolicy::call_predicate_helper(int i, int b, double scale, Method* method) {
   double threshold_scaling;
@@ -215,6 +218,7 @@
 
 void TieredThresholdPolicy::initialize() {
   int count = CICompilerCount;
+  bool c1_only = TieredStopAtLevel < CompLevel_full_optimization;
 #ifdef _LP64
   // Turn on ergonomic compiler count selection
   if (FLAG_IS_DEFAULT(CICompilerCountPerCPU) && FLAG_IS_DEFAULT(CICompilerCount)) {
@@ -225,6 +229,15 @@
     int log_cpu = log2_intptr(os::active_processor_count());
     int loglog_cpu = log2_intptr(MAX2(log_cpu, 1));
     count = MAX2(log_cpu * loglog_cpu * 3 / 2, 2);
+    // Make sure there is enough space in the code cache to hold all the compiler buffers
+    size_t c1_size = Compiler::code_buffer_size();
+    size_t c2_size = C2Compiler::initial_code_buffer_size();
+    size_t buffer_size = c1_only ? c1_size : (c1_size/3 + 2*c2_size/3);
+    int max_count = (ReservedCodeCacheSize - (CodeCacheMinimumUseSpace DEBUG_ONLY(* 3))) / (int)buffer_size;
+    if (count > max_count) {
+      // Lower the compiler count such that all buffers fit into the code cache
+      count = MAX2(max_count, c1_only ? 1 : 2);
+    }
     FLAG_SET_ERGO(intx, CICompilerCount, count);
   }
 #else
@@ -241,7 +254,7 @@
   }
 #endif
 
-  if (TieredStopAtLevel < CompLevel_full_optimization) {
+  if (c1_only) {
     // No C2 compiler thread required
     set_c1_count(count);
   } else {
--- a/src/java.base/share/classes/java/lang/StringConcatHelper.java	Tue Oct 30 11:46:26 2018 +0100
+++ b/src/java.base/share/classes/java/lang/StringConcatHelper.java	Tue Oct 30 13:50:05 2018 +0100
@@ -37,119 +37,104 @@
     }
 
     /**
-     * Check for overflow, throw the exception on overflow.
-     * @param len String length
-     * @return length
+     * Check for overflow, throw exception on overflow.
+     * @param lengthCoder String length and coder
+     * @return lengthCoder
      */
-    private static int checkOverflow(int len) {
-        if (len < 0) {
-            throw new OutOfMemoryError("Overflow: String length out of range");
+    private static long checkOverflow(long lengthCoder) {
+        if ((int)lengthCoder >= 0) {
+            return lengthCoder;
         }
-        return len;
+        throw new OutOfMemoryError("Overflow: String length out of range");
     }
 
     /**
-     * Mix value length into current length
+     * Mix value length and coder into current length and coder.
      * @param current current length
      * @param value   value to mix in
-     * @return new length
+     * @return new length and coder
      */
-    static int mixLen(int current, boolean value) {
+    static long mix(long current, boolean value) {
         return checkOverflow(current + (value ? 4 : 5));
     }
 
     /**
-     * Mix value length into current length
+     * Mix value length and coder into current length and coder.
      * @param current current length
      * @param value   value to mix in
-     * @return new length
+     * @return new length and coder
      */
-    static int mixLen(int current, byte value) {
-        return mixLen(current, (int)value);
+    static long mix(long current, byte value) {
+        return mix(current, (int)value);
     }
 
     /**
-     * Mix value length into current length
+     * Mix value length and coder into current length and coder.
      * @param current current length
      * @param value   value to mix in
-     * @return new length
+     * @return new length and coder
      */
-    static int mixLen(int current, char value) {
-        return checkOverflow(current + 1);
+    static long mix(long current, char value) {
+        return checkOverflow(current + 1) | (StringLatin1.canEncode(value) ? 0 : UTF16);
     }
 
     /**
-     * Mix value length into current length
+     * Mix value length and coder into current length and coder.
      * @param current current length
      * @param value   value to mix in
-     * @return new length
+     * @return new length and coder
      */
-    static int mixLen(int current, short value) {
-        return mixLen(current, (int)value);
+    static long mix(long current, short value) {
+        return mix(current, (int)value);
     }
 
     /**
-     * Mix value length into current length
+     * Mix value length and coder into current length and coder.
      * @param current current length
      * @param value   value to mix in
-     * @return new length
+     * @return new length and coder
      */
-    static int mixLen(int current, int value) {
+    static long mix(long current, int value) {
         return checkOverflow(current + Integer.stringSize(value));
     }
 
     /**
-     * Mix value length into current length
+     * Mix value length and coder into current length and coder.
      * @param current current length
      * @param value   value to mix in
-     * @return new length
+     * @return new length and coder
      */
-    static int mixLen(int current, long value) {
+    static long mix(long current, long value) {
         return checkOverflow(current + Long.stringSize(value));
     }
 
     /**
-     * Mix value length into current length
+     * Mix value length and coder into current length and coder.
      * @param current current length
      * @param value   value to mix in
-     * @return new length
-     */
-    static int mixLen(int current, String value) {
-        return checkOverflow(current + value.length());
-    }
-
-    /**
-     * Mix coder into current coder
-     * @param current current coder
-     * @param value   value to mix in
-     * @return new coder
+     * @return new length and coder
      */
-    static byte mixCoder(byte current, char value) {
-        return (byte)(current | (StringLatin1.canEncode(value) ? 0 : 1));
-    }
-
-    /**
-     * Mix coder into current coder
-     * @param current current coder
-     * @param value   value to mix in
-     * @return new coder
-     */
-    static byte mixCoder(byte current, String value) {
-        return (byte)(current | value.coder());
+    static long mix(long current, String value) {
+        current += value.length();
+        if (value.coder() == String.UTF16) {
+            current |= UTF16;
+        }
+        return checkOverflow(current);
     }
 
     /**
      * Prepends the stringly representation of boolean value into buffer,
      * given the coder and final index. Index is measured in chars, not in bytes!
      *
-     * @param index final char index in the buffer
-     * @param buf   buffer to append to
-     * @param coder coder to add with
-     * @param value boolean value to encode
-     * @return new index
+     * @param indexCoder final char index in the buffer, along with coder packed
+     *                   into higher bits.
+     * @param buf        buffer to append to
+     * @param value      boolean value to encode
+     * @return           updated index (coder value retained)
      */
-    static int prepend(int index, byte[] buf, byte coder, boolean value) {
-        if (coder == String.LATIN1) {
+    static long prepend(long indexCoder, byte[] buf, boolean value) {
+        int index = (int)indexCoder;
+        if (indexCoder < UTF16) {
             if (value) {
                 buf[--index] = 'e';
                 buf[--index] = 'u';
@@ -162,6 +147,7 @@
                 buf[--index] = 'a';
                 buf[--index] = 'f';
             }
+            return index;
         } else {
             if (value) {
                 StringUTF16.putChar(buf, --index, 'e');
@@ -175,72 +161,72 @@
                 StringUTF16.putChar(buf, --index, 'a');
                 StringUTF16.putChar(buf, --index, 'f');
             }
+            return index | UTF16;
         }
-        return index;
     }
 
     /**
      * Prepends the stringly representation of byte value into buffer,
      * given the coder and final index. Index is measured in chars, not in bytes!
      *
-     * @param index final char index in the buffer
-     * @param buf   buffer to append to
-     * @param coder coder to add with
-     * @param value byte value to encode
-     * @return new index
+     * @param indexCoder final char index in the buffer, along with coder packed
+     *                   into higher bits.
+     * @param buf        buffer to append to
+     * @param value      byte value to encode
+     * @return           updated index (coder value retained)
      */
-    static int prepend(int index, byte[] buf, byte coder, byte value) {
-        return prepend(index, buf, coder, (int)value);
+    static long prepend(long indexCoder, byte[] buf, byte value) {
+        return prepend(indexCoder, buf, (int)value);
     }
 
     /**
      * Prepends the stringly representation of char value into buffer,
      * given the coder and final index. Index is measured in chars, not in bytes!
      *
-     * @param index final char index in the buffer
-     * @param buf   buffer to append to
-     * @param coder coder to add with
-     * @param value char value to encode
-     * @return new index
+     * @param indexCoder final char index in the buffer, along with coder packed
+     *                   into higher bits.
+     * @param buf        buffer to append to
+     * @param value      char value to encode
+     * @return           updated index (coder value retained)
      */
-    static int prepend(int index, byte[] buf, byte coder, char value) {
-        if (coder == String.LATIN1) {
-            buf[--index] = (byte) (value & 0xFF);
+    static long prepend(long indexCoder, byte[] buf, char value) {
+        if (indexCoder < UTF16) {
+            buf[(int)(--indexCoder)] = (byte) (value & 0xFF);
         } else {
-            StringUTF16.putChar(buf, --index, value);
+            StringUTF16.putChar(buf, (int)(--indexCoder), value);
         }
-        return index;
+        return indexCoder;
     }
 
     /**
      * Prepends the stringly representation of short value into buffer,
      * given the coder and final index. Index is measured in chars, not in bytes!
      *
-     * @param index final char index in the buffer
-     * @param buf   buffer to append to
-     * @param coder coder to add with
-     * @param value short value to encode
-     * @return new index
+     * @param indexCoder final char index in the buffer, along with coder packed
+     *                   into higher bits.
+     * @param buf        buffer to append to
+     * @param value      short value to encode
+     * @return           updated index (coder value retained)
      */
-    static int prepend(int index, byte[] buf, byte coder, short value) {
-        return prepend(index, buf, coder, (int)value);
+    static long prepend(long indexCoder, byte[] buf, short value) {
+        return prepend(indexCoder, buf, (int)value);
     }
 
     /**
      * Prepends the stringly representation of integer value into buffer,
      * given the coder and final index. Index is measured in chars, not in bytes!
      *
-     * @param index final char index in the buffer
-     * @param buf   buffer to append to
-     * @param coder coder to add with
-     * @param value integer value to encode
-     * @return new index
+     * @param indexCoder final char index in the buffer, along with coder packed
+     *                   into higher bits.
+     * @param buf        buffer to append to
+     * @param value      integer value to encode
+     * @return           updated index (coder value retained)
      */
-    static int prepend(int index, byte[] buf, byte coder, int value) {
-        if (coder == String.LATIN1) {
-            return Integer.getChars(value, index, buf);
+    static long prepend(long indexCoder, byte[] buf, int value) {
+        if (indexCoder < UTF16) {
+            return Integer.getChars(value, (int)indexCoder, buf);
         } else {
-            return StringUTF16.getChars(value, index, buf);
+            return StringUTF16.getChars(value, (int)indexCoder, buf) | UTF16;
         }
     }
 
@@ -248,17 +234,17 @@
      * Prepends the stringly representation of long value into buffer,
      * given the coder and final index. Index is measured in chars, not in bytes!
      *
-     * @param index final char index in the buffer
-     * @param buf   buffer to append to
-     * @param coder coder to add with
-     * @param value long value to encode
-     * @return new index
+     * @param indexCoder final char index in the buffer, along with coder packed
+     *                   into higher bits.
+     * @param buf        buffer to append to
+     * @param value      long value to encode
+     * @return           updated index (coder value retained)
      */
-    static int prepend(int index, byte[] buf, byte coder, long value) {
-        if (coder == String.LATIN1) {
-            return Long.getChars(value, index, buf);
+    static long prepend(long indexCoder, byte[] buf, long value) {
+        if (indexCoder < UTF16) {
+            return Long.getChars(value, (int)indexCoder, buf);
         } else {
-            return StringUTF16.getChars(value, index, buf);
+            return StringUTF16.getChars(value, (int)indexCoder, buf) | UTF16;
         }
     }
 
@@ -266,39 +252,49 @@
      * Prepends the stringly representation of String value into buffer,
      * given the coder and final index. Index is measured in chars, not in bytes!
      *
-     * @param index final char index in the buffer
-     * @param buf   buffer to append to
-     * @param coder coder to add with
-     * @param value String value to encode
-     * @return new index
+     * @param indexCoder final char index in the buffer, along with coder packed
+     *                   into higher bits.
+     * @param buf        buffer to append to
+     * @param value      String value to encode
+     * @return           updated index (coder value retained)
      */
-    static int prepend(int index, byte[] buf, byte coder, String value) {
-        index -= value.length();
-        value.getBytes(buf, index, coder);
-        return index;
+    static long prepend(long indexCoder, byte[] buf, String value) {
+        indexCoder -= value.length();
+        if (indexCoder < UTF16) {
+            value.getBytes(buf, (int)indexCoder, String.LATIN1);
+        } else {
+            value.getBytes(buf, (int)indexCoder, String.UTF16);
+        }
+        return indexCoder;
     }
 
     /**
      * Instantiates the String with given buffer and coder
-     * @param buf     buffer to use
-     * @param index   remaining index
-     * @param coder   coder to use
-     * @return String resulting string
+     * @param buf           buffer to use
+     * @param indexCoder    remaining index (should be zero) and coder
+     * @return String       resulting string
      */
-    static String newString(byte[] buf, int index, byte coder) {
+    static String newString(byte[] buf, long indexCoder) {
         // Use the private, non-copying constructor (unsafe!)
-        if (index != 0) {
-            throw new InternalError("Storage is not completely initialized, " + index + " bytes left");
+        if (indexCoder == LATIN1) {
+            return new String(buf, String.LATIN1);
+        } else if (indexCoder == UTF16) {
+            return new String(buf, String.UTF16);
+        } else {
+            throw new InternalError("Storage is not completely initialized, " + (int)indexCoder + " bytes left");
         }
-        return new String(buf, coder);
     }
 
+    private static final long LATIN1 = (long)String.LATIN1 << 32;
+
+    private static final long UTF16 = (long)String.UTF16 << 32;
+
     /**
      * Provides the initial coder for the String.
-     * @return initial coder
+     * @return initial coder, adjusted into the upper half
      */
-    static byte initialCoder() {
-        return String.COMPACT_STRINGS ? String.LATIN1 : String.UTF16;
+    static long initialCoder() {
+        return String.COMPACT_STRINGS ? LATIN1 : UTF16;
     }
 
 }
--- a/src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java	Tue Oct 30 11:46:26 2018 +0100
+++ b/src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java	Tue Oct 30 13:50:05 2018 +0100
@@ -318,6 +318,13 @@
         }
 
         @Override
+        public String toString() {
+            return "Recipe{" +
+                    "elements=" + elements +
+                    '}';
+        }
+
+        @Override
         public int hashCode() {
             return elements.hashCode();
         }
@@ -368,6 +375,15 @@
         }
 
         @Override
+        public String toString() {
+            return "RecipeElement{" +
+                    "value='" + value + '\'' +
+                    ", argPos=" + argPos +
+                    ", tag=" + tag +
+                    '}';
+        }
+
+        @Override
         public int hashCode() {
             return (int)tag;
         }
@@ -1520,24 +1536,24 @@
             }
 
             // Start building the combinator tree. The tree "starts" with (<parameters>)String, and "finishes"
-            // with the (int, byte[], byte)String in String helper. The combinators are assembled bottom-up,
-            // which makes the code arguably hard to read.
+            // with the (byte[], long)String shape to invoke newString in StringConcatHelper. The combinators are
+            // assembled bottom-up, which makes the code arguably hard to read.
 
             // Drop all remaining parameter types, leave only helper arguments:
             MethodHandle mh;
 
-            mh = MethodHandles.dropArguments(NEW_STRING, 3, ptypes);
+            mh = MethodHandles.dropArguments(NEW_STRING, 2, ptypes);
 
-            // Mix in prependers. This happens when (byte[], int, byte) = (storage, index, coder) is already
-            // known from the combinators below. We are assembling the string backwards, so "index" is the
-            // *ending* index.
+            // Mix in prependers. This happens when (byte[], long) = (storage, indexCoder) is already
+            // known from the combinators below. We are assembling the string backwards, so the index coded
+            // into indexCoder is the *ending* index.
             for (RecipeElement el : recipe.getElements()) {
                 // Do the prepend, and put "new" index at index 1
                 switch (el.getTag()) {
                     case TAG_CONST: {
-                        MethodHandle prepender = MethodHandles.insertArguments(prepender(String.class), 3, el.getValue());
+                        MethodHandle prepender = MethodHandles.insertArguments(prepender(String.class), 2, el.getValue());
                         mh = MethodHandles.filterArgumentsWithCombiner(mh, 1, prepender,
-                                1, 0, 2 // index, storage, coder
+                                1, 0 // indexCoder, storage
                         );
                         break;
                     }
@@ -1545,8 +1561,8 @@
                         int pos = el.getArgPos();
                         MethodHandle prepender = prepender(ptypes[pos]);
                         mh = MethodHandles.filterArgumentsWithCombiner(mh, 1, prepender,
-                                1, 0, 2, // index, storage, coder
-                                3 + pos  // selected argument
+                                1, 0, // indexCoder, storage
+                                2 + pos  // selected argument
                         );
                         break;
                     }
@@ -1557,7 +1573,7 @@
 
             // Fold in byte[] instantiation at argument 0
             mh = MethodHandles.foldArgumentsWithCombiner(mh, 0, NEW_ARRAY,
-                    1, 2 // index, coder
+                    1 // index
             );
 
             // Start combining length and coder mixers.
@@ -1569,47 +1585,28 @@
             // Coders are more interesting. Only Object, String and char arguments (and constants)
             // can have non-Latin1 encoding. It is easier to blindly convert constants to String,
             // and deduce the coder from there. Arguments would be either converted to Strings
-            // during the initial filtering, or handled by primitive specializations in CODER_MIXERS.
+            // during the initial filtering, or handled by specializations in MIXERS.
             //
-            // The method handle shape before and after all length and coder mixers is:
-            //   (int, byte, <args>)String = ("index", "coder", <args>)
-            byte initialCoder = INITIAL_CODER;
-            int initialLen = 0;    // initial length, in characters
+            // The method handle shape before and after all mixers are combined in is:
+            //   (long, <args>)String = ("indexCoder", <args>)
+            long initialLengthCoder = INITIAL_CODER;
             for (RecipeElement el : recipe.getElements()) {
                 switch (el.getTag()) {
                     case TAG_CONST:
                         String constant = el.getValue();
-                        initialCoder = (byte) coderMixer(String.class).invoke(initialCoder, constant);
-                        initialLen += constant.length();
+                        initialLengthCoder = (long)mixer(String.class).invoke(initialLengthCoder, constant);
                         break;
                     case TAG_ARG:
                         int ac = el.getArgPos();
 
                         Class<?> argClass = ptypes[ac];
-                        MethodHandle lm = lengthMixer(argClass);
-
-                        if (argClass.isPrimitive() && argClass != char.class) {
-                            // Compute new "index" in-place using old value plus the appropriate argument.
-                            mh = MethodHandles.filterArgumentsWithCombiner(mh, 0, lm,
-                                    0, // old-index
-                                    2 + ac // selected argument
-                            );
-
-                        } else {
-                            MethodHandle cm = coderMixer(argClass);
+                        MethodHandle mix = mixer(argClass);
 
-                            // Compute new "index" in-place using old value plus the appropriate argument.
-                            mh = MethodHandles.filterArgumentsWithCombiner(mh, 0, lm,
-                                    0, // old-index
-                                    2 + ac // selected argument
-                            );
-
-                            // Compute new "coder" in-place using old value plus the appropriate argument.
-                            mh = MethodHandles.filterArgumentsWithCombiner(mh, 1, cm,
-                                    1, // old-coder
-                                    2 + ac // selected argument
-                            );
-                        }
+                        // Compute new "index" in-place using old value plus the appropriate argument.
+                        mh = MethodHandles.filterArgumentsWithCombiner(mh, 0, mix,
+                                0, // old-index
+                                1 + ac // selected argument
+                        );
 
                         break;
                     default:
@@ -1617,9 +1614,9 @@
                 }
             }
 
-            // Insert initial lengths and coders here.
+            // Insert initial length and coder value here.
             // The method handle shape here is (<args>).
-            mh = MethodHandles.insertArguments(mh, 0, initialLen, initialCoder);
+            mh = MethodHandles.insertArguments(mh, 0, initialLengthCoder);
 
             // Apply filters, converting the arguments:
             if (filters != null) {
@@ -1630,45 +1627,34 @@
         }
 
         @ForceInline
-        private static byte[] newArray(int length, byte coder) {
-            return (byte[]) UNSAFE.allocateUninitializedArray(byte.class, length << coder);
+        private static byte[] newArray(long indexCoder) {
+            byte coder = (byte)(indexCoder >> 32);
+            int index = ((int)indexCoder & 0x7FFFFFFF);
+            return (byte[]) UNSAFE.allocateUninitializedArray(byte.class, index << coder);
         }
 
         private static MethodHandle prepender(Class<?> cl) {
             return PREPENDERS.computeIfAbsent(cl, PREPEND);
         }
 
-        private static MethodHandle coderMixer(Class<?> cl) {
-            return CODER_MIXERS.computeIfAbsent(cl, CODER_MIX);
-        }
-
-        private static MethodHandle lengthMixer(Class<?> cl) {
-            return LENGTH_MIXERS.computeIfAbsent(cl, LENGTH_MIX);
+        private static MethodHandle mixer(Class<?> cl) {
+            return MIXERS.computeIfAbsent(cl, MIX);
         }
 
         // This one is deliberately non-lambdified to optimize startup time:
         private static final Function<Class<?>, MethodHandle> PREPEND = new Function<Class<?>, MethodHandle>() {
             @Override
             public MethodHandle apply(Class<?> c) {
-                return lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "prepend", int.class, int.class, byte[].class, byte.class,
+                return lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "prepend", long.class, long.class, byte[].class,
                         Wrapper.asPrimitiveType(c));
             }
         };
 
         // This one is deliberately non-lambdified to optimize startup time:
-        private static final Function<Class<?>, MethodHandle> CODER_MIX = new Function<Class<?>, MethodHandle>() {
+        private static final Function<Class<?>, MethodHandle> MIX = new Function<Class<?>, MethodHandle>() {
             @Override
             public MethodHandle apply(Class<?> c) {
-                return lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "mixCoder", byte.class, byte.class,
-                        Wrapper.asPrimitiveType(c));
-            }
-        };
-
-        // This one is deliberately non-lambdified to optimize startup time:
-        private static final Function<Class<?>, MethodHandle> LENGTH_MIX = new Function<Class<?>, MethodHandle>() {
-            @Override
-            public MethodHandle apply(Class<?> c) {
-                return lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "mixLen", int.class, int.class,
+                return lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "mix", long.class, long.class,
                         Wrapper.asPrimitiveType(c));
             }
         };
@@ -1676,26 +1662,24 @@
         private static final MethodHandle NEW_STRING;
         private static final MethodHandle NEW_ARRAY;
         private static final ConcurrentMap<Class<?>, MethodHandle> PREPENDERS;
-        private static final ConcurrentMap<Class<?>, MethodHandle> LENGTH_MIXERS;
-        private static final ConcurrentMap<Class<?>, MethodHandle> CODER_MIXERS;
-        private static final byte INITIAL_CODER;
+        private static final ConcurrentMap<Class<?>, MethodHandle> MIXERS;
+        private static final long INITIAL_CODER;
         static final Class<?> STRING_HELPER;
 
         static {
             try {
                 STRING_HELPER = Class.forName("java.lang.StringConcatHelper");
-                MethodHandle initCoder = lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "initialCoder", byte.class);
-                INITIAL_CODER = (byte) initCoder.invoke();
+                MethodHandle initCoder = lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "initialCoder", long.class);
+                INITIAL_CODER = (long) initCoder.invoke();
             } catch (Throwable e) {
                 throw new AssertionError(e);
             }
 
             PREPENDERS = new ConcurrentHashMap<>();
-            LENGTH_MIXERS = new ConcurrentHashMap<>();
-            CODER_MIXERS = new ConcurrentHashMap<>();
+            MIXERS = new ConcurrentHashMap<>();
 
-            NEW_STRING = lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "newString", String.class, byte[].class, int.class, byte.class);
-            NEW_ARRAY  = lookupStatic(Lookup.IMPL_LOOKUP, MethodHandleInlineCopyStrategy.class, "newArray", byte[].class, int.class, byte.class);
+            NEW_STRING = lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "newString", String.class, byte[].class, long.class);
+            NEW_ARRAY  = lookupStatic(Lookup.IMPL_LOOKUP, MethodHandleInlineCopyStrategy.class, "newArray", byte[].class, long.class);
         }
     }
 
--- a/src/java.base/unix/native/jspawnhelper/jspawnhelper.c	Tue Oct 30 11:46:26 2018 +0100
+++ b/src/java.base/unix/native/jspawnhelper/jspawnhelper.c	Tue Oct 30 13:50:05 2018 +0100
@@ -49,7 +49,10 @@
 #define ERR_ARGS 3
 
 void error (int fd, int err) {
-    write (fd, &err, sizeof(err));
+    if (write (fd, &err, sizeof(err)) != sizeof(err)) {
+        /* Not sure what to do here. I have no one to speak to. */
+        exit(0x80 + err);
+    }
     exit (1);
 }