--- old/agent/src/share/classes/sun/jvm/hotspot/oops/OopUtilities.java 2015-10-30 19:14:40.384475288 +0100 +++ new/agent/src/share/classes/sun/jvm/hotspot/oops/OopUtilities.java 2015-10-30 19:14:40.316475285 +0100 @@ -40,8 +40,7 @@ // FIXME: access should be synchronized and cleared when VM is // resumed // String fields - private static IntField offsetField; - private static IntField countField; + private static ByteField coderField; private static OopField valueField; // ThreadGroup fields private static OopField threadGroupParentField; @@ -96,20 +95,30 @@ if (charArray == null) { return null; } - return charArrayToString(charArray, 0, (int) charArray.getLength()); + int length = (int)charArray.getLength(); + StringBuffer buf = new StringBuffer(length); + for (int i = 0; i < length; i++) { + buf.append(charArray.getCharAt(i)); + } + return buf.toString(); } - public static String charArrayToString(TypeArray charArray, int offset, int length) { - if (charArray == null) { + public static String byteArrayToString(TypeArray byteArray, byte coder) { + if (byteArray == null) { return null; } - final int limit = offset + length; - if (Assert.ASSERTS_ENABLED) { - Assert.that(offset >= 0 && limit <= charArray.getLength(), "out of bounds"); - } + int length = (int)byteArray.getLength() >> coder; StringBuffer buf = new StringBuffer(length); - for (int i = offset; i < limit; i++) { - buf.append(charArray.getCharAt(i)); + if (coder == 0) { + // Latin1 encoded + for (int i = 0; i < length; i++) { + buf.append((char)(byteArray.getByteAt(i) & 0xff)); + } + } else { + // UTF16 encoded + for (int i = 0; i < length; i++) { + buf.append(byteArray.getCharAt(i)); + } } return buf.toString(); } @@ -141,21 +150,14 @@ } public static String stringOopToString(Oop stringOop) { - if (offsetField == null) { - InstanceKlass k = (InstanceKlass) stringOop.getKlass(); - offsetField = (IntField) k.findField("offset", "I"); // optional - countField = (IntField) k.findField("count", "I"); // optional - valueField = (OopField) k.findField("value", "[C"); - if (Assert.ASSERTS_ENABLED) { - Assert.that(valueField != null, "Field \'value\' of java.lang.String not found"); - } - } - if (offsetField != null && countField != null) { - return charArrayToString((TypeArray) valueField.getValue(stringOop), - offsetField.getValue(stringOop), - countField.getValue(stringOop)); + InstanceKlass k = (InstanceKlass) stringOop.getKlass(); + coderField = (ByteField) k.findField("coder", "B"); + valueField = (OopField) k.findField("value", "[B"); + if (Assert.ASSERTS_ENABLED) { + Assert.that(coderField != null, "Field \'coder\' of java.lang.String not found"); + Assert.that(valueField != null, "Field \'value\' of java.lang.String not found"); } - return charArrayToString((TypeArray) valueField.getValue(stringOop)); + return byteArrayToString((TypeArray) valueField.getValue(stringOop), coderField.getValue(stringOop)); } public static String stringOopToEscapedString(Oop stringOop) { --- old/agent/src/share/classes/sun/jvm/hotspot/tools/HeapSummary.java 2015-10-30 19:14:40.704475298 +0100 +++ new/agent/src/share/classes/sun/jvm/hotspot/tools/HeapSummary.java 2015-10-30 19:14:40.600475294 +0100 @@ -268,8 +268,8 @@ VM vm = VM.getVM(); SystemDictionary sysDict = vm.getSystemDictionary(); InstanceKlass strKlass = sysDict.getStringKlass(); - // String has a field named 'value' of type 'char[]'. - stringValueField = (OopField) strKlass.findField("value", "[C"); + // String has a field named 'value' of type 'byte[]'. + stringValueField = (OopField) strKlass.findField("value", "[B"); } private long stringSize(Instance instance) { --- old/agent/src/share/classes/sun/jvm/hotspot/utilities/Hashtable.java 2015-10-30 19:14:40.972475306 +0100 +++ new/agent/src/share/classes/sun/jvm/hotspot/utilities/Hashtable.java 2015-10-30 19:14:40.908475304 +0100 @@ -61,9 +61,8 @@ long h = 0; int s = 0; int len = buf.length; - // Emulate the unsigned int in java_lang_String::hash_code while (len-- > 0) { - h = 31*h + (0xFFFFFFFFL & buf[s]); + h = 31*h + (0xFFL & buf[s]); s++; } return h & 0xFFFFFFFFL; --- old/src/cpu/aarch64/vm/aarch64.ad 2015-10-30 19:14:41.220475314 +0100 +++ new/src/cpu/aarch64/vm/aarch64.ad 2015-10-30 19:14:41.148475312 +0100 @@ -14150,6 +14150,7 @@ instruct string_compare(iRegP_R1 str1, iRegI_R2 cnt1, iRegP_R3 str2, iRegI_R4 cnt2, iRegI_R0 result, iRegP_R10 tmp1, rFlagsReg cr) %{ + predicate(!CompactStrings); match(Set result (StrComp (Binary str1 cnt1) (Binary str2 cnt2))); effect(KILL tmp1, USE_KILL str1, USE_KILL str2, USE_KILL cnt1, USE_KILL cnt2, KILL cr); @@ -14165,6 +14166,7 @@ instruct string_indexof(iRegP_R1 str1, iRegI_R4 cnt1, iRegP_R3 str2, iRegI_R2 cnt2, iRegI_R0 result, iRegI tmp1, iRegI tmp2, iRegI tmp3, iRegI tmp4, rFlagsReg cr) %{ + predicate(!CompactStrings); match(Set result (StrIndexOf (Binary str1 cnt1) (Binary str2 cnt2))); effect(USE_KILL str1, USE_KILL str2, USE_KILL cnt1, USE_KILL cnt2, TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4, KILL cr); @@ -14184,6 +14186,7 @@ immI_le_4 int_cnt2, iRegI_R0 result, iRegI tmp1, iRegI tmp2, iRegI tmp3, iRegI tmp4, rFlagsReg cr) %{ + predicate(!CompactStrings); match(Set result (StrIndexOf (Binary str1 cnt1) (Binary str2 int_cnt2))); effect(USE_KILL str1, USE_KILL str2, USE_KILL cnt1, TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4, KILL cr); @@ -14203,6 +14206,7 @@ instruct string_equals(iRegP_R1 str1, iRegP_R3 str2, iRegI_R4 cnt, iRegI_R0 result, iRegP_R10 tmp, rFlagsReg cr) %{ + predicate(!CompactStrings); match(Set result (StrEquals (Binary str1 str2) cnt)); effect(KILL tmp, USE_KILL str1, USE_KILL str2, USE_KILL cnt, KILL cr); @@ -14218,6 +14222,7 @@ instruct array_equals(iRegP_R1 ary1, iRegP_R2 ary2, iRegI_R0 result, iRegP_R10 tmp, rFlagsReg cr) %{ + predicate(((AryEqNode*)n)->encoding() == StrIntrinsicNode::UU); match(Set result (AryEq ary1 ary2)); effect(KILL tmp, USE_KILL ary1, USE_KILL ary2, KILL cr); --- old/src/cpu/aarch64/vm/c1_LIRAssembler_aarch64.cpp 2015-10-30 19:14:41.528475324 +0100 +++ new/src/cpu/aarch64/vm/c1_LIRAssembler_aarch64.cpp 2015-10-30 19:14:41.468475322 +0100 @@ -483,15 +483,6 @@ return offset; } - -// This is the fast version of java.lang.String.compare; it has not -// OSR-entry and therefore, we generate a slow version for OSR's -void LIR_Assembler::emit_string_compare(LIR_Opr arg0, LIR_Opr arg1, LIR_Opr dst, CodeEmitInfo* info) { - __ mov(r2, (address)__FUNCTION__); - __ call_Unimplemented(); -} - - void LIR_Assembler::add_debug_info_for_branch(address adr, CodeEmitInfo* info) { _masm->code_section()->relocate(adr, relocInfo::poll_type); int pc_offset = code_offset(); --- old/src/cpu/aarch64/vm/globals_aarch64.hpp 2015-10-30 19:14:41.788475332 +0100 +++ new/src/cpu/aarch64/vm/globals_aarch64.hpp 2015-10-30 19:14:41.724475330 +0100 @@ -79,6 +79,9 @@ define_pd_global(uintx, TypeProfileLevel, 111); +// No performance work done here yet. +define_pd_global(bool, CompactStrings, false); + // avoid biased locking while we are bootstrapping the aarch64 build define_pd_global(bool, UseBiasedLocking, false); --- old/src/cpu/ppc/vm/globals_ppc.hpp 2015-10-30 19:14:42.024475340 +0100 +++ new/src/cpu/ppc/vm/globals_ppc.hpp 2015-10-30 19:14:41.956475337 +0100 @@ -72,6 +72,9 @@ define_pd_global(uintx, TypeProfileLevel, 111); +// No performance work done here yet. +define_pd_global(bool, CompactStrings, false); + // Platform dependent flag handling: flags only defined on this platform. #define ARCH_FLAGS(develop, product, diagnostic, experimental, notproduct, range, constraint) \ \ --- old/src/cpu/ppc/vm/ppc.ad 2015-10-30 19:14:42.264475347 +0100 +++ new/src/cpu/ppc/vm/ppc.ad 2015-10-30 19:14:42.192475345 +0100 @@ -2054,11 +2054,11 @@ return (UsePopCountInstruction && VM_Version::has_popcntw()); case Op_StrComp: - return SpecialStringCompareTo; + return SpecialStringCompareTo && !CompactStrings; case Op_StrEquals: - return SpecialStringEquals; + return SpecialStringEquals && !CompactStrings; case Op_StrIndexOf: - return SpecialStringIndexOf; + return SpecialStringIndexOf && !CompactStrings; } return true; // Per default match rules are supported. @@ -11077,7 +11077,7 @@ immP needleImm, immL offsetImm, immI_1 needlecntImm, iRegIdst tmp1, iRegIdst tmp2, flagsRegCR0 cr0, flagsRegCR1 cr1) %{ - predicate(SpecialStringIndexOf); // type check implicit by parameter type, See Matcher::match_rule_supported + predicate(SpecialStringIndexOf && !CompactStrings); // type check implicit by parameter type, See Matcher::match_rule_supported match(Set result (StrIndexOf (Binary haystack haycnt) (Binary (AddP needleImm offsetImm) needlecntImm))); effect(TEMP_DEF result, TEMP tmp1, TEMP tmp2, KILL cr0, KILL cr1); @@ -11120,7 +11120,7 @@ effect(USE_KILL needle, /* TDEF needle, */ TEMP_DEF result, TEMP tmp1, TEMP tmp2); // Required for EA: check if it is still a type_array. - predicate(SpecialStringIndexOf && n->in(3)->in(1)->bottom_type()->is_aryptr()->const_oop() && + predicate(SpecialStringIndexOf && !CompactStrings && n->in(3)->in(1)->bottom_type()->is_aryptr()->const_oop() && n->in(3)->in(1)->bottom_type()->is_aryptr()->const_oop()->is_type_array()); ins_cost(180); @@ -11167,7 +11167,7 @@ effect(USE_KILL haycnt, /* better: TDEF haycnt, */ TEMP_DEF result, TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4, TEMP tmp5, KILL cr0, KILL cr1, KILL cr6); // Required for EA: check if it is still a type_array. - predicate(SpecialStringIndexOf && n->in(3)->in(1)->bottom_type()->is_aryptr()->const_oop() && + predicate(SpecialStringIndexOf && !CompactStrings && n->in(3)->in(1)->bottom_type()->is_aryptr()->const_oop() && n->in(3)->in(1)->bottom_type()->is_aryptr()->const_oop()->is_type_array()); ins_cost(250); @@ -11200,7 +11200,7 @@ effect(USE_KILL haycnt, USE_KILL needlecnt, /*better: TDEF haycnt, TDEF needlecnt,*/ TEMP_DEF result, TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4, KILL cr0, KILL cr1, KILL cr6); - predicate(SpecialStringIndexOf); // See Matcher::match_rule_supported. + predicate(SpecialStringIndexOf && !CompactStrings); // See Matcher::match_rule_supported. ins_cost(300); ins_alignment(8); // 'compute_padding()' gets called, up to this number-1 nops will get inserted. @@ -11224,7 +11224,7 @@ match(Set result (StrEquals (Binary str1 str2) cntImm)); effect(TEMP_DEF result, TEMP tmp1, TEMP tmp2, KILL cr0, KILL cr6, KILL ctr); - predicate(SpecialStringEquals); // See Matcher::match_rule_supported. + predicate(SpecialStringEquals && !CompactStrings); // See Matcher::match_rule_supported. ins_cost(250); ins_alignment(8); // 'compute_padding()' gets called, up to this number-1 nops will get inserted. @@ -11247,7 +11247,7 @@ match(Set result (StrEquals (Binary str1 str2) cnt)); effect(TEMP_DEF result, TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4, TEMP tmp5, KILL cr0, KILL cr1, KILL cr6, KILL ctr); - predicate(SpecialStringEquals); // See Matcher::match_rule_supported. + predicate(SpecialStringEquals && !CompactStrings); // See Matcher::match_rule_supported. ins_cost(300); ins_alignment(8); // 'compute_padding()' gets called, up to this number-1 nops will get inserted. @@ -11267,6 +11267,7 @@ // Use dst register classes if register gets killed, as it is the case for TEMP operands! instruct string_compare(rarg1RegP str1, rarg2RegP str2, rarg3RegI cnt1, rarg4RegI cnt2, iRegIdst result, iRegPdst tmp, flagsRegCR0 cr0, regCTR ctr) %{ + predicate(!CompactStrings); match(Set result (StrComp (Binary str1 cnt1) (Binary str2 cnt2))); effect(USE_KILL cnt1, USE_KILL cnt2, USE_KILL str1, USE_KILL str2, TEMP_DEF result, TEMP tmp, KILL cr0, KILL ctr); ins_cost(300); --- old/src/cpu/sparc/vm/assembler_sparc.hpp 2015-10-30 19:14:42.564475357 +0100 +++ new/src/cpu/sparc/vm/assembler_sparc.hpp 2015-10-30 19:14:42.496475355 +0100 @@ -124,6 +124,8 @@ impdep1_op3 = 0x36, aes3_op3 = 0x36, sha_op3 = 0x36, + bmask_op3 = 0x36, + bshuffle_op3 = 0x36, alignaddr_op3 = 0x36, faligndata_op3 = 0x36, flog3_op3 = 0x36, @@ -194,6 +196,7 @@ fnegd_opf = 0x06, alignaddr_opf = 0x18, + bmask_opf = 0x19, fadds_opf = 0x41, faddd_opf = 0x42, @@ -204,6 +207,7 @@ fmuls_opf = 0x49, fmuld_opf = 0x4a, + bshuffle_opf = 0x4c, fdivs_opf = 0x4d, fdivd_opf = 0x4e, @@ -1226,6 +1230,9 @@ void edge8n( Register s1, Register s2, Register d ) { vis2_only(); emit_int32( op(arith_op) | rd(d) | op3(edge_op3) | rs1(s1) | opf(edge8n_opf) | rs2(s2)); } + void bmask( Register s1, Register s2, Register d ) { vis2_only(); emit_int32( op(arith_op) | rd(d) | op3(bmask_op3) | rs1(s1) | opf(bmask_opf) | rs2(s2)); } + void bshuffle( FloatRegister s1, FloatRegister s2, FloatRegister d ) { vis2_only(); emit_int32( op(arith_op) | fd(d, FloatRegisterImpl::D) | op3(bshuffle_op3) | fs1(s1, FloatRegisterImpl::D) | opf(bshuffle_opf) | fs2(s2, FloatRegisterImpl::D)); } + // VIS3 instructions void movstosw( FloatRegister s, Register d ) { vis3_only(); emit_int32( op(arith_op) | rd(d) | op3(mftoi_op3) | opf(mstosw_opf) | fs2(s, FloatRegisterImpl::S)); } --- old/src/cpu/sparc/vm/c1_LIRAssembler_sparc.cpp 2015-10-30 19:14:42.816475365 +0100 +++ new/src/cpu/sparc/vm/c1_LIRAssembler_sparc.cpp 2015-10-30 19:14:42.752475363 +0100 @@ -232,118 +232,6 @@ } -// Optimized Library calls -// This is the fast version of java.lang.String.compare; it has not -// OSR-entry and therefore, we generate a slow version for OSR's -void LIR_Assembler::emit_string_compare(LIR_Opr left, LIR_Opr right, LIR_Opr dst, CodeEmitInfo* info) { - Register str0 = left->as_register(); - Register str1 = right->as_register(); - - Label Ldone; - - Register result = dst->as_register(); - { - // Get a pointer to the first character of string0 in tmp0 - // and get string0.length() in str0 - // Get a pointer to the first character of string1 in tmp1 - // and get string1.length() in str1 - // Also, get string0.length()-string1.length() in - // o7 and get the condition code set - // Note: some instructions have been hoisted for better instruction scheduling - - Register tmp0 = L0; - Register tmp1 = L1; - Register tmp2 = L2; - - int value_offset = java_lang_String:: value_offset_in_bytes(); // char array - if (java_lang_String::has_offset_field()) { - int offset_offset = java_lang_String::offset_offset_in_bytes(); // first character position - int count_offset = java_lang_String:: count_offset_in_bytes(); - __ load_heap_oop(str0, value_offset, tmp0); - __ ld(str0, offset_offset, tmp2); - __ add(tmp0, arrayOopDesc::base_offset_in_bytes(T_CHAR), tmp0); - __ ld(str0, count_offset, str0); - __ sll(tmp2, exact_log2(sizeof(jchar)), tmp2); - } else { - __ load_heap_oop(str0, value_offset, tmp1); - __ add(tmp1, arrayOopDesc::base_offset_in_bytes(T_CHAR), tmp0); - __ ld(tmp1, arrayOopDesc::length_offset_in_bytes(), str0); - } - - // str1 may be null - add_debug_info_for_null_check_here(info); - - if (java_lang_String::has_offset_field()) { - int offset_offset = java_lang_String::offset_offset_in_bytes(); // first character position - int count_offset = java_lang_String:: count_offset_in_bytes(); - __ load_heap_oop(str1, value_offset, tmp1); - __ add(tmp0, tmp2, tmp0); - - __ ld(str1, offset_offset, tmp2); - __ add(tmp1, arrayOopDesc::base_offset_in_bytes(T_CHAR), tmp1); - __ ld(str1, count_offset, str1); - __ sll(tmp2, exact_log2(sizeof(jchar)), tmp2); - __ add(tmp1, tmp2, tmp1); - } else { - __ load_heap_oop(str1, value_offset, tmp2); - __ add(tmp2, arrayOopDesc::base_offset_in_bytes(T_CHAR), tmp1); - __ ld(tmp2, arrayOopDesc::length_offset_in_bytes(), str1); - } - __ subcc(str0, str1, O7); - } - - { - // Compute the minimum of the string lengths, scale it and store it in limit - Register count0 = I0; - Register count1 = I1; - Register limit = L3; - - Label Lskip; - __ sll(count0, exact_log2(sizeof(jchar)), limit); // string0 is shorter - __ br(Assembler::greater, true, Assembler::pt, Lskip); - __ delayed()->sll(count1, exact_log2(sizeof(jchar)), limit); // string1 is shorter - __ bind(Lskip); - - // If either string is empty (or both of them) the result is the difference in lengths - __ cmp(limit, 0); - __ br(Assembler::equal, true, Assembler::pn, Ldone); - __ delayed()->mov(O7, result); // result is difference in lengths - } - - { - // Neither string is empty - Label Lloop; - - Register base0 = L0; - Register base1 = L1; - Register chr0 = I0; - Register chr1 = I1; - Register limit = L3; - - // Shift base0 and base1 to the end of the arrays, negate limit - __ add(base0, limit, base0); - __ add(base1, limit, base1); - __ neg(limit); // limit = -min{string0.length(), string1.length()} - - __ lduh(base0, limit, chr0); - __ bind(Lloop); - __ lduh(base1, limit, chr1); - __ subcc(chr0, chr1, chr0); - __ br(Assembler::notZero, false, Assembler::pn, Ldone); - assert(chr0 == result, "result must be pre-placed"); - __ delayed()->inccc(limit, sizeof(jchar)); - __ br(Assembler::notZero, true, Assembler::pt, Lloop); - __ delayed()->lduh(base0, limit, chr0); - } - - // If strings are equal up to min length, return the length difference. - __ mov(O7, result); - - // Otherwise, return the difference between the first mismatched chars. - __ bind(Ldone); -} - - // -------------------------------------------------------------------------------------------- void LIR_Assembler::monitorexit(LIR_Opr obj_opr, LIR_Opr lock_opr, Register hdr, int monitor_no) { --- old/src/cpu/sparc/vm/globals_sparc.hpp 2015-10-30 19:14:43.080475373 +0100 +++ new/src/cpu/sparc/vm/globals_sparc.hpp 2015-10-30 19:14:43.016475371 +0100 @@ -86,6 +86,8 @@ define_pd_global(uintx, TypeProfileLevel, 111); +define_pd_global(bool, CompactStrings, true); + #define ARCH_FLAGS(develop, product, diagnostic, experimental, notproduct, range, constraint) \ \ product(intx, UseVIS, 99, \ --- old/src/cpu/sparc/vm/macroAssembler_sparc.cpp 2015-10-30 19:14:43.324475381 +0100 +++ new/src/cpu/sparc/vm/macroAssembler_sparc.cpp 2015-10-30 19:14:43.264475379 +0100 @@ -44,6 +44,9 @@ #include "gc/g1/g1SATBCardTableModRefBS.hpp" #include "gc/g1/heapRegion.hpp" #endif // INCLUDE_ALL_GCS +#ifdef COMPILER2 +#include "opto/intrinsicnode.hpp" +#endif #ifdef PRODUCT #define BLOCK_COMMENT(str) /* nothing */ @@ -4253,27 +4256,385 @@ } } -// Compare char[] arrays aligned to 4 bytes. -void MacroAssembler::char_arrays_equals(Register ary1, Register ary2, - Register limit, Register result, - Register chr1, Register chr2, Label& Ldone) { - Label Lvector, Lloop; - assert(chr1 == result, "should be the same"); +#ifdef COMPILER2 + +// Compress char[] to byte[] by compressing 16 bytes at once. Return 0 on failure. +void MacroAssembler::string_compress_16(Register src, Register dst, Register cnt, Register result, + Register tmp1, Register tmp2, Register tmp3, Register tmp4, + FloatRegister ftmp1, FloatRegister ftmp2, FloatRegister ftmp3, Label& Ldone) { + Label Lloop, Lslow; + assert(UseVIS >= 3, "VIS3 is required"); + assert_different_registers(src, dst, cnt, tmp1, tmp2, tmp3, tmp4, result); + assert_different_registers(ftmp1, ftmp2, ftmp3); + + // Check if cnt >= 8 (= 16 bytes) + cmp(cnt, 8); + br(Assembler::less, false, Assembler::pn, Lslow); + delayed()->mov(cnt, result); // copy count + + // Check for 8-byte alignment of src and dst + or3(src, dst, tmp1); + andcc(tmp1, 7, G0); + br(Assembler::notZero, false, Assembler::pn, Lslow); + delayed()->nop(); + + // Set mask for bshuffle instruction + Register mask = tmp4; + set(0x13579bdf, mask); + bmask(mask, G0, G0); + + // Set mask to 0xff00 ff00 ff00 ff00 to check for non-latin1 characters + Assembler::sethi(0xff00fc00, mask); // mask = 0x0000 0000 ff00 fc00 + add(mask, 0x300, mask); // mask = 0x0000 0000 ff00 ff00 + sllx(mask, 32, tmp1); // tmp1 = 0xff00 ff00 0000 0000 + or3(mask, tmp1, mask); // mask = 0xff00 ff00 ff00 ff00 + + // Load first 8 bytes + ldx(src, 0, tmp1); + + bind(Lloop); + // Load next 8 bytes + ldx(src, 8, tmp2); + + // Check for non-latin1 character by testing if the most significant byte of a char is set. + // Although we have to move the data between integer and floating point registers, this is + // still faster than the corresponding VIS instructions (ford/fand/fcmpd). + or3(tmp1, tmp2, tmp3); + btst(tmp3, mask); + // annul zeroing if branch is not taken to preserve original count + brx(Assembler::notZero, true, Assembler::pn, Ldone); + delayed()->mov(G0, result); // 0 - failed + + // Move bytes into float register + movxtod(tmp1, ftmp1); + movxtod(tmp2, ftmp2); + + // Compress by copying one byte per char from ftmp1 and ftmp2 to ftmp3 + bshuffle(ftmp1, ftmp2, ftmp3); + stf(FloatRegisterImpl::D, ftmp3, dst, 0); + + // Increment addresses and decrement count + inc(src, 16); + inc(dst, 8); + dec(cnt, 8); + + cmp(cnt, 8); + // annul LDX if branch is not taken to prevent access past end of string + br(Assembler::greaterEqual, true, Assembler::pt, Lloop); + delayed()->ldx(src, 0, tmp1); + + // Fallback to slow version + bind(Lslow); +} + +// Compress char[] to byte[]. Return 0 on failure. +void MacroAssembler::string_compress(Register src, Register dst, Register cnt, Register result, Register tmp, Label& Ldone) { + Label Lloop; + assert_different_registers(src, dst, cnt, tmp, result); + + lduh(src, 0, tmp); + + bind(Lloop); + inc(src, sizeof(jchar)); + cmp(tmp, 0xff); + // annul zeroing if branch is not taken to preserve original count + br(Assembler::greater, true, Assembler::pn, Ldone); // don't check xcc + delayed()->mov(G0, result); // 0 - failed + deccc(cnt); + stb(tmp, dst, 0); + inc(dst); + // annul LDUH if branch is not taken to prevent access past end of string + br(Assembler::notZero, true, Assembler::pt, Lloop); + delayed()->lduh(src, 0, tmp); // hoisted +} + +// Inflate byte[] to char[] by inflating 16 bytes at once. +void MacroAssembler::string_inflate_16(Register src, Register dst, Register cnt, Register tmp, + FloatRegister ftmp1, FloatRegister ftmp2, FloatRegister ftmp3, FloatRegister ftmp4, Label& Ldone) { + Label Lloop, Lslow; + assert(UseVIS >= 3, "VIS3 is required"); + assert_different_registers(src, dst, cnt, tmp); + assert_different_registers(ftmp1, ftmp2, ftmp3, ftmp4); + + // Check if cnt >= 8 (= 16 bytes) + cmp(cnt, 8); + br(Assembler::less, false, Assembler::pn, Lslow); + delayed()->nop(); + + // Check for 8-byte alignment of src and dst + or3(src, dst, tmp); + andcc(tmp, 7, G0); + br(Assembler::notZero, false, Assembler::pn, Lslow); + // Initialize float register to zero + FloatRegister zerof = ftmp4; + delayed()->fzero(FloatRegisterImpl::D, zerof); + + // Load first 8 bytes + ldf(FloatRegisterImpl::D, src, 0, ftmp1); + + bind(Lloop); + inc(src, 8); + dec(cnt, 8); + + // Inflate the string by interleaving each byte from the source array + // with a zero byte and storing the result in the destination array. + fpmerge(zerof, ftmp1->successor(), ftmp2); + stf(FloatRegisterImpl::D, ftmp2, dst, 8); + fpmerge(zerof, ftmp1, ftmp3); + stf(FloatRegisterImpl::D, ftmp3, dst, 0); + + inc(dst, 16); + + cmp(cnt, 8); + // annul LDX if branch is not taken to prevent access past end of string + br(Assembler::greaterEqual, true, Assembler::pt, Lloop); + delayed()->ldf(FloatRegisterImpl::D, src, 0, ftmp1); + + // Fallback to slow version + bind(Lslow); +} + +// Inflate byte[] to char[]. +void MacroAssembler::string_inflate(Register src, Register dst, Register cnt, Register tmp, Label& Ldone) { + Label Loop; + assert_different_registers(src, dst, cnt, tmp); + + ldub(src, 0, tmp); + bind(Loop); + inc(src); + deccc(cnt); + sth(tmp, dst, 0); + inc(dst, sizeof(jchar)); + // annul LDUB if branch is not taken to prevent access past end of string + br(Assembler::notZero, true, Assembler::pt, Loop); + delayed()->ldub(src, 0, tmp); // hoisted +} + +void MacroAssembler::string_compare(Register str1, Register str2, + Register cnt1, Register cnt2, + Register tmp1, Register tmp2, + Register result, int ae) { + Label Ldone, Lloop; + assert_different_registers(str1, str2, cnt1, cnt2, tmp1, result); + int stride1, stride2; + + // Note: Making use of the fact that compareTo(a, b) == -compareTo(b, a) + // we interchange str1 and str2 in the UL case and negate the result. + // Like this, str1 is always latin1 encoded, expect for the UU case. + + if (ae == StrIntrinsicNode::LU || ae == StrIntrinsicNode::UL) { + srl(cnt2, 1, cnt2); + } + + // See if the lengths are different, and calculate min in cnt1. + // Save diff in case we need it for a tie-breaker. + Label Lskip; + Register diff = tmp1; + subcc(cnt1, cnt2, diff); + br(Assembler::greater, true, Assembler::pt, Lskip); + // cnt2 is shorter, so use its count: + delayed()->mov(cnt2, cnt1); + bind(Lskip); + + // Rename registers + Register limit1 = cnt1; + Register limit2 = limit1; + Register chr1 = result; + Register chr2 = cnt2; + if (ae == StrIntrinsicNode::LU || ae == StrIntrinsicNode::UL) { + // We need an additional register to keep track of two limits + assert_different_registers(str1, str2, cnt1, cnt2, tmp1, tmp2, result); + limit2 = tmp2; + } + + // Is the minimum length zero? + cmp(limit1, (int)0); // use cast to resolve overloading ambiguity + br(Assembler::equal, true, Assembler::pn, Ldone); + // result is difference in lengths + if (ae == StrIntrinsicNode::UU) { + delayed()->sra(diff, 1, result); // Divide by 2 to get number of chars + } else { + delayed()->mov(diff, result); + } + + // Load first characters + if (ae == StrIntrinsicNode::LL) { + stride1 = stride2 = sizeof(jbyte); + ldub(str1, 0, chr1); + ldub(str2, 0, chr2); + } else if (ae == StrIntrinsicNode::UU) { + stride1 = stride2 = sizeof(jchar); + lduh(str1, 0, chr1); + lduh(str2, 0, chr2); + } else { + stride1 = sizeof(jbyte); + stride2 = sizeof(jchar); + ldub(str1, 0, chr1); + lduh(str2, 0, chr2); + } + + // Compare first characters + subcc(chr1, chr2, chr1); + br(Assembler::notZero, false, Assembler::pt, Ldone); + assert(chr1 == result, "result must be pre-placed"); + delayed()->nop(); + + // Check if the strings start at same location + cmp(str1, str2); + brx(Assembler::equal, true, Assembler::pn, Ldone); + delayed()->mov(G0, result); // result is zero + + // We have no guarantee that on 64 bit the higher half of limit is 0 + signx(limit1); + + // Get limit + if (ae == StrIntrinsicNode::LU || ae == StrIntrinsicNode::UL) { + sll(limit1, 1, limit2); + subcc(limit2, stride2, chr2); + } + subcc(limit1, stride1, chr1); + br(Assembler::zero, true, Assembler::pn, Ldone); + // result is difference in lengths + if (ae == StrIntrinsicNode::UU) { + delayed()->sra(diff, 1, result); // Divide by 2 to get number of chars + } else { + delayed()->mov(diff, result); + } + + // Shift str1 and str2 to the end of the arrays, negate limit + add(str1, limit1, str1); + add(str2, limit2, str2); + neg(chr1, limit1); // limit1 = -(limit1-stride1) + if (ae == StrIntrinsicNode::LU || ae == StrIntrinsicNode::UL) { + neg(chr2, limit2); // limit2 = -(limit2-stride2) + } + + // Compare the rest of the characters + if (ae == StrIntrinsicNode::UU) { + lduh(str1, limit1, chr1); + } else { + ldub(str1, limit1, chr1); + } + + bind(Lloop); + if (ae == StrIntrinsicNode::LL) { + ldub(str2, limit2, chr2); + } else { + lduh(str2, limit2, chr2); + } + + subcc(chr1, chr2, chr1); + br(Assembler::notZero, false, Assembler::pt, Ldone); + assert(chr1 == result, "result must be pre-placed"); + delayed()->inccc(limit1, stride1); + if (ae == StrIntrinsicNode::LU || ae == StrIntrinsicNode::UL) { + inccc(limit2, stride2); + } + + // annul LDUB if branch is not taken to prevent access past end of string + br(Assembler::notZero, true, Assembler::pt, Lloop); + if (ae == StrIntrinsicNode::UU) { + delayed()->lduh(str1, limit2, chr1); + } else { + delayed()->ldub(str1, limit1, chr1); + } + + // If strings are equal up to min length, return the length difference. + if (ae == StrIntrinsicNode::UU) { + // Divide by 2 to get number of chars + sra(diff, 1, result); + } else { + mov(diff, result); + } + + // Otherwise, return the difference between the first mismatched chars. + bind(Ldone); + if(ae == StrIntrinsicNode::UL) { + // Negate result (see note above) + neg(result); + } +} + +void MacroAssembler::array_equals(bool is_array_equ, Register ary1, Register ary2, + Register limit, Register tmp, Register result, bool is_byte) { + Label Ldone, Lvector, Lloop; + assert_different_registers(ary1, ary2, limit, tmp, result); + + int length_offset = arrayOopDesc::length_offset_in_bytes(); + int base_offset = arrayOopDesc::base_offset_in_bytes(is_byte ? T_BYTE : T_CHAR); - // Note: limit contains number of bytes (2*char_elements) != 0. - andcc(limit, 0x2, chr1); // trailing character ? + if (is_array_equ) { + // return true if the same array + cmp(ary1, ary2); + brx(Assembler::equal, true, Assembler::pn, Ldone); + delayed()->add(G0, 1, result); // equal + + br_null(ary1, true, Assembler::pn, Ldone); + delayed()->mov(G0, result); // not equal + + br_null(ary2, true, Assembler::pn, Ldone); + delayed()->mov(G0, result); // not equal + + // load the lengths of arrays + ld(Address(ary1, length_offset), limit); + ld(Address(ary2, length_offset), tmp); + + // return false if the two arrays are not equal length + cmp(limit, tmp); + br(Assembler::notEqual, true, Assembler::pn, Ldone); + delayed()->mov(G0, result); // not equal + } + + cmp_zero_and_br(Assembler::zero, limit, Ldone, true, Assembler::pn); + delayed()->add(G0, 1, result); // zero-length arrays are equal + + if (is_array_equ) { + // load array addresses + add(ary1, base_offset, ary1); + add(ary2, base_offset, ary2); + } else { + // We have no guarantee that on 64 bit the higher half of limit is 0 + signx(limit); + } + + if (is_byte) { + Label Lskip; + // check for trailing byte + andcc(limit, 0x1, tmp); + br(Assembler::zero, false, Assembler::pt, Lskip); + delayed()->nop(); + + // compare the trailing byte + sub(limit, sizeof(jbyte), limit); + ldub(ary1, limit, result); + ldub(ary2, limit, tmp); + cmp(result, tmp); + br(Assembler::notEqual, true, Assembler::pt, Ldone); + delayed()->mov(G0, result); // not equal + + // only one byte? + cmp_zero_and_br(zero, limit, Ldone, true, Assembler::pn); + delayed()->add(G0, 1, result); // zero-length arrays are equal + bind(Lskip); + } else if (is_array_equ) { + // set byte count + sll(limit, exact_log2(sizeof(jchar)), limit); + } + + // check for trailing character + andcc(limit, 0x2, tmp); br(Assembler::zero, false, Assembler::pt, Lvector); delayed()->nop(); // compare the trailing char sub(limit, sizeof(jchar), limit); - lduh(ary1, limit, chr1); - lduh(ary2, limit, chr2); - cmp(chr1, chr2); + lduh(ary1, limit, result); + lduh(ary2, limit, tmp); + cmp(result, tmp); br(Assembler::notEqual, true, Assembler::pt, Ldone); delayed()->mov(G0, result); // not equal - // only one char ? + // only one char? cmp_zero_and_br(zero, limit, Ldone, true, Assembler::pn); delayed()->add(G0, 1, result); // zero-length arrays are equal @@ -4284,21 +4645,23 @@ add(ary2, limit, ary2); neg(limit, limit); - lduw(ary1, limit, chr1); + lduw(ary1, limit, result); bind(Lloop); - lduw(ary2, limit, chr2); - cmp(chr1, chr2); + lduw(ary2, limit, tmp); + cmp(result, tmp); br(Assembler::notEqual, true, Assembler::pt, Ldone); delayed()->mov(G0, result); // not equal inccc(limit, 2*sizeof(jchar)); // annul LDUW if branch is not taken to prevent access past end of array br(Assembler::notZero, true, Assembler::pt, Lloop); - delayed()->lduw(ary1, limit, chr1); // hoisted + delayed()->lduw(ary1, limit, result); // hoisted - // Caller should set it: - // add(G0, 1, result); // equals + add(G0, 1, result); // equals + bind(Ldone); } +#endif + // Use BIS for zeroing (count is in bytes). void MacroAssembler::bis_zeroing(Register to, Register count, Register temp, Label& Ldone) { assert(UseBlockZeroing && VM_Version::has_block_zeroing(), "only works with BIS zeroing"); --- old/src/cpu/sparc/vm/macroAssembler_sparc.hpp 2015-10-30 19:14:43.616475390 +0100 +++ new/src/cpu/sparc/vm/macroAssembler_sparc.hpp 2015-10-30 19:14:43.548475388 +0100 @@ -1433,10 +1433,31 @@ void inc_counter(address counter_addr, Register Rtmp1, Register Rtmp2); void inc_counter(int* counter_addr, Register Rtmp1, Register Rtmp2); - // Compare char[] arrays aligned to 4 bytes. - void char_arrays_equals(Register ary1, Register ary2, - Register limit, Register result, - Register chr1, Register chr2, Label& Ldone); +#ifdef COMPILER2 + // Compress char[] to byte[] by compressing 16 bytes at once. Return 0 on failure. + void string_compress_16(Register src, Register dst, Register cnt, Register result, + Register tmp1, Register tmp2, Register tmp3, Register tmp4, + FloatRegister ftmp1, FloatRegister ftmp2, FloatRegister ftmp3, Label& Ldone); + + // Compress char[] to byte[]. Return 0 on failure. + void string_compress(Register src, Register dst, Register cnt, Register tmp, Register result, Label& Ldone); + + // Inflate byte[] to char[] by inflating 16 bytes at once. + void string_inflate_16(Register src, Register dst, Register cnt, Register tmp, + FloatRegister ftmp1, FloatRegister ftmp2, FloatRegister ftmp3, FloatRegister ftmp4, Label& Ldone); + + // Inflate byte[] to char[]. + void string_inflate(Register src, Register dst, Register cnt, Register tmp, Label& Ldone); + + void string_compare(Register str1, Register str2, + Register cnt1, Register cnt2, + Register tmp1, Register tmp2, + Register result, int ae); + + void array_equals(bool is_array_equ, Register ary1, Register ary2, + Register limit, Register tmp, Register result, bool is_byte); +#endif + // Use BIS for zeroing void bis_zeroing(Register to, Register count, Register temp, Label& Ldone); --- old/src/cpu/sparc/vm/sparc.ad 2015-10-30 19:14:43.872475398 +0100 +++ new/src/cpu/sparc/vm/sparc.ad 2015-10-30 19:14:43.804475396 +0100 @@ -2905,232 +2905,6 @@ __ float_cmp( $primary, -1, Fsrc1, Fsrc2, Rdst); %} - - enc_class enc_String_Compare(o0RegP str1, o1RegP str2, g3RegI cnt1, g4RegI cnt2, notemp_iRegI result) %{ - Label Ldone, Lloop; - MacroAssembler _masm(&cbuf); - - Register str1_reg = reg_to_register_object($str1$$reg); - Register str2_reg = reg_to_register_object($str2$$reg); - Register cnt1_reg = reg_to_register_object($cnt1$$reg); - Register cnt2_reg = reg_to_register_object($cnt2$$reg); - Register result_reg = reg_to_register_object($result$$reg); - - assert(result_reg != str1_reg && - result_reg != str2_reg && - result_reg != cnt1_reg && - result_reg != cnt2_reg , - "need different registers"); - - // Compute the minimum of the string lengths(str1_reg) and the - // difference of the string lengths (stack) - - // See if the lengths are different, and calculate min in str1_reg. - // Stash diff in O7 in case we need it for a tie-breaker. - Label Lskip; - __ subcc(cnt1_reg, cnt2_reg, O7); - __ sll(cnt1_reg, exact_log2(sizeof(jchar)), cnt1_reg); // scale the limit - __ br(Assembler::greater, true, Assembler::pt, Lskip); - // cnt2 is shorter, so use its count: - __ delayed()->sll(cnt2_reg, exact_log2(sizeof(jchar)), cnt1_reg); // scale the limit - __ bind(Lskip); - - // reallocate cnt1_reg, cnt2_reg, result_reg - // Note: limit_reg holds the string length pre-scaled by 2 - Register limit_reg = cnt1_reg; - Register chr2_reg = cnt2_reg; - Register chr1_reg = result_reg; - // str{12} are the base pointers - - // Is the minimum length zero? - __ cmp(limit_reg, (int)(0 * sizeof(jchar))); // use cast to resolve overloading ambiguity - __ br(Assembler::equal, true, Assembler::pn, Ldone); - __ delayed()->mov(O7, result_reg); // result is difference in lengths - - // Load first characters - __ lduh(str1_reg, 0, chr1_reg); - __ lduh(str2_reg, 0, chr2_reg); - - // Compare first characters - __ subcc(chr1_reg, chr2_reg, chr1_reg); - __ br(Assembler::notZero, false, Assembler::pt, Ldone); - assert(chr1_reg == result_reg, "result must be pre-placed"); - __ delayed()->nop(); - - { - // Check after comparing first character to see if strings are equivalent - Label LSkip2; - // Check if the strings start at same location - __ cmp(str1_reg, str2_reg); - __ brx(Assembler::notEqual, true, Assembler::pt, LSkip2); - __ delayed()->nop(); - - // Check if the length difference is zero (in O7) - __ cmp(G0, O7); - __ br(Assembler::equal, true, Assembler::pn, Ldone); - __ delayed()->mov(G0, result_reg); // result is zero - - // Strings might not be equal - __ bind(LSkip2); - } - - // We have no guarantee that on 64 bit the higher half of limit_reg is 0 - __ signx(limit_reg); - - __ subcc(limit_reg, 1 * sizeof(jchar), chr1_reg); - __ br(Assembler::equal, true, Assembler::pn, Ldone); - __ delayed()->mov(O7, result_reg); // result is difference in lengths - - // Shift str1_reg and str2_reg to the end of the arrays, negate limit - __ add(str1_reg, limit_reg, str1_reg); - __ add(str2_reg, limit_reg, str2_reg); - __ neg(chr1_reg, limit_reg); // limit = -(limit-2) - - // Compare the rest of the characters - __ lduh(str1_reg, limit_reg, chr1_reg); - __ bind(Lloop); - // __ lduh(str1_reg, limit_reg, chr1_reg); // hoisted - __ lduh(str2_reg, limit_reg, chr2_reg); - __ subcc(chr1_reg, chr2_reg, chr1_reg); - __ br(Assembler::notZero, false, Assembler::pt, Ldone); - assert(chr1_reg == result_reg, "result must be pre-placed"); - __ delayed()->inccc(limit_reg, sizeof(jchar)); - // annul LDUH if branch is not taken to prevent access past end of string - __ br(Assembler::notZero, true, Assembler::pt, Lloop); - __ delayed()->lduh(str1_reg, limit_reg, chr1_reg); // hoisted - - // If strings are equal up to min length, return the length difference. - __ mov(O7, result_reg); - - // Otherwise, return the difference between the first mismatched chars. - __ bind(Ldone); - %} - -enc_class enc_String_Equals(o0RegP str1, o1RegP str2, g3RegI cnt, notemp_iRegI result) %{ - Label Lchar, Lchar_loop, Ldone; - MacroAssembler _masm(&cbuf); - - Register str1_reg = reg_to_register_object($str1$$reg); - Register str2_reg = reg_to_register_object($str2$$reg); - Register cnt_reg = reg_to_register_object($cnt$$reg); - Register tmp1_reg = O7; - Register result_reg = reg_to_register_object($result$$reg); - - assert(result_reg != str1_reg && - result_reg != str2_reg && - result_reg != cnt_reg && - result_reg != tmp1_reg , - "need different registers"); - - __ cmp(str1_reg, str2_reg); //same char[] ? - __ brx(Assembler::equal, true, Assembler::pn, Ldone); - __ delayed()->add(G0, 1, result_reg); - - __ cmp_zero_and_br(Assembler::zero, cnt_reg, Ldone, true, Assembler::pn); - __ delayed()->add(G0, 1, result_reg); // count == 0 - - //rename registers - Register limit_reg = cnt_reg; - Register chr1_reg = result_reg; - Register chr2_reg = tmp1_reg; - - // We have no guarantee that on 64 bit the higher half of limit_reg is 0 - __ signx(limit_reg); - - //check for alignment and position the pointers to the ends - __ or3(str1_reg, str2_reg, chr1_reg); - __ andcc(chr1_reg, 0x3, chr1_reg); - // notZero means at least one not 4-byte aligned. - // We could optimize the case when both arrays are not aligned - // but it is not frequent case and it requires additional checks. - __ br(Assembler::notZero, false, Assembler::pn, Lchar); // char by char compare - __ delayed()->sll(limit_reg, exact_log2(sizeof(jchar)), limit_reg); // set byte count - - // Compare char[] arrays aligned to 4 bytes. - __ char_arrays_equals(str1_reg, str2_reg, limit_reg, result_reg, - chr1_reg, chr2_reg, Ldone); - __ ba(Ldone); - __ delayed()->add(G0, 1, result_reg); - - // char by char compare - __ bind(Lchar); - __ add(str1_reg, limit_reg, str1_reg); - __ add(str2_reg, limit_reg, str2_reg); - __ neg(limit_reg); //negate count - - __ lduh(str1_reg, limit_reg, chr1_reg); - // Lchar_loop - __ bind(Lchar_loop); - __ lduh(str2_reg, limit_reg, chr2_reg); - __ cmp(chr1_reg, chr2_reg); - __ br(Assembler::notEqual, true, Assembler::pt, Ldone); - __ delayed()->mov(G0, result_reg); //not equal - __ inccc(limit_reg, sizeof(jchar)); - // annul LDUH if branch is not taken to prevent access past end of string - __ br(Assembler::notZero, true, Assembler::pt, Lchar_loop); - __ delayed()->lduh(str1_reg, limit_reg, chr1_reg); // hoisted - - __ add(G0, 1, result_reg); //equal - - __ bind(Ldone); - %} - -enc_class enc_Array_Equals(o0RegP ary1, o1RegP ary2, g3RegP tmp1, notemp_iRegI result) %{ - Label Lvector, Ldone, Lloop; - MacroAssembler _masm(&cbuf); - - Register ary1_reg = reg_to_register_object($ary1$$reg); - Register ary2_reg = reg_to_register_object($ary2$$reg); - Register tmp1_reg = reg_to_register_object($tmp1$$reg); - Register tmp2_reg = O7; - Register result_reg = reg_to_register_object($result$$reg); - - int length_offset = arrayOopDesc::length_offset_in_bytes(); - int base_offset = arrayOopDesc::base_offset_in_bytes(T_CHAR); - - // return true if the same array - __ cmp(ary1_reg, ary2_reg); - __ brx(Assembler::equal, true, Assembler::pn, Ldone); - __ delayed()->add(G0, 1, result_reg); // equal - - __ br_null(ary1_reg, true, Assembler::pn, Ldone); - __ delayed()->mov(G0, result_reg); // not equal - - __ br_null(ary2_reg, true, Assembler::pn, Ldone); - __ delayed()->mov(G0, result_reg); // not equal - - //load the lengths of arrays - __ ld(Address(ary1_reg, length_offset), tmp1_reg); - __ ld(Address(ary2_reg, length_offset), tmp2_reg); - - // return false if the two arrays are not equal length - __ cmp(tmp1_reg, tmp2_reg); - __ br(Assembler::notEqual, true, Assembler::pn, Ldone); - __ delayed()->mov(G0, result_reg); // not equal - - __ cmp_zero_and_br(Assembler::zero, tmp1_reg, Ldone, true, Assembler::pn); - __ delayed()->add(G0, 1, result_reg); // zero-length arrays are equal - - // load array addresses - __ add(ary1_reg, base_offset, ary1_reg); - __ add(ary2_reg, base_offset, ary2_reg); - - // renaming registers - Register chr1_reg = result_reg; // for characters in ary1 - Register chr2_reg = tmp2_reg; // for characters in ary2 - Register limit_reg = tmp1_reg; // length - - // set byte count - __ sll(limit_reg, exact_log2(sizeof(jchar)), limit_reg); - - // Compare char[] arrays aligned to 4 bytes. - __ char_arrays_equals(ary1_reg, ary2_reg, limit_reg, result_reg, - chr1_reg, chr2_reg, Ldone); - __ add(G0, 1, result_reg); // equals - - __ bind(Ldone); - %} - enc_class enc_rethrow() %{ cbuf.set_insts_mark(); Register temp_reg = G3; @@ -10275,33 +10049,204 @@ ins_pipe(long_memory_op); %} -instruct string_compare(o0RegP str1, o1RegP str2, g3RegI cnt1, g4RegI cnt2, notemp_iRegI result, - o7RegI tmp, flagsReg ccr) %{ +instruct string_compareL(o0RegP str1, o1RegP str2, g3RegI cnt1, g4RegI cnt2, notemp_iRegI result, + o7RegI tmp, flagsReg ccr) %{ + predicate(((StrCompNode*)n)->encoding() == StrIntrinsicNode::LL); match(Set result (StrComp (Binary str1 cnt1) (Binary str2 cnt2))); effect(USE_KILL str1, USE_KILL str2, USE_KILL cnt1, USE_KILL cnt2, KILL ccr, KILL tmp); ins_cost(300); - format %{ "String Compare $str1,$cnt1,$str2,$cnt2 -> $result // KILL $tmp" %} - ins_encode( enc_String_Compare(str1, str2, cnt1, cnt2, result) ); + format %{ "String Compare byte[] $str1,$cnt1,$str2,$cnt2 -> $result // KILL $tmp" %} + ins_encode %{ + __ string_compare($str1$$Register, $str2$$Register, + $cnt1$$Register, $cnt2$$Register, + $tmp$$Register, $tmp$$Register, + $result$$Register, StrIntrinsicNode::LL); + %} + ins_pipe(long_memory_op); +%} + +instruct string_compareU(o0RegP str1, o1RegP str2, g3RegI cnt1, g4RegI cnt2, notemp_iRegI result, + o7RegI tmp, flagsReg ccr) %{ + predicate(((StrCompNode*)n)->encoding() == StrIntrinsicNode::UU); + match(Set result (StrComp (Binary str1 cnt1) (Binary str2 cnt2))); + effect(USE_KILL str1, USE_KILL str2, USE_KILL cnt1, USE_KILL cnt2, KILL ccr, KILL tmp); + ins_cost(300); + format %{ "String Compare char[] $str1,$cnt1,$str2,$cnt2 -> $result // KILL $tmp" %} + ins_encode %{ + __ string_compare($str1$$Register, $str2$$Register, + $cnt1$$Register, $cnt2$$Register, + $tmp$$Register, $tmp$$Register, + $result$$Register, StrIntrinsicNode::UU); + %} + ins_pipe(long_memory_op); +%} + +instruct string_compareLU(o0RegP str1, o1RegP str2, g3RegI cnt1, g4RegI cnt2, notemp_iRegI result, + o7RegI tmp1, g1RegI tmp2, flagsReg ccr) %{ + predicate(((StrCompNode*)n)->encoding() == StrIntrinsicNode::LU); + match(Set result (StrComp (Binary str1 cnt1) (Binary str2 cnt2))); + effect(USE_KILL str1, USE_KILL str2, USE_KILL cnt1, USE_KILL cnt2, KILL ccr, KILL tmp1, KILL tmp2); + ins_cost(300); + format %{ "String Compare byte[] $str1,$cnt1,$str2,$cnt2 -> $result // KILL $tmp1,$tmp2" %} + ins_encode %{ + __ string_compare($str1$$Register, $str2$$Register, + $cnt1$$Register, $cnt2$$Register, + $tmp1$$Register, $tmp2$$Register, + $result$$Register, StrIntrinsicNode::LU); + %} ins_pipe(long_memory_op); %} -instruct string_equals(o0RegP str1, o1RegP str2, g3RegI cnt, notemp_iRegI result, - o7RegI tmp, flagsReg ccr) %{ +instruct string_compareUL(o0RegP str1, o1RegP str2, g3RegI cnt1, g4RegI cnt2, notemp_iRegI result, + o7RegI tmp1, g1RegI tmp2, flagsReg ccr) %{ + predicate(((StrCompNode*)n)->encoding() == StrIntrinsicNode::UL); + match(Set result (StrComp (Binary str1 cnt1) (Binary str2 cnt2))); + effect(USE_KILL str1, USE_KILL str2, USE_KILL cnt1, USE_KILL cnt2, KILL ccr, KILL tmp1, KILL tmp2); + ins_cost(300); + format %{ "String Compare byte[] $str1,$cnt1,$str2,$cnt2 -> $result // KILL $tmp1,$tmp2" %} + ins_encode %{ + __ string_compare($str2$$Register, $str1$$Register, + $cnt2$$Register, $cnt1$$Register, + $tmp1$$Register, $tmp2$$Register, + $result$$Register, StrIntrinsicNode::UL); + %} + ins_pipe(long_memory_op); +%} + +instruct string_equalsL(o0RegP str1, o1RegP str2, g3RegI cnt, notemp_iRegI result, + o7RegI tmp, flagsReg ccr) %{ + predicate(((StrEqualsNode*)n)->encoding() == StrIntrinsicNode::LL); match(Set result (StrEquals (Binary str1 str2) cnt)); effect(USE_KILL str1, USE_KILL str2, USE_KILL cnt, KILL tmp, KILL ccr); ins_cost(300); - format %{ "String Equals $str1,$str2,$cnt -> $result // KILL $tmp" %} - ins_encode( enc_String_Equals(str1, str2, cnt, result) ); + format %{ "String Equals byte[] $str1,$str2,$cnt -> $result // KILL $tmp" %} + ins_encode %{ + __ array_equals(false, $str1$$Register, $str2$$Register, + $cnt$$Register, $tmp$$Register, + $result$$Register, true /* byte */); + %} ins_pipe(long_memory_op); %} -instruct array_equals(o0RegP ary1, o1RegP ary2, g3RegI tmp1, notemp_iRegI result, - o7RegI tmp2, flagsReg ccr) %{ +instruct string_equalsU(o0RegP str1, o1RegP str2, g3RegI cnt, notemp_iRegI result, + o7RegI tmp, flagsReg ccr) %{ + predicate(((StrEqualsNode*)n)->encoding() == StrIntrinsicNode::UU); + match(Set result (StrEquals (Binary str1 str2) cnt)); + effect(USE_KILL str1, USE_KILL str2, USE_KILL cnt, KILL tmp, KILL ccr); + ins_cost(300); + format %{ "String Equals char[] $str1,$str2,$cnt -> $result // KILL $tmp" %} + ins_encode %{ + __ array_equals(false, $str1$$Register, $str2$$Register, + $cnt$$Register, $tmp$$Register, + $result$$Register, false /* byte */); + %} + ins_pipe(long_memory_op); +%} + +instruct array_equalsB(o0RegP ary1, o1RegP ary2, g3RegI tmp1, notemp_iRegI result, + o7RegI tmp2, flagsReg ccr) %{ + predicate(((AryEqNode*)n)->encoding() == StrIntrinsicNode::LL); + match(Set result (AryEq ary1 ary2)); + effect(USE_KILL ary1, USE_KILL ary2, KILL tmp1, KILL tmp2, KILL ccr); + ins_cost(300); + format %{ "Array Equals $ary1,$ary2 -> $result // KILL $tmp1,$tmp2" %} + ins_encode %{ + __ array_equals(true, $ary1$$Register, $ary2$$Register, + $tmp1$$Register, $tmp2$$Register, + $result$$Register, true /* byte */); + %} + ins_pipe(long_memory_op); +%} + +instruct array_equalsC(o0RegP ary1, o1RegP ary2, g3RegI tmp1, notemp_iRegI result, + o7RegI tmp2, flagsReg ccr) %{ + predicate(((AryEqNode*)n)->encoding() == StrIntrinsicNode::UU); match(Set result (AryEq ary1 ary2)); effect(USE_KILL ary1, USE_KILL ary2, KILL tmp1, KILL tmp2, KILL ccr); ins_cost(300); format %{ "Array Equals $ary1,$ary2 -> $result // KILL $tmp1,$tmp2" %} - ins_encode( enc_Array_Equals(ary1, ary2, tmp1, result)); + ins_encode %{ + __ array_equals(true, $ary1$$Register, $ary2$$Register, + $tmp1$$Register, $tmp2$$Register, + $result$$Register, false /* byte */); + %} + ins_pipe(long_memory_op); +%} + +// char[] to byte[] compression +instruct string_compress(o0RegP src, o1RegP dst, g3RegI len, notemp_iRegI result, iRegL tmp, flagsReg ccr) %{ + predicate(UseVIS < 3); + match(Set result (StrCompressedCopy src (Binary dst len))); + effect(TEMP result, TEMP tmp, USE_KILL src, USE_KILL dst, USE_KILL len, KILL ccr); + ins_cost(300); + format %{ "String Compress $src,$dst,$len -> $result // KILL $tmp" %} + ins_encode %{ + Label Ldone; + __ signx($len$$Register); + __ cmp_zero_and_br(Assembler::zero, $len$$Register, Ldone, false, Assembler::pn); + __ delayed()->mov($len$$Register, $result$$Register); // copy count + __ string_compress($src$$Register, $dst$$Register, $len$$Register, $result$$Register, $tmp$$Register, Ldone); + __ bind(Ldone); + %} + ins_pipe(long_memory_op); +%} + +// fast char[] to byte[] compression using VIS instructions +instruct string_compress_fast(o0RegP src, o1RegP dst, g3RegI len, notemp_iRegI result, + iRegL tmp1, iRegL tmp2, iRegL tmp3, iRegL tmp4, + regD ftmp1, regD ftmp2, regD ftmp3, flagsReg ccr) %{ + predicate(UseVIS >= 3); + match(Set result (StrCompressedCopy src (Binary dst len))); + effect(TEMP result, TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4, TEMP ftmp1, TEMP ftmp2, TEMP ftmp3, USE_KILL src, USE_KILL dst, USE_KILL len, KILL ccr); + ins_cost(300); + format %{ "String Compress Fast $src,$dst,$len -> $result // KILL $tmp1,$tmp2,$tmp3,$tmp4,$ftmp1,$ftmp2,$ftmp3" %} + ins_encode %{ + Label Ldone; + __ signx($len$$Register); + __ string_compress_16($src$$Register, $dst$$Register, $len$$Register, $result$$Register, + $tmp1$$Register, $tmp2$$Register, $tmp3$$Register, $tmp4$$Register, + $ftmp1$$FloatRegister, $ftmp2$$FloatRegister, $ftmp3$$FloatRegister, Ldone); + __ cmp_and_brx_short($len$$Register, 0, Assembler::equal, Assembler::pn, Ldone); + __ string_compress($src$$Register, $dst$$Register, $len$$Register, $result$$Register, $tmp1$$Register, Ldone); + __ bind(Ldone); + %} + ins_pipe(long_memory_op); +%} + +// byte[] to char[] inflation +instruct string_inflate(Universe dummy, o0RegP src, o1RegP dst, g3RegI len, + iRegL tmp, flagsReg ccr) %{ + match(Set dummy (StrInflatedCopy src (Binary dst len))); + effect(TEMP tmp, USE_KILL src, USE_KILL dst, USE_KILL len, KILL ccr); + ins_cost(300); + format %{ "String Inflate $src,$dst,$len // KILL $tmp" %} + ins_encode %{ + Label Ldone; + __ signx($len$$Register); + __ cmp_and_brx_short($len$$Register, 0, Assembler::equal, Assembler::pn, Ldone); + __ string_inflate($src$$Register, $dst$$Register, $len$$Register, $tmp$$Register, Ldone); + __ bind(Ldone); + %} + ins_pipe(long_memory_op); +%} + +// fast byte[] to char[] inflation using VIS instructions +instruct string_inflate_fast(Universe dummy, o0RegP src, o1RegP dst, g3RegI len, + iRegL tmp, regD ftmp1, regD ftmp2, regD ftmp3, regD ftmp4, flagsReg ccr) %{ + predicate(UseVIS >= 3); + match(Set dummy (StrInflatedCopy src (Binary dst len))); + effect(TEMP tmp, TEMP ftmp1, TEMP ftmp2, TEMP ftmp3, TEMP ftmp4, USE_KILL src, USE_KILL dst, USE_KILL len, KILL ccr); + ins_cost(300); + format %{ "String Inflate Fast $src,$dst,$len // KILL $tmp,$ftmp1,$ftmp2,$ftmp3,$ftmp4" %} + ins_encode %{ + Label Ldone; + __ signx($len$$Register); + __ string_inflate_16($src$$Register, $dst$$Register, $len$$Register, $tmp$$Register, + $ftmp1$$FloatRegister, $ftmp2$$FloatRegister, $ftmp3$$FloatRegister, $ftmp4$$FloatRegister, Ldone); + __ cmp_and_brx_short($len$$Register, 0, Assembler::equal, Assembler::pn, Ldone); + __ string_inflate($src$$Register, $dst$$Register, $len$$Register, $tmp$$Register, Ldone); + __ bind(Ldone); + %} ins_pipe(long_memory_op); %} --- old/src/cpu/x86/vm/assembler_x86.cpp 2015-10-30 19:14:44.176475408 +0100 +++ new/src/cpu/x86/vm/assembler_x86.cpp 2015-10-30 19:14:44.108475406 +0100 @@ -3027,6 +3027,35 @@ emit_int8(imm8); } +void Assembler::pcmpeqw(XMMRegister dst, XMMRegister src) { + NOT_LP64(assert(VM_Version::supports_sse2(), "")); + emit_simd_arith(0x75, dst, src, VEX_SIMD_66, + false, (VM_Version::supports_avx512dq() == false)); +} + +void Assembler::vpcmpeqw(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { + assert(UseAVX > 0, "some form of AVX must be enabled"); + emit_vex_arith(0x75, dst, nds, src, VEX_SIMD_66, vector_len, + false, (VM_Version::supports_avx512dq() == false)); +} + +void Assembler::pmovmskb(Register dst, XMMRegister src) { + assert(VM_Version::supports_sse2(), ""); + int encode = simd_prefix_and_encode(as_XMMRegister(dst->encoding()), xnoreg, src, VEX_SIMD_66, true, VEX_OPCODE_0F, + false, AVX_128bit, (VM_Version::supports_avx512dq() == false)); + emit_int8((unsigned char)0xD7); + emit_int8((unsigned char)(0xC0 | encode)); +} + +void Assembler::vpmovmskb(Register dst, XMMRegister src) { + assert(VM_Version::supports_avx2(), ""); + int vector_len = AVX_256bit; + int encode = vex_prefix_and_encode(as_XMMRegister(dst->encoding()), xnoreg, src, VEX_SIMD_66, + vector_len, VEX_OPCODE_0F, true, false); + emit_int8((unsigned char)0xD7); + emit_int8((unsigned char)(0xC0 | encode)); +} + void Assembler::pextrd(Register dst, XMMRegister src, int imm8) { assert(VM_Version::supports_sse4_1(), ""); int encode = simd_prefix_and_encode(as_XMMRegister(dst->encoding()), xnoreg, src, VEX_SIMD_66, /* no_mask_reg */ true, @@ -3099,6 +3128,17 @@ emit_int8((unsigned char)(0xC0 | encode)); } +void Assembler::vpmovzxbw(XMMRegister dst, Address src) { + assert(VM_Version::supports_avx(), ""); + InstructionMark im(this); + bool vector256 = true; + assert(dst != xnoreg, "sanity"); + int dst_enc = dst->encoding(); + vex_prefix(src, 0, dst_enc, VEX_SIMD_66, VEX_OPCODE_0F_38, false, vector256); + emit_int8(0x30); + emit_operand(dst, src); +} + // generic void Assembler::pop(Register dst) { int encode = prefix_and_encode(dst->encoding()); @@ -5347,6 +5387,16 @@ emit_int8((unsigned char)(0xC0 | encode)); } +// duplicate 2-bytes integer data from src into 16 locations in dest +void Assembler::vpbroadcastw(XMMRegister dst, XMMRegister src) { + assert(VM_Version::supports_avx2(), ""); + bool vector_len = AVX_256bit; + int encode = vex_prefix_and_encode(dst, xnoreg, src, VEX_SIMD_66, + vector_len, VEX_OPCODE_0F_38, false); + emit_int8(0x79); + emit_int8((unsigned char)(0xC0 | encode)); +} + // duplicate 1-byte integer data from src into 16||32|64 locations in dest : requires AVX512BW and AVX512VL void Assembler::evpbroadcastb(XMMRegister dst, XMMRegister src, int vector_len) { _instruction_uses_vl = true; --- old/src/cpu/x86/vm/assembler_x86.hpp 2015-10-30 19:14:44.460475417 +0100 +++ new/src/cpu/x86/vm/assembler_x86.hpp 2015-10-30 19:14:44.388475415 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -1680,6 +1680,12 @@ void pcmpestri(XMMRegister xmm1, XMMRegister xmm2, int imm8); void pcmpestri(XMMRegister xmm1, Address src, int imm8); + void pcmpeqw(XMMRegister dst, XMMRegister src); + void vpcmpeqw(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len); + + void pmovmskb(Register dst, XMMRegister src); + void vpmovmskb(Register dst, XMMRegister src); + // SSE 4.1 extract void pextrd(Register dst, XMMRegister src, int imm8); void pextrq(Register dst, XMMRegister src, int imm8); @@ -1696,6 +1702,8 @@ void pmovzxbw(XMMRegister dst, XMMRegister src); void pmovzxbw(XMMRegister dst, Address src); + void vpmovzxbw(XMMRegister dst, Address src); + #ifndef _LP64 // no 32bit push/pop on amd64 void popl(Address dst); #endif @@ -2110,6 +2118,9 @@ // duplicate 4-bytes integer data from src into 8 locations in dest void vpbroadcastd(XMMRegister dst, XMMRegister src); + // duplicate 2-bytes integer data from src into 16 locations in dest + void vpbroadcastw(XMMRegister dst, XMMRegister src); + // duplicate n-bytes integer data from src into vector_len locations in dest void evpbroadcastb(XMMRegister dst, XMMRegister src, int vector_len); void evpbroadcastb(XMMRegister dst, Address src, int vector_len); --- old/src/cpu/x86/vm/c1_LIRAssembler_x86.cpp 2015-10-30 19:14:44.716475425 +0100 +++ new/src/cpu/x86/vm/c1_LIRAssembler_x86.cpp 2015-10-30 19:14:44.648475423 +0100 @@ -509,86 +509,6 @@ } -// This is the fast version of java.lang.String.compare; it has not -// OSR-entry and therefore, we generate a slow version for OSR's -void LIR_Assembler::emit_string_compare(LIR_Opr arg0, LIR_Opr arg1, LIR_Opr dst, CodeEmitInfo* info) { - __ movptr (rbx, rcx); // receiver is in rcx - __ movptr (rax, arg1->as_register()); - - // Get addresses of first characters from both Strings - __ load_heap_oop(rsi, Address(rax, java_lang_String::value_offset_in_bytes())); - if (java_lang_String::has_offset_field()) { - __ movptr (rcx, Address(rax, java_lang_String::offset_offset_in_bytes())); - __ movl (rax, Address(rax, java_lang_String::count_offset_in_bytes())); - __ lea (rsi, Address(rsi, rcx, Address::times_2, arrayOopDesc::base_offset_in_bytes(T_CHAR))); - } else { - __ movl (rax, Address(rsi, arrayOopDesc::length_offset_in_bytes())); - __ lea (rsi, Address(rsi, arrayOopDesc::base_offset_in_bytes(T_CHAR))); - } - - // rbx, may be NULL - add_debug_info_for_null_check_here(info); - __ load_heap_oop(rdi, Address(rbx, java_lang_String::value_offset_in_bytes())); - if (java_lang_String::has_offset_field()) { - __ movptr (rcx, Address(rbx, java_lang_String::offset_offset_in_bytes())); - __ movl (rbx, Address(rbx, java_lang_String::count_offset_in_bytes())); - __ lea (rdi, Address(rdi, rcx, Address::times_2, arrayOopDesc::base_offset_in_bytes(T_CHAR))); - } else { - __ movl (rbx, Address(rdi, arrayOopDesc::length_offset_in_bytes())); - __ lea (rdi, Address(rdi, arrayOopDesc::base_offset_in_bytes(T_CHAR))); - } - - // compute minimum length (in rax) and difference of lengths (on top of stack) - __ mov (rcx, rbx); - __ subptr(rbx, rax); // subtract lengths - __ push (rbx); // result - __ cmov (Assembler::lessEqual, rax, rcx); - - // is minimum length 0? - Label noLoop, haveResult; - __ testptr (rax, rax); - __ jcc (Assembler::zero, noLoop); - - // compare first characters - __ load_unsigned_short(rcx, Address(rdi, 0)); - __ load_unsigned_short(rbx, Address(rsi, 0)); - __ subl(rcx, rbx); - __ jcc(Assembler::notZero, haveResult); - // starting loop - __ decrement(rax); // we already tested index: skip one - __ jcc(Assembler::zero, noLoop); - - // set rsi.edi to the end of the arrays (arrays have same length) - // negate the index - - __ lea(rsi, Address(rsi, rax, Address::times_2, type2aelembytes(T_CHAR))); - __ lea(rdi, Address(rdi, rax, Address::times_2, type2aelembytes(T_CHAR))); - __ negptr(rax); - - // compare the strings in a loop - - Label loop; - __ align(wordSize); - __ bind(loop); - __ load_unsigned_short(rcx, Address(rdi, rax, Address::times_2, 0)); - __ load_unsigned_short(rbx, Address(rsi, rax, Address::times_2, 0)); - __ subl(rcx, rbx); - __ jcc(Assembler::notZero, haveResult); - __ increment(rax); - __ jcc(Assembler::notZero, loop); - - // strings are equal up to min length - - __ bind(noLoop); - __ pop(rax); - return_op(LIR_OprFact::illegalOpr); - - __ bind(haveResult); - // leave instruction is going to discard the TOS value - __ mov (rax, rcx); // result of call is in rax, -} - - void LIR_Assembler::return_op(LIR_Opr result) { assert(result->is_illegal() || !result->is_single_cpu() || result->as_register() == rax, "word returns are in rax,"); if (!result->is_illegal() && result->is_float_kind() && !result->is_xmm_register()) { --- old/src/cpu/x86/vm/globals_x86.hpp 2015-10-30 19:14:44.988475434 +0100 +++ new/src/cpu/x86/vm/globals_x86.hpp 2015-10-30 19:14:44.920475431 +0100 @@ -91,6 +91,8 @@ define_pd_global(uintx, TypeProfileLevel, 111); +define_pd_global(bool, CompactStrings, true); + define_pd_global(bool, PreserveFramePointer, false); #define ARCH_FLAGS(develop, product, diagnostic, experimental, notproduct, range, constraint) \ --- old/src/cpu/x86/vm/macroAssembler_x86.cpp 2015-10-30 19:14:45.232475441 +0100 +++ new/src/cpu/x86/vm/macroAssembler_x86.cpp 2015-10-30 19:14:45.164475439 +0100 @@ -46,6 +46,9 @@ #include "gc/g1/heapRegion.hpp" #endif // INCLUDE_ALL_GCS #include "crc32c.h" +#ifdef COMPILER2 +#include "opto/intrinsicnode.hpp" +#endif #ifdef PRODUCT #define BLOCK_COMMENT(str) /* nothing */ @@ -6299,25 +6302,34 @@ } } +#ifdef COMPILER2 + // IndexOf for constant substrings with size >= 8 chars // which don't need to be loaded through stack. void MacroAssembler::string_indexofC8(Register str1, Register str2, Register cnt1, Register cnt2, int int_cnt2, Register result, - XMMRegister vec, Register tmp) { + XMMRegister vec, Register tmp, + int ae) { ShortBranchVerifier sbv(this); assert(UseSSE42Intrinsics, "SSE4.2 is required"); + assert(ae != StrIntrinsicNode::LU, "Invalid encoding"); - // This method uses pcmpestri instruction with bound registers + // This method uses the pcmpestri instruction with bound registers // inputs: // xmm - substring // rax - substring length (elements count) // mem - scanned string // rdx - string length (elements count) // 0xd - mode: 1100 (substring search) + 01 (unsigned shorts) + // 0xc - mode: 1100 (substring search) + 00 (unsigned bytes) // outputs: // rcx - matched index in string assert(cnt1 == rdx && cnt2 == rax && tmp == rcx, "pcmpestri"); + int mode = (ae == StrIntrinsicNode::LL) ? 0x0c : 0x0d; // bytes or shorts + int stride = (ae == StrIntrinsicNode::LL) ? 16 : 8; //UU, UL -> 8 + Address::ScaleFactor scale1 = (ae == StrIntrinsicNode::LL) ? Address::times_1 : Address::times_2; + Address::ScaleFactor scale2 = (ae == StrIntrinsicNode::UL) ? Address::times_1 : scale1; Label RELOAD_SUBSTR, SCAN_TO_SUBSTR, SCAN_SUBSTR, RET_FOUND, RET_NOT_FOUND, EXIT, FOUND_SUBSTR, @@ -6326,20 +6338,28 @@ // Note, inline_string_indexOf() generates checks: // if (substr.count > string.count) return -1; // if (substr.count == 0) return 0; - assert(int_cnt2 >= 8, "this code isused only for cnt2 >= 8 chars"); + assert(int_cnt2 >= stride, "this code is used only for cnt2 >= 8 chars"); // Load substring. - movdqu(vec, Address(str2, 0)); + if (ae == StrIntrinsicNode::UL) { + pmovzxbw(vec, Address(str2, 0)); + } else { + movdqu(vec, Address(str2, 0)); + } movl(cnt2, int_cnt2); movptr(result, str1); // string addr - if (int_cnt2 > 8) { + if (int_cnt2 > stride) { jmpb(SCAN_TO_SUBSTR); // Reload substr for rescan, this code // is executed only for large substrings (> 8 chars) bind(RELOAD_SUBSTR); - movdqu(vec, Address(str2, 0)); + if (ae == StrIntrinsicNode::UL) { + pmovzxbw(vec, Address(str2, 0)); + } else { + movdqu(vec, Address(str2, 0)); + } negptr(cnt2); // Jumped here with negative cnt2, convert to positive bind(RELOAD_STR); @@ -6358,15 +6378,15 @@ cmpl(cnt1, cnt2); jccb(Assembler::negative, RET_NOT_FOUND); // Left less then substring - addptr(result, 2); + addptr(result, (1< 8) // Scan string for start of substr in 16-byte vectors bind(SCAN_TO_SUBSTR); - pcmpestri(vec, Address(result, 0), 0x0d); + pcmpestri(vec, Address(result, 0), mode); jccb(Assembler::below, FOUND_CANDIDATE); // CF == 1 - subl(cnt1, 8); + subl(cnt1, stride); jccb(Assembler::lessEqual, RET_NOT_FOUND); // Scanned full string cmpl(cnt1, cnt2); jccb(Assembler::negative, RET_NOT_FOUND); // Left less then substring @@ -6376,19 +6396,19 @@ // Found a potential substr bind(FOUND_CANDIDATE); // Matched whole vector if first element matched (tmp(rcx) == 0). - if (int_cnt2 == 8) { + if (int_cnt2 == stride) { jccb(Assembler::overflow, RET_FOUND); // OF == 1 } else { // int_cnt2 > 8 jccb(Assembler::overflow, FOUND_SUBSTR); } // After pcmpestri tmp(rcx) contains matched element index // Compute start addr of substr - lea(result, Address(result, tmp, Address::times_2)); + lea(result, Address(result, tmp, scale1)); // Make sure string is still long enough subl(cnt1, tmp); cmpl(cnt1, cnt2); - if (int_cnt2 == 8) { + if (int_cnt2 == stride) { jccb(Assembler::greaterEqual, SCAN_TO_SUBSTR); } else { // int_cnt2 > 8 jccb(Assembler::greaterEqual, MATCH_SUBSTR_HEAD); @@ -6399,11 +6419,11 @@ movl(result, -1); jmpb(EXIT); - if (int_cnt2 > 8) { + if (int_cnt2 > stride) { // This code is optimized for the case when whole substring // is matched if its head is matched. bind(MATCH_SUBSTR_HEAD); - pcmpestri(vec, Address(result, 0), 0x0d); + pcmpestri(vec, Address(result, 0), mode); // Reload only string if does not match jccb(Assembler::noOverflow, RELOAD_STR); // OF == 0 @@ -6412,31 +6432,41 @@ bind(FOUND_SUBSTR); // First 8 chars are already matched. negptr(cnt2); - addptr(cnt2, 8); + addptr(cnt2, stride); bind(SCAN_SUBSTR); - subl(cnt1, 8); - cmpl(cnt2, -8); // Do not read beyond substring + subl(cnt1, stride); + cmpl(cnt2, -stride); // Do not read beyond substring jccb(Assembler::lessEqual, CONT_SCAN_SUBSTR); // Back-up strings to avoid reading beyond substring: // cnt1 = cnt1 - cnt2 + 8 addl(cnt1, cnt2); // cnt2 is negative - addl(cnt1, 8); - movl(cnt2, 8); negptr(cnt2); + addl(cnt1, stride); + movl(cnt2, stride); negptr(cnt2); bind(CONT_SCAN_SUBSTR); if (int_cnt2 < (int)G) { - movdqu(vec, Address(str2, cnt2, Address::times_2, int_cnt2*2)); - pcmpestri(vec, Address(result, cnt2, Address::times_2, int_cnt2*2), 0x0d); + int tail_off1 = int_cnt2< string.count) return -1; // if (substr.count == 0) return 0; // - assert(int_cnt2 == -1 || (0 < int_cnt2 && int_cnt2 < 8), "should be != 0"); - - // This method uses pcmpestri instruction with bound registers + int stride = (ae == StrIntrinsicNode::LL) ? 16 : 8; //UU, UL -> 8 + assert(int_cnt2 == -1 || (0 < int_cnt2 && int_cnt2 < stride), "should be != 0"); + // This method uses the pcmpestri instruction with bound registers // inputs: // xmm - substring // rax - substring length (elements count) // mem - scanned string // rdx - string length (elements count) // 0xd - mode: 1100 (substring search) + 01 (unsigned shorts) + // 0xc - mode: 1100 (substring search) + 00 (unsigned bytes) // outputs: // rcx - matched index in string assert(cnt1 == rdx && cnt2 == rax && tmp == rcx, "pcmpestri"); + int mode = (ae == StrIntrinsicNode::LL) ? 0x0c : 0x0d; // bytes or shorts + Address::ScaleFactor scale1 = (ae == StrIntrinsicNode::LL) ? Address::times_1 : Address::times_2; + Address::ScaleFactor scale2 = (ae == StrIntrinsicNode::UL) ? Address::times_1 : scale1; Label RELOAD_SUBSTR, SCAN_TO_SUBSTR, SCAN_SUBSTR, ADJUST_STR, RET_FOUND, RET_NOT_FOUND, CLEANUP, FOUND_SUBSTR, @@ -6492,23 +6531,40 @@ movptr(tmp, rsp); // save old SP if (int_cnt2 > 0) { // small (< 8 chars) constant substring - if (int_cnt2 == 1) { // One char + if (int_cnt2 == (1>>scale2)) { // One byte + assert((ae == StrIntrinsicNode::LL || ae == StrIntrinsicNode::UL), "Only possible for latin1 encoding"); + load_unsigned_byte(result, Address(str2, 0)); + movdl(vec, result); // move 32 bits + } else if (ae == StrIntrinsicNode::LL && int_cnt2 == 3) { // Three bytes + // Not enough header space in 32-bit VM: 12+3 = 15. + movl(result, Address(str2, -1)); + shrl(result, 8); + movdl(vec, result); // move 32 bits + } else if (ae != StrIntrinsicNode::UL && int_cnt2 == (2>>scale2)) { // One char load_unsigned_short(result, Address(str2, 0)); movdl(vec, result); // move 32 bits - } else if (int_cnt2 == 2) { // Two chars + } else if (ae != StrIntrinsicNode::UL && int_cnt2 == (4>>scale2)) { // Two chars movdl(vec, Address(str2, 0)); // move 32 bits - } else if (int_cnt2 == 4) { // Four chars + } else if (ae != StrIntrinsicNode::UL && int_cnt2 == (8>>scale2)) { // Four chars movq(vec, Address(str2, 0)); // move 64 bits - } else { // cnt2 = { 3, 5, 6, 7 } + } else { // cnt2 = { 3, 5, 6, 7 } || (ae == StrIntrinsicNode::UL && cnt2 ={2, ..., 7}) // Array header size is 12 bytes in 32-bit VM // + 6 bytes for 3 chars == 18 bytes, // enough space to load vec and shift. assert(HeapWordSize*TypeArrayKlass::header_size() >= 12,"sanity"); - movdqu(vec, Address(str2, (int_cnt2*2)-16)); - psrldq(vec, 16-(int_cnt2*2)); + if (ae == StrIntrinsicNode::UL) { + int tail_off = int_cnt2-8; + pmovzxbw(vec, Address(str2, tail_off)); + psrldq(vec, -2*tail_off); + } + else { + int tail_off = int_cnt2*(1< 0) { // Constant substring // Repeat search for small substring (< 8 chars) // from new point without reloading substring. // Have to check that we don't read beyond string. - cmpl(tmp, 8-int_cnt2); + cmpl(tmp, stride-int_cnt2); jccb(Assembler::greater, ADJUST_STR); // Fall through if matched whole substring. } else { // non constant @@ -6660,12 +6735,12 @@ addl(tmp, cnt2); // Found result if we matched whole substring. - cmpl(tmp, 8); + cmpl(tmp, stride); jccb(Assembler::lessEqual, RET_FOUND); // Repeat search for small substring (<= 8 chars) // from new point 'str1' without reloading substring. - cmpl(cnt2, 8); + cmpl(cnt2, stride); // Have to check that we don't read beyond string. jccb(Assembler::lessEqual, ADJUST_STR); @@ -6678,26 +6753,40 @@ jccb(Assembler::equal, CHECK_NEXT); bind(SCAN_SUBSTR); - pcmpestri(vec, Address(str1, 0), 0x0d); + pcmpestri(vec, Address(str1, 0), mode); // Need to reload strings pointers if not matched whole vector jcc(Assembler::noOverflow, RELOAD_SUBSTR); // OF == 0 bind(CHECK_NEXT); - subl(cnt2, 8); + subl(cnt2, stride); jccb(Assembler::lessEqual, RET_FOUND_LONG); // Found full substring addptr(str1, 16); - addptr(str2, 16); - subl(cnt1, 8); - cmpl(cnt2, 8); // Do not read beyond substring + if (ae == StrIntrinsicNode::UL) { + addptr(str2, 8); + } else { + addptr(str2, 16); + } + subl(cnt1, stride); + cmpl(cnt2, stride); // Do not read beyond substring jccb(Assembler::greaterEqual, CONT_SCAN_SUBSTR); // Back-up strings to avoid reading beyond substring. - lea(str2, Address(str2, cnt2, Address::times_2, -16)); - lea(str1, Address(str1, cnt2, Address::times_2, -16)); + + if (ae == StrIntrinsicNode::UL) { + lea(str2, Address(str2, cnt2, scale2, -8)); + lea(str1, Address(str1, cnt2, scale1, -16)); + } else { + lea(str2, Address(str2, cnt2, scale2, -16)); + lea(str1, Address(str1, cnt2, scale1, -16)); + } subl(cnt1, cnt2); - movl(cnt2, 8); - addl(cnt1, 8); + movl(cnt2, stride); + addl(cnt1, stride); bind(CONT_SCAN_SUBSTR); - movdqu(vec, Address(str2, 0)); + if (ae == StrIntrinsicNode::UL) { + pmovzxbw(vec, Address(str2, 0)); + } else { + movdqu(vec, Address(str2, 0)); + } jmpb(SCAN_SUBSTR); bind(RET_FOUND_LONG); @@ -6707,20 +6796,143 @@ bind(RET_FOUND); // Compute substr offset subptr(result, str1); - shrl(result, 1); // index - + if (ae == StrIntrinsicNode::UU || ae == StrIntrinsicNode::UL) { + shrl(result, 1); // index + } bind(CLEANUP); pop(rsp); // restore SP } // string_indexof -// Compare strings. +void MacroAssembler::string_indexof_char(Register str1, Register cnt1, Register ch, Register result, + XMMRegister vec1, XMMRegister vec2, XMMRegister vec3, Register tmp) { + ShortBranchVerifier sbv(this); + assert(UseSSE42Intrinsics, "SSE4.2 is required"); + + int stride = 8; + + Label FOUND_CHAR, SCAN_TO_CHAR, SCAN_TO_CHAR_LOOP, + SCAN_TO_8_CHAR, SCAN_TO_8_CHAR_LOOP, SCAN_TO_16_CHAR_LOOP, + RET_NOT_FOUND, SCAN_TO_8_CHAR_INIT, + FOUND_SEQ_CHAR, DONE_LABEL; + + movptr(result, str1); + if (UseAVX >= 2) { + cmpl(cnt1, stride); + jccb(Assembler::less, SCAN_TO_CHAR_LOOP); + cmpl(cnt1, 2*stride); + jccb(Assembler::less, SCAN_TO_8_CHAR_INIT); + movdl(vec1, ch); + vpbroadcastw(vec1, vec1); + vpxor(vec2, vec2); + movl(tmp, cnt1); + andl(tmp, 0xFFFFFFF0); //vector count (in chars) + andl(cnt1,0x0000000F); //tail count (in chars) + + bind(SCAN_TO_16_CHAR_LOOP); + vmovdqu(vec3, Address(result, 0)); + vpcmpeqw(vec3, vec3, vec1, true); + vptest(vec2, vec3); + jcc(Assembler::carryClear, FOUND_CHAR); + addptr(result, 32); + subl(tmp, 2*stride); + jccb(Assembler::notZero, SCAN_TO_16_CHAR_LOOP); + jmp(SCAN_TO_8_CHAR); + bind(SCAN_TO_8_CHAR_INIT); + movdl(vec1, ch); + pshuflw(vec1, vec1, 0x00); + pshufd(vec1, vec1, 0); + pxor(vec2, vec2); + } + if (UseAVX >= 2 || UseSSE42Intrinsics) { + bind(SCAN_TO_8_CHAR); + cmpl(cnt1, stride); + if (UseAVX >= 2) { + jccb(Assembler::less, SCAN_TO_CHAR); + } + if (!(UseAVX >= 2)) { + jccb(Assembler::less, SCAN_TO_CHAR_LOOP); + movdl(vec1, ch); + pshuflw(vec1, vec1, 0x00); + pshufd(vec1, vec1, 0); + pxor(vec2, vec2); + } + movl(tmp, cnt1); + andl(tmp, 0xFFFFFFF8); //vector count (in chars) + andl(cnt1,0x00000007); //tail count (in chars) + + bind(SCAN_TO_8_CHAR_LOOP); + movdqu(vec3, Address(result, 0)); + pcmpeqw(vec3, vec1); + ptest(vec2, vec3); + jcc(Assembler::carryClear, FOUND_CHAR); + addptr(result, 16); + subl(tmp, stride); + jccb(Assembler::notZero, SCAN_TO_8_CHAR_LOOP); + } + bind(SCAN_TO_CHAR); + testl(cnt1, cnt1); + jcc(Assembler::zero, RET_NOT_FOUND); + + bind(SCAN_TO_CHAR_LOOP); + load_unsigned_short(tmp, Address(result, 0)); + cmpl(ch, tmp); + jccb(Assembler::equal, FOUND_SEQ_CHAR); + addptr(result, 2); + subl(cnt1, 1); + jccb(Assembler::zero, RET_NOT_FOUND); + jmp(SCAN_TO_CHAR_LOOP); + + bind(RET_NOT_FOUND); + movl(result, -1); + jmpb(DONE_LABEL); + + if (UseAVX >= 2 || UseSSE42Intrinsics) { + bind(FOUND_CHAR); + if (UseAVX >= 2) { + vpmovmskb(tmp, vec3); + } else { + pmovmskb(tmp, vec3); + } + bsfl(ch, tmp); + addl(result, ch); + } + + bind(FOUND_SEQ_CHAR); + subptr(result, str1); + shrl(result, 1); + + bind(DONE_LABEL); +} // string_indexof_char + +// helper function for string_compare +void MacroAssembler::load_next_elements(Register elem1, Register elem2, Register str1, Register str2, + Address::ScaleFactor scale, Address::ScaleFactor scale1, + Address::ScaleFactor scale2, Register index, int ae) { + if (ae == StrIntrinsicNode::LL) { + load_unsigned_byte(elem1, Address(str1, index, scale, 0)); + load_unsigned_byte(elem2, Address(str2, index, scale, 0)); + } else if (ae == StrIntrinsicNode::UU) { + load_unsigned_short(elem1, Address(str1, index, scale, 0)); + load_unsigned_short(elem2, Address(str2, index, scale, 0)); + } else { + load_unsigned_byte(elem1, Address(str1, index, scale1, 0)); + load_unsigned_short(elem2, Address(str2, index, scale2, 0)); + } +} + +// Compare strings, used for char[] and byte[]. void MacroAssembler::string_compare(Register str1, Register str2, Register cnt1, Register cnt2, Register result, - XMMRegister vec1) { + XMMRegister vec1, int ae) { ShortBranchVerifier sbv(this); Label LENGTH_DIFF_LABEL, POP_LABEL, DONE_LABEL, WHILE_HEAD_LABEL; + int stride, stride2, adr_stride, adr_stride1, adr_stride2; + Address::ScaleFactor scale, scale1, scale2; + if (ae == StrIntrinsicNode::LU || ae == StrIntrinsicNode::UL) { + shrl(cnt2, 1); + } // Compute the minimum of the string lengths and the // difference of the string lengths (stack). // Do the conditional move stuff @@ -6732,32 +6944,67 @@ // Is the minimum length zero? testl(cnt2, cnt2); jcc(Assembler::zero, LENGTH_DIFF_LABEL); - - // Compare first characters - load_unsigned_short(result, Address(str1, 0)); - load_unsigned_short(cnt1, Address(str2, 0)); + if (ae == StrIntrinsicNode::LL) { + // Load first bytes + load_unsigned_byte(result, Address(str1, 0)); + load_unsigned_byte(cnt1, Address(str2, 0)); + } else if (ae == StrIntrinsicNode::UU) { + // Load first characters + load_unsigned_short(result, Address(str1, 0)); + load_unsigned_short(cnt1, Address(str2, 0)); + } else { + load_unsigned_byte(result, Address(str1, 0)); + load_unsigned_short(cnt1, Address(str2, 0)); + } subl(result, cnt1); jcc(Assembler::notZero, POP_LABEL); - cmpl(cnt2, 1); - jcc(Assembler::equal, LENGTH_DIFF_LABEL); - // Check if the strings start at the same location. - cmpptr(str1, str2); + if (ae == StrIntrinsicNode::UU) { + // Divide length by 2 to get number of chars + shrl(cnt2, 1); + } + cmpl(cnt2, 1); jcc(Assembler::equal, LENGTH_DIFF_LABEL); - Address::ScaleFactor scale = Address::times_2; - int stride = 8; + // Check if the strings start at the same location and setup scale and stride + if (ae == StrIntrinsicNode::LL || ae == StrIntrinsicNode::UU) { + cmpptr(str1, str2); + jcc(Assembler::equal, LENGTH_DIFF_LABEL); + if (ae == StrIntrinsicNode::LL) { + scale = Address::times_1; + stride = 16; + } else { + scale = Address::times_2; + stride = 8; + } + } else { + scale1 = Address::times_1; + scale2 = Address::times_2; + stride = 8; + } if (UseAVX >= 2 && UseSSE42Intrinsics) { Label COMPARE_WIDE_VECTORS, VECTOR_NOT_EQUAL, COMPARE_WIDE_TAIL, COMPARE_SMALL_STR; Label COMPARE_WIDE_VECTORS_LOOP, COMPARE_16_CHARS, COMPARE_INDEX_CHAR; Label COMPARE_TAIL_LONG; int pcmpmask = 0x19; + if (ae == StrIntrinsicNode::LL) { + pcmpmask &= ~0x01; + } // Setup to compare 16-chars (32-bytes) vectors, // start from first character again because it has aligned address. - int stride2 = 16; - int adr_stride = stride << scale; + if (ae == StrIntrinsicNode::LL) { + stride2 = 32; + } else { + stride2 = 16; + } + if (ae == StrIntrinsicNode::LL || ae == StrIntrinsicNode::UU) { + adr_stride = stride << scale; + } else { + adr_stride1 = 8; //stride << scale1; + adr_stride2 = 16; //stride << scale2; + } assert(result == rax && cnt2 == rdx && cnt1 == rcx, "pcmpestri"); // rax and rdx are used by pcmpestri as elements counters @@ -6767,26 +7014,39 @@ // fast path : compare first 2 8-char vectors. bind(COMPARE_16_CHARS); - movdqu(vec1, Address(str1, 0)); + if (ae == StrIntrinsicNode::LL || ae == StrIntrinsicNode::UU) { + movdqu(vec1, Address(str1, 0)); + } else { + pmovzxbw(vec1, Address(str1, 0)); + } pcmpestri(vec1, Address(str2, 0), pcmpmask); jccb(Assembler::below, COMPARE_INDEX_CHAR); - movdqu(vec1, Address(str1, adr_stride)); - pcmpestri(vec1, Address(str2, adr_stride), pcmpmask); + if (ae == StrIntrinsicNode::LL || ae == StrIntrinsicNode::UU) { + movdqu(vec1, Address(str1, adr_stride)); + pcmpestri(vec1, Address(str2, adr_stride), pcmpmask); + } else { + pmovzxbw(vec1, Address(str1, adr_stride1)); + pcmpestri(vec1, Address(str2, adr_stride2), pcmpmask); + } jccb(Assembler::aboveEqual, COMPARE_WIDE_VECTORS); addl(cnt1, stride); // Compare the characters at index in cnt1 - bind(COMPARE_INDEX_CHAR); //cnt1 has the offset of the mismatching character - load_unsigned_short(result, Address(str1, cnt1, scale)); - load_unsigned_short(cnt2, Address(str2, cnt1, scale)); + bind(COMPARE_INDEX_CHAR); // cnt1 has the offset of the mismatching character + load_next_elements(result, cnt2, str1, str2, scale, scale1, scale2, cnt1, ae); subl(result, cnt2); jmp(POP_LABEL); // Setup the registers to start vector comparison loop bind(COMPARE_WIDE_VECTORS); - lea(str1, Address(str1, result, scale)); - lea(str2, Address(str2, result, scale)); + if (ae == StrIntrinsicNode::LL || ae == StrIntrinsicNode::UU) { + lea(str1, Address(str1, result, scale)); + lea(str2, Address(str2, result, scale)); + } else { + lea(str1, Address(str1, result, scale1)); + lea(str2, Address(str2, result, scale2)); + } subl(result, stride2); subl(cnt2, stride2); jccb(Assembler::zero, COMPARE_WIDE_TAIL); @@ -6794,8 +7054,13 @@ // In a loop, compare 16-chars (32-bytes) at once using (vpxor+vptest) bind(COMPARE_WIDE_VECTORS_LOOP); - vmovdqu(vec1, Address(str1, result, scale)); - vpxor(vec1, Address(str2, result, scale)); + if (ae == StrIntrinsicNode::LL || ae == StrIntrinsicNode::UU) { + vmovdqu(vec1, Address(str1, result, scale)); + vpxor(vec1, Address(str2, result, scale)); + } else { + vpmovzxbw(vec1, Address(str1, result, scale1)); + vpxor(vec1, Address(str2, result, scale2)); + } vptest(vec1, vec1); jccb(Assembler::notZero, VECTOR_NOT_EQUAL); addptr(result, stride2); @@ -6818,8 +7083,13 @@ bind(VECTOR_NOT_EQUAL); // clean upper bits of YMM registers vpxor(vec1, vec1); - lea(str1, Address(str1, result, scale)); - lea(str2, Address(str2, result, scale)); + if (ae == StrIntrinsicNode::LL || ae == StrIntrinsicNode::UU) { + lea(str1, Address(str1, result, scale)); + lea(str2, Address(str2, result, scale)); + } else { + lea(str1, Address(str1, result, scale1)); + lea(str2, Address(str2, result, scale2)); + } jmp(COMPARE_16_CHARS); // Compare tail chars, length between 1 to 15 chars @@ -6828,13 +7098,22 @@ cmpl(cnt2, stride); jccb(Assembler::less, COMPARE_SMALL_STR); - movdqu(vec1, Address(str1, 0)); + if (ae == StrIntrinsicNode::LL || ae == StrIntrinsicNode::UU) { + movdqu(vec1, Address(str1, 0)); + } else { + pmovzxbw(vec1, Address(str1, 0)); + } pcmpestri(vec1, Address(str2, 0), pcmpmask); jcc(Assembler::below, COMPARE_INDEX_CHAR); subptr(cnt2, stride); jccb(Assembler::zero, LENGTH_DIFF_LABEL); - lea(str1, Address(str1, result, scale)); - lea(str2, Address(str2, result, scale)); + if (ae == StrIntrinsicNode::LL || ae == StrIntrinsicNode::UU) { + lea(str1, Address(str1, result, scale)); + lea(str2, Address(str2, result, scale)); + } else { + lea(str1, Address(str1, result, scale1)); + lea(str2, Address(str2, result, scale2)); + } negptr(cnt2); jmpb(WHILE_HEAD_LABEL); @@ -6846,10 +7125,17 @@ // start from first character again because it has aligned address. movl(result, cnt2); andl(cnt2, ~(stride - 1)); // cnt2 holds the vector count + if (ae == StrIntrinsicNode::LL) { + pcmpmask &= ~0x01; + } jccb(Assembler::zero, COMPARE_TAIL); - - lea(str1, Address(str1, result, scale)); - lea(str2, Address(str2, result, scale)); + if (ae == StrIntrinsicNode::LL || ae == StrIntrinsicNode::UU) { + lea(str1, Address(str1, result, scale)); + lea(str2, Address(str2, result, scale)); + } else { + lea(str1, Address(str1, result, scale1)); + lea(str2, Address(str2, result, scale2)); + } negptr(result); // pcmpestri @@ -6865,8 +7151,13 @@ assert(result == rax && cnt2 == rdx && cnt1 == rcx, "pcmpestri"); bind(COMPARE_WIDE_VECTORS); - movdqu(vec1, Address(str1, result, scale)); - pcmpestri(vec1, Address(str2, result, scale), pcmpmask); + if (ae == StrIntrinsicNode::LL || ae == StrIntrinsicNode::UU) { + movdqu(vec1, Address(str1, result, scale)); + pcmpestri(vec1, Address(str2, result, scale), pcmpmask); + } else { + pmovzxbw(vec1, Address(str1, result, scale1)); + pcmpestri(vec1, Address(str2, result, scale2), pcmpmask); + } // After pcmpestri cnt1(rcx) contains mismatched element index jccb(Assembler::below, VECTOR_NOT_EQUAL); // CF==1 @@ -6881,15 +7172,19 @@ movl(cnt2, stride); movl(result, stride); negptr(result); - movdqu(vec1, Address(str1, result, scale)); - pcmpestri(vec1, Address(str2, result, scale), pcmpmask); + if (ae == StrIntrinsicNode::LL || ae == StrIntrinsicNode::UU) { + movdqu(vec1, Address(str1, result, scale)); + pcmpestri(vec1, Address(str2, result, scale), pcmpmask); + } else { + pmovzxbw(vec1, Address(str1, result, scale1)); + pcmpestri(vec1, Address(str2, result, scale2), pcmpmask); + } jccb(Assembler::aboveEqual, LENGTH_DIFF_LABEL); // Mismatched characters in the vectors bind(VECTOR_NOT_EQUAL); addptr(cnt1, result); - load_unsigned_short(result, Address(str1, cnt1, scale)); - load_unsigned_short(cnt2, Address(str2, cnt1, scale)); + load_next_elements(result, cnt2, str1, str2, scale, scale1, scale2, cnt1, ae); subl(result, cnt2); jmpb(POP_LABEL); @@ -6898,15 +7193,19 @@ // Fallthru to tail compare } // Shift str2 and str1 to the end of the arrays, negate min - lea(str1, Address(str1, cnt2, scale)); - lea(str2, Address(str2, cnt2, scale)); + if (ae == StrIntrinsicNode::LL || ae == StrIntrinsicNode::UU) { + lea(str1, Address(str1, cnt2, scale)); + lea(str2, Address(str2, cnt2, scale)); + } else { + lea(str1, Address(str1, cnt2, scale1)); + lea(str2, Address(str2, cnt2, scale2)); + } decrementl(cnt2); // first character was compared already negptr(cnt2); // Compare the rest of the elements bind(WHILE_HEAD_LABEL); - load_unsigned_short(result, Address(str1, cnt2, scale, 0)); - load_unsigned_short(cnt1, Address(str2, cnt2, scale, 0)); + load_next_elements(result, cnt1, str1, str2, scale, scale1, scale2, cnt2, ae); subl(result, cnt1); jccb(Assembler::notZero, POP_LABEL); increment(cnt2); @@ -6915,6 +7214,10 @@ // Strings are equal up to min length. Return the length difference. bind(LENGTH_DIFF_LABEL); pop(result); + if (ae == StrIntrinsicNode::UU) { + // Divide diff by 2 to get number of chars + sarl(result, 1); + } jmpb(DONE_LABEL); // Discard the stored length difference @@ -6923,23 +7226,164 @@ // That's it bind(DONE_LABEL); + if(ae == StrIntrinsicNode::UL) { + negl(result); + } } -// Compare char[] arrays aligned to 4 bytes or substrings. -void MacroAssembler::char_arrays_equals(bool is_array_equ, Register ary1, Register ary2, - Register limit, Register result, Register chr, - XMMRegister vec1, XMMRegister vec2) { +// Search for Non-ASCII character (Negative byte value) in a byte array, +// return true if it has any and false otherwise. +void MacroAssembler::has_negatives(Register ary1, Register len, + Register result, Register tmp1, + XMMRegister vec1, XMMRegister vec2) { + + // rsi: byte array + // rcx: len + // rax: result ShortBranchVerifier sbv(this); - Label TRUE_LABEL, FALSE_LABEL, DONE, COMPARE_VECTORS, COMPARE_CHAR; + assert_different_registers(ary1, len, result, tmp1); + assert_different_registers(vec1, vec2); + Label TRUE_LABEL, FALSE_LABEL, DONE, COMPARE_CHAR, COMPARE_VECTORS, COMPARE_BYTE; - int length_offset = arrayOopDesc::length_offset_in_bytes(); - int base_offset = arrayOopDesc::base_offset_in_bytes(T_CHAR); + // len == 0 + testl(len, len); + jcc(Assembler::zero, FALSE_LABEL); - // Check the input args - cmpptr(ary1, ary2); - jcc(Assembler::equal, TRUE_LABEL); + movl(result, len); // copy + + if (UseAVX >= 2) { + // With AVX2, use 32-byte vector compare + Label COMPARE_WIDE_VECTORS, COMPARE_TAIL; + + // Compare 32-byte vectors + andl(result, 0x0000001f); // tail count (in bytes) + andl(len, 0xffffffe0); // vector count (in bytes) + jccb(Assembler::zero, COMPARE_TAIL); + + lea(ary1, Address(ary1, len, Address::times_1)); + negptr(len); + + movl(tmp1, 0x80808080); // create mask to test for Unicode chars in vector + movdl(vec2, tmp1); + vpbroadcastd(vec2, vec2); + + bind(COMPARE_WIDE_VECTORS); + vmovdqu(vec1, Address(ary1, len, Address::times_1)); + vptest(vec1, vec2); + jccb(Assembler::notZero, TRUE_LABEL); + addptr(len, 32); + jcc(Assembler::notZero, COMPARE_WIDE_VECTORS); + + testl(result, result); + jccb(Assembler::zero, FALSE_LABEL); + + vmovdqu(vec1, Address(ary1, result, Address::times_1, -32)); + vptest(vec1, vec2); + jccb(Assembler::notZero, TRUE_LABEL); + jmpb(FALSE_LABEL); + + bind(COMPARE_TAIL); // len is zero + movl(len, result); + // Fallthru to tail compare + } else if (UseSSE42Intrinsics) { + // With SSE4.2, use double quad vector compare + Label COMPARE_WIDE_VECTORS, COMPARE_TAIL; + + // Compare 16-byte vectors + andl(result, 0x0000000f); // tail count (in bytes) + andl(len, 0xfffffff0); // vector count (in bytes) + jccb(Assembler::zero, COMPARE_TAIL); + + lea(ary1, Address(ary1, len, Address::times_1)); + negptr(len); + + movl(tmp1, 0x80808080); + movdl(vec2, tmp1); + pshufd(vec2, vec2, 0); + + bind(COMPARE_WIDE_VECTORS); + movdqu(vec1, Address(ary1, len, Address::times_1)); + ptest(vec1, vec2); + jccb(Assembler::notZero, TRUE_LABEL); + addptr(len, 16); + jcc(Assembler::notZero, COMPARE_WIDE_VECTORS); + + testl(result, result); + jccb(Assembler::zero, FALSE_LABEL); + + movdqu(vec1, Address(ary1, result, Address::times_1, -16)); + ptest(vec1, vec2); + jccb(Assembler::notZero, TRUE_LABEL); + jmpb(FALSE_LABEL); + + bind(COMPARE_TAIL); // len is zero + movl(len, result); + // Fallthru to tail compare + } + + // Compare 4-byte vectors + andl(len, 0xfffffffc); // vector count (in bytes) + jccb(Assembler::zero, COMPARE_CHAR); + + lea(ary1, Address(ary1, len, Address::times_1)); + negptr(len); + + bind(COMPARE_VECTORS); + movl(tmp1, Address(ary1, len, Address::times_1)); + andl(tmp1, 0x80808080); + jccb(Assembler::notZero, TRUE_LABEL); + addptr(len, 4); + jcc(Assembler::notZero, COMPARE_VECTORS); + + // Compare trailing char (final 2 bytes), if any + bind(COMPARE_CHAR); + testl(result, 0x2); // tail char + jccb(Assembler::zero, COMPARE_BYTE); + load_unsigned_short(tmp1, Address(ary1, 0)); + andl(tmp1, 0x00008080); + jccb(Assembler::notZero, TRUE_LABEL); + subptr(result, 2); + lea(ary1, Address(ary1, 2)); + + bind(COMPARE_BYTE); + testl(result, 0x1); // tail byte + jccb(Assembler::zero, FALSE_LABEL); + load_unsigned_byte(tmp1, Address(ary1, 0)); + andl(tmp1, 0x00000080); + jccb(Assembler::notEqual, TRUE_LABEL); + jmpb(FALSE_LABEL); + + bind(TRUE_LABEL); + movl(result, 1); // return true + jmpb(DONE); + + bind(FALSE_LABEL); + xorl(result, result); // return false + + // That's it + bind(DONE); + if (UseAVX >= 2) { + // clean upper bits of YMM registers + vpxor(vec1, vec1); + vpxor(vec2, vec2); + } +} + +// Compare char[] or byte[] arrays aligned to 4 bytes or substrings. +void MacroAssembler::arrays_equals(bool is_array_equ, Register ary1, Register ary2, + Register limit, Register result, Register chr, + XMMRegister vec1, XMMRegister vec2, bool is_char) { + ShortBranchVerifier sbv(this); + Label TRUE_LABEL, FALSE_LABEL, DONE, COMPARE_VECTORS, COMPARE_CHAR, COMPARE_BYTE; + + int length_offset = arrayOopDesc::length_offset_in_bytes(); + int base_offset = arrayOopDesc::base_offset_in_bytes(is_char ? T_CHAR : T_BYTE); if (is_array_equ) { + // Check the input args + cmpptr(ary1, ary2); + jcc(Assembler::equal, TRUE_LABEL); + // Need additional checks for arrays_equals. testptr(ary1, ary1); jcc(Assembler::zero, FALSE_LABEL); @@ -6962,7 +7406,10 @@ lea(ary2, Address(ary2, base_offset)); } - shll(limit, 1); // byte count != 0 + if (is_array_equ && is_char) { + // arrays_equals when used for char[]. + shll(limit, 1); // byte count != 0 + } movl(result, limit); // copy if (UseAVX >= 2) { @@ -6970,7 +7417,7 @@ Label COMPARE_WIDE_VECTORS, COMPARE_TAIL; // Compare 32-byte vectors - andl(result, 0x0000001e); // tail count (in bytes) + andl(result, 0x0000001f); // tail count (in bytes) andl(limit, 0xffffffe0); // vector count (in bytes) jccb(Assembler::zero, COMPARE_TAIL); @@ -7007,7 +7454,7 @@ Label COMPARE_WIDE_VECTORS, COMPARE_TAIL; // Compare 16-byte vectors - andl(result, 0x0000000e); // tail count (in bytes) + andl(result, 0x0000000f); // tail count (in bytes) andl(limit, 0xfffffff0); // vector count (in bytes) jccb(Assembler::zero, COMPARE_TAIL); @@ -7059,12 +7506,26 @@ // Compare trailing char (final 2 bytes), if any bind(COMPARE_CHAR); testl(result, 0x2); // tail char - jccb(Assembler::zero, TRUE_LABEL); + jccb(Assembler::zero, COMPARE_BYTE); load_unsigned_short(chr, Address(ary1, 0)); load_unsigned_short(limit, Address(ary2, 0)); cmpl(chr, limit); jccb(Assembler::notEqual, FALSE_LABEL); + if (is_array_equ && is_char) { + bind(COMPARE_BYTE); + } else { + lea(ary1, Address(ary1, 2)); + lea(ary2, Address(ary2, 2)); + + bind(COMPARE_BYTE); + testl(result, 0x1); // tail byte + jccb(Assembler::zero, TRUE_LABEL); + load_unsigned_byte(chr, Address(ary1, 0)); + load_unsigned_byte(limit, Address(ary2, 0)); + cmpl(chr, limit); + jccb(Assembler::notEqual, FALSE_LABEL); + } bind(TRUE_LABEL); movl(result, 1); // return true jmpb(DONE); @@ -7081,6 +7542,8 @@ } } +#endif + void MacroAssembler::generate_fill(BasicType t, bool aligned, Register to, Register value, Register count, Register rtmp, XMMRegister xtmp) { @@ -9085,6 +9548,179 @@ #undef BLOCK_COMMENT +// Compress char[] array to byte[]. +void MacroAssembler::char_array_compress(Register src, Register dst, Register len, + XMMRegister tmp1Reg, XMMRegister tmp2Reg, + XMMRegister tmp3Reg, XMMRegister tmp4Reg, + Register tmp5, Register result) { + Label copy_chars_loop, return_length, return_zero, done; + + // rsi: src + // rdi: dst + // rdx: len + // rcx: tmp5 + // rax: result + + // rsi holds start addr of source char[] to be compressed + // rdi holds start addr of destination byte[] + // rdx holds length + + assert(len != result, ""); + + // save length for return + push(len); + + if (UseSSE42Intrinsics) { + Label copy_32_loop, copy_16, copy_tail; + + movl(result, len); + movl(tmp5, 0xff00ff00); // create mask to test for Unicode chars in vectors + + // vectored compression + andl(len, 0xfffffff0); // vector count (in chars) + andl(result, 0x0000000f); // tail count (in chars) + testl(len, len); + jccb(Assembler::zero, copy_16); + + // compress 16 chars per iter + movdl(tmp1Reg, tmp5); + pshufd(tmp1Reg, tmp1Reg, 0); // store Unicode mask in tmp1Reg + pxor(tmp4Reg, tmp4Reg); + + lea(src, Address(src, len, Address::times_2)); + lea(dst, Address(dst, len, Address::times_1)); + negptr(len); + + bind(copy_32_loop); + movdqu(tmp2Reg, Address(src, len, Address::times_2)); // load 1st 8 characters + por(tmp4Reg, tmp2Reg); + movdqu(tmp3Reg, Address(src, len, Address::times_2, 16)); // load next 8 characters + por(tmp4Reg, tmp3Reg); + ptest(tmp4Reg, tmp1Reg); // check for Unicode chars in next vector + jcc(Assembler::notZero, return_zero); + packuswb(tmp2Reg, tmp3Reg); // only ASCII chars; compress each to 1 byte + movdqu(Address(dst, len, Address::times_1), tmp2Reg); + addptr(len, 16); + jcc(Assembler::notZero, copy_32_loop); + + // compress next vector of 8 chars (if any) + bind(copy_16); + movl(len, result); + andl(len, 0xfffffff8); // vector count (in chars) + andl(result, 0x00000007); // tail count (in chars) + testl(len, len); + jccb(Assembler::zero, copy_tail); + + movdl(tmp1Reg, tmp5); + pshufd(tmp1Reg, tmp1Reg, 0); // store Unicode mask in tmp1Reg + pxor(tmp3Reg, tmp3Reg); + + movdqu(tmp2Reg, Address(src, 0)); + ptest(tmp2Reg, tmp1Reg); // check for Unicode chars in vector + jccb(Assembler::notZero, return_zero); + packuswb(tmp2Reg, tmp3Reg); // only LATIN1 chars; compress each to 1 byte + movq(Address(dst, 0), tmp2Reg); + addptr(src, 16); + addptr(dst, 8); + + bind(copy_tail); + movl(len, result); + } + // compress 1 char per iter + testl(len, len); + jccb(Assembler::zero, return_length); + lea(src, Address(src, len, Address::times_2)); + lea(dst, Address(dst, len, Address::times_1)); + negptr(len); + + bind(copy_chars_loop); + load_unsigned_short(result, Address(src, len, Address::times_2)); + testl(result, 0xff00); // check if Unicode char + jccb(Assembler::notZero, return_zero); + movb(Address(dst, len, Address::times_1), result); // ASCII char; compress to 1 byte + increment(len); + jcc(Assembler::notZero, copy_chars_loop); + + // if compression succeeded, return length + bind(return_length); + pop(result); + jmpb(done); + + // if compression failed, return 0 + bind(return_zero); + xorl(result, result); + addptr(rsp, wordSize); + + bind(done); +} + +// Inflate byte[] array to char[]. +void MacroAssembler::byte_array_inflate(Register src, Register dst, Register len, + XMMRegister tmp1, Register tmp2) { + Label copy_chars_loop, done; + + // rsi: src + // rdi: dst + // rdx: len + // rcx: tmp2 + + // rsi holds start addr of source byte[] to be inflated + // rdi holds start addr of destination char[] + // rdx holds length + assert_different_registers(src, dst, len, tmp2); + + if (UseSSE42Intrinsics) { + Label copy_8_loop, copy_bytes, copy_tail; + + movl(tmp2, len); + andl(tmp2, 0x00000007); // tail count (in chars) + andl(len, 0xfffffff8); // vector count (in chars) + jccb(Assembler::zero, copy_tail); + + // vectored inflation + lea(src, Address(src, len, Address::times_1)); + lea(dst, Address(dst, len, Address::times_2)); + negptr(len); + + // inflate 8 chars per iter + bind(copy_8_loop); + pmovzxbw(tmp1, Address(src, len, Address::times_1)); // unpack to 8 words + movdqu(Address(dst, len, Address::times_2), tmp1); + addptr(len, 8); + jcc(Assembler::notZero, copy_8_loop); + + bind(copy_tail); + movl(len, tmp2); + + cmpl(len, 4); + jccb(Assembler::less, copy_bytes); + + movdl(tmp1, Address(src, 0)); // load 4 byte chars + pmovzxbw(tmp1, tmp1); + movq(Address(dst, 0), tmp1); + subptr(len, 4); + addptr(src, 4); + addptr(dst, 8); + + bind(copy_bytes); + } + testl(len, len); + jccb(Assembler::zero, done); + lea(src, Address(src, len, Address::times_1)); + lea(dst, Address(dst, len, Address::times_2)); + negptr(len); + + // inflate 1 char per iter + bind(copy_chars_loop); + load_unsigned_byte(tmp2, Address(src, len, Address::times_1)); // load byte char + movw(Address(dst, len, Address::times_2), tmp2); // inflate byte char to word + increment(len); + jcc(Assembler::notZero, copy_chars_loop); + + bind(done); +} + + Assembler::Condition MacroAssembler::negate_condition(Assembler::Condition cond) { switch (cond) { // Note some conditions are synonyms for others --- old/src/cpu/x86/vm/macroAssembler_x86.hpp 2015-10-30 19:14:45.568475452 +0100 +++ new/src/cpu/x86/vm/macroAssembler_x86.hpp 2015-10-30 19:14:45.508475450 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -29,7 +29,6 @@ #include "utilities/macros.hpp" #include "runtime/rtmLocking.hpp" - // MacroAssembler extends Assembler by frequently used macros. // // Instructions for which a 'better' code sequence exists depending @@ -1205,32 +1204,50 @@ // clear memory of size 'cnt' qwords, starting at 'base'. void clear_mem(Register base, Register cnt, Register rtmp); +#ifdef COMPILER2 + void string_indexof_char(Register str1, Register cnt1, Register ch, Register result, + XMMRegister vec1, XMMRegister vec2, XMMRegister vec3, Register tmp); + // IndexOf strings. // Small strings are loaded through stack if they cross page boundary. void string_indexof(Register str1, Register str2, Register cnt1, Register cnt2, int int_cnt2, Register result, - XMMRegister vec, Register tmp); + XMMRegister vec, Register tmp, + int ae); // IndexOf for constant substrings with size >= 8 elements // which don't need to be loaded through stack. void string_indexofC8(Register str1, Register str2, Register cnt1, Register cnt2, int int_cnt2, Register result, - XMMRegister vec, Register tmp); + XMMRegister vec, Register tmp, + int ae); // Smallest code: we don't need to load through stack, // check string tail. + // helper function for string_compare + void load_next_elements(Register elem1, Register elem2, Register str1, Register str2, + Address::ScaleFactor scale, Address::ScaleFactor scale1, + Address::ScaleFactor scale2, Register index, int ae); // Compare strings. void string_compare(Register str1, Register str2, Register cnt1, Register cnt2, Register result, - XMMRegister vec1); + XMMRegister vec1, int ae); - // Compare char[] arrays. - void char_arrays_equals(bool is_array_equ, Register ary1, Register ary2, - Register limit, Register result, Register chr, - XMMRegister vec1, XMMRegister vec2); + // Search for Non-ASCII character (Negative byte value) in a byte array, + // return true if it has any and false otherwise. + void has_negatives(Register ary1, Register len, + Register result, Register tmp1, + XMMRegister vec1, XMMRegister vec2); + + // Compare char[] or byte[] arrays. + void arrays_equals(bool is_array_equ, Register ary1, Register ary2, + Register limit, Register result, Register chr, + XMMRegister vec1, XMMRegister vec2, bool is_char); + +#endif // Fill primitive arrays void generate_fill(BasicType t, bool aligned, @@ -1325,6 +1342,15 @@ void fold_8bit_crc32(Register crc, Register table, Register tmp); void fold_8bit_crc32(XMMRegister crc, Register table, XMMRegister xtmp, Register tmp); + // Compress char[] array to byte[]. + void char_array_compress(Register src, Register dst, Register len, + XMMRegister tmp1, XMMRegister tmp2, XMMRegister tmp3, + XMMRegister tmp4, Register tmp5, Register result); + + // Inflate byte[] array to char[]. + void byte_array_inflate(Register src, Register dst, Register len, + XMMRegister tmp1, Register tmp2); + #undef VIRTUAL }; --- old/src/cpu/x86/vm/x86_32.ad 2015-10-30 19:14:45.832475460 +0100 +++ new/src/cpu/x86/vm/x86_32.ad 2015-10-30 19:14:45.756475458 +0100 @@ -11470,16 +11470,62 @@ ins_pipe( pipe_slow ); %} -instruct string_compare(eDIRegP str1, eCXRegI cnt1, eSIRegP str2, eDXRegI cnt2, - eAXRegI result, regD tmp1, eFlagsReg cr) %{ +instruct string_compareL(eDIRegP str1, eCXRegI cnt1, eSIRegP str2, eDXRegI cnt2, + eAXRegI result, regD tmp1, eFlagsReg cr) %{ + predicate(((StrCompNode*)n)->encoding() == StrIntrinsicNode::LL); match(Set result (StrComp (Binary str1 cnt1) (Binary str2 cnt2))); effect(TEMP tmp1, USE_KILL str1, USE_KILL str2, USE_KILL cnt1, USE_KILL cnt2, KILL cr); - format %{ "String Compare $str1,$cnt1,$str2,$cnt2 -> $result // KILL $tmp1" %} + format %{ "String Compare byte[] $str1,$cnt1,$str2,$cnt2 -> $result // KILL $tmp1" %} ins_encode %{ __ string_compare($str1$$Register, $str2$$Register, $cnt1$$Register, $cnt2$$Register, $result$$Register, - $tmp1$$XMMRegister); + $tmp1$$XMMRegister, StrIntrinsicNode::LL); + %} + ins_pipe( pipe_slow ); +%} + +instruct string_compareU(eDIRegP str1, eCXRegI cnt1, eSIRegP str2, eDXRegI cnt2, + eAXRegI result, regD tmp1, eFlagsReg cr) %{ + predicate(((StrCompNode*)n)->encoding() == StrIntrinsicNode::UU); + match(Set result (StrComp (Binary str1 cnt1) (Binary str2 cnt2))); + effect(TEMP tmp1, USE_KILL str1, USE_KILL str2, USE_KILL cnt1, USE_KILL cnt2, KILL cr); + + format %{ "String Compare char[] $str1,$cnt1,$str2,$cnt2 -> $result // KILL $tmp1" %} + ins_encode %{ + __ string_compare($str1$$Register, $str2$$Register, + $cnt1$$Register, $cnt2$$Register, $result$$Register, + $tmp1$$XMMRegister, StrIntrinsicNode::UU); + %} + ins_pipe( pipe_slow ); +%} + +instruct string_compareLU(eDIRegP str1, eCXRegI cnt1, eSIRegP str2, eDXRegI cnt2, + eAXRegI result, regD tmp1, eFlagsReg cr) %{ + predicate(((StrCompNode*)n)->encoding() == StrIntrinsicNode::LU); + match(Set result (StrComp (Binary str1 cnt1) (Binary str2 cnt2))); + effect(TEMP tmp1, USE_KILL str1, USE_KILL str2, USE_KILL cnt1, USE_KILL cnt2, KILL cr); + + format %{ "String Compare byte[] $str1,$cnt1,$str2,$cnt2 -> $result // KILL $tmp1" %} + ins_encode %{ + __ string_compare($str1$$Register, $str2$$Register, + $cnt1$$Register, $cnt2$$Register, $result$$Register, + $tmp1$$XMMRegister, StrIntrinsicNode::LU); + %} + ins_pipe( pipe_slow ); +%} + +instruct string_compareUL(eSIRegP str1, eDXRegI cnt1, eDIRegP str2, eCXRegI cnt2, + eAXRegI result, regD tmp1, eFlagsReg cr) %{ + predicate(((StrCompNode*)n)->encoding() == StrIntrinsicNode::UL); + match(Set result (StrComp (Binary str1 cnt1) (Binary str2 cnt2))); + effect(TEMP tmp1, USE_KILL str1, USE_KILL str2, USE_KILL cnt1, USE_KILL cnt2, KILL cr); + + format %{ "String Compare byte[] $str1,$cnt1,$str2,$cnt2 -> $result // KILL $tmp1" %} + ins_encode %{ + __ string_compare($str2$$Register, $str1$$Register, + $cnt2$$Register, $cnt1$$Register, $result$$Register, + $tmp1$$XMMRegister, StrIntrinsicNode::UL); %} ins_pipe( pipe_slow ); %} @@ -11492,21 +11538,50 @@ format %{ "String Equals $str1,$str2,$cnt -> $result // KILL $tmp1, $tmp2, $tmp3" %} ins_encode %{ - __ char_arrays_equals(false, $str1$$Register, $str2$$Register, - $cnt$$Register, $result$$Register, $tmp3$$Register, - $tmp1$$XMMRegister, $tmp2$$XMMRegister); + __ arrays_equals(false, $str1$$Register, $str2$$Register, + $cnt$$Register, $result$$Register, $tmp3$$Register, + $tmp1$$XMMRegister, $tmp2$$XMMRegister, false /* char */); + %} + + ins_pipe( pipe_slow ); +%} + +// fast search of substring with known size. +instruct string_indexof_conL(eDIRegP str1, eDXRegI cnt1, eSIRegP str2, immI int_cnt2, + eBXRegI result, regD vec, eAXRegI cnt2, eCXRegI tmp, eFlagsReg cr) %{ + predicate(UseSSE42Intrinsics && (((StrIndexOfNode*)n)->encoding() == StrIntrinsicNode::LL)); + match(Set result (StrIndexOf (Binary str1 cnt1) (Binary str2 int_cnt2))); + effect(TEMP vec, USE_KILL str1, USE_KILL str2, USE_KILL cnt1, KILL cnt2, KILL tmp, KILL cr); + + format %{ "String IndexOf byte[] $str1,$cnt1,$str2,$int_cnt2 -> $result // KILL $vec, $cnt1, $cnt2, $tmp" %} + ins_encode %{ + int icnt2 = (int)$int_cnt2$$constant; + if (icnt2 >= 16) { + // IndexOf for constant substrings with size >= 16 elements + // which don't need to be loaded through stack. + __ string_indexofC8($str1$$Register, $str2$$Register, + $cnt1$$Register, $cnt2$$Register, + icnt2, $result$$Register, + $vec$$XMMRegister, $tmp$$Register, StrIntrinsicNode::LL); + } else { + // Small strings are loaded through stack if they cross page boundary. + __ string_indexof($str1$$Register, $str2$$Register, + $cnt1$$Register, $cnt2$$Register, + icnt2, $result$$Register, + $vec$$XMMRegister, $tmp$$Register, StrIntrinsicNode::LL); + } %} ins_pipe( pipe_slow ); %} // fast search of substring with known size. -instruct string_indexof_con(eDIRegP str1, eDXRegI cnt1, eSIRegP str2, immI int_cnt2, - eBXRegI result, regD vec, eAXRegI cnt2, eCXRegI tmp, eFlagsReg cr) %{ - predicate(UseSSE42Intrinsics); +instruct string_indexof_conU(eDIRegP str1, eDXRegI cnt1, eSIRegP str2, immI int_cnt2, + eBXRegI result, regD vec, eAXRegI cnt2, eCXRegI tmp, eFlagsReg cr) %{ + predicate(UseSSE42Intrinsics && (((StrIndexOfNode*)n)->encoding() == StrIntrinsicNode::UU)); match(Set result (StrIndexOf (Binary str1 cnt1) (Binary str2 int_cnt2))); effect(TEMP vec, USE_KILL str1, USE_KILL str2, USE_KILL cnt1, KILL cnt2, KILL tmp, KILL cr); - format %{ "String IndexOf $str1,$cnt1,$str2,$int_cnt2 -> $result // KILL $vec, $cnt1, $cnt2, $tmp" %} + format %{ "String IndexOf char[] $str1,$cnt1,$str2,$int_cnt2 -> $result // KILL $vec, $cnt1, $cnt2, $tmp" %} ins_encode %{ int icnt2 = (int)$int_cnt2$$constant; if (icnt2 >= 8) { @@ -11515,47 +11590,182 @@ __ string_indexofC8($str1$$Register, $str2$$Register, $cnt1$$Register, $cnt2$$Register, icnt2, $result$$Register, - $vec$$XMMRegister, $tmp$$Register); + $vec$$XMMRegister, $tmp$$Register, StrIntrinsicNode::UU); } else { // Small strings are loaded through stack if they cross page boundary. __ string_indexof($str1$$Register, $str2$$Register, $cnt1$$Register, $cnt2$$Register, icnt2, $result$$Register, - $vec$$XMMRegister, $tmp$$Register); + $vec$$XMMRegister, $tmp$$Register, StrIntrinsicNode::UU); } %} ins_pipe( pipe_slow ); %} -instruct string_indexof(eDIRegP str1, eDXRegI cnt1, eSIRegP str2, eAXRegI cnt2, - eBXRegI result, regD vec, eCXRegI tmp, eFlagsReg cr) %{ - predicate(UseSSE42Intrinsics); +// fast search of substring with known size. +instruct string_indexof_conUL(eDIRegP str1, eDXRegI cnt1, eSIRegP str2, immI int_cnt2, + eBXRegI result, regD vec, eAXRegI cnt2, eCXRegI tmp, eFlagsReg cr) %{ + predicate(UseSSE42Intrinsics && (((StrIndexOfNode*)n)->encoding() == StrIntrinsicNode::UL)); + match(Set result (StrIndexOf (Binary str1 cnt1) (Binary str2 int_cnt2))); + effect(TEMP vec, USE_KILL str1, USE_KILL str2, USE_KILL cnt1, KILL cnt2, KILL tmp, KILL cr); + + format %{ "String IndexOf char[] $str1,$cnt1,$str2,$int_cnt2 -> $result // KILL $vec, $cnt1, $cnt2, $tmp" %} + ins_encode %{ + int icnt2 = (int)$int_cnt2$$constant; + if (icnt2 >= 8) { + // IndexOf for constant substrings with size >= 8 elements + // which don't need to be loaded through stack. + __ string_indexofC8($str1$$Register, $str2$$Register, + $cnt1$$Register, $cnt2$$Register, + icnt2, $result$$Register, + $vec$$XMMRegister, $tmp$$Register, StrIntrinsicNode::UL); + } else { + // Small strings are loaded through stack if they cross page boundary. + __ string_indexof($str1$$Register, $str2$$Register, + $cnt1$$Register, $cnt2$$Register, + icnt2, $result$$Register, + $vec$$XMMRegister, $tmp$$Register, StrIntrinsicNode::UL); + } + %} + ins_pipe( pipe_slow ); +%} + +instruct string_indexofL(eDIRegP str1, eDXRegI cnt1, eSIRegP str2, eAXRegI cnt2, + eBXRegI result, regD vec, eCXRegI tmp, eFlagsReg cr) %{ + predicate(UseSSE42Intrinsics && (((StrIndexOfNode*)n)->encoding() == StrIntrinsicNode::LL)); + match(Set result (StrIndexOf (Binary str1 cnt1) (Binary str2 cnt2))); + effect(TEMP vec, USE_KILL str1, USE_KILL str2, USE_KILL cnt1, USE_KILL cnt2, KILL tmp, KILL cr); + + format %{ "String IndexOf byte[] $str1,$cnt1,$str2,$cnt2 -> $result // KILL all" %} + ins_encode %{ + __ string_indexof($str1$$Register, $str2$$Register, + $cnt1$$Register, $cnt2$$Register, + (-1), $result$$Register, + $vec$$XMMRegister, $tmp$$Register, StrIntrinsicNode::LL); + %} + ins_pipe( pipe_slow ); +%} + +instruct string_indexofU(eDIRegP str1, eDXRegI cnt1, eSIRegP str2, eAXRegI cnt2, + eBXRegI result, regD vec, eCXRegI tmp, eFlagsReg cr) %{ + predicate(UseSSE42Intrinsics && (((StrIndexOfNode*)n)->encoding() == StrIntrinsicNode::UU)); + match(Set result (StrIndexOf (Binary str1 cnt1) (Binary str2 cnt2))); + effect(TEMP vec, USE_KILL str1, USE_KILL str2, USE_KILL cnt1, USE_KILL cnt2, KILL tmp, KILL cr); + + format %{ "String IndexOf char[] $str1,$cnt1,$str2,$cnt2 -> $result // KILL all" %} + ins_encode %{ + __ string_indexof($str1$$Register, $str2$$Register, + $cnt1$$Register, $cnt2$$Register, + (-1), $result$$Register, + $vec$$XMMRegister, $tmp$$Register, StrIntrinsicNode::UU); + %} + ins_pipe( pipe_slow ); +%} + +instruct string_indexofUL(eDIRegP str1, eDXRegI cnt1, eSIRegP str2, eAXRegI cnt2, + eBXRegI result, regD vec, eCXRegI tmp, eFlagsReg cr) %{ + predicate(UseSSE42Intrinsics && (((StrIndexOfNode*)n)->encoding() == StrIntrinsicNode::UL)); match(Set result (StrIndexOf (Binary str1 cnt1) (Binary str2 cnt2))); effect(TEMP vec, USE_KILL str1, USE_KILL str2, USE_KILL cnt1, USE_KILL cnt2, KILL tmp, KILL cr); - format %{ "String IndexOf $str1,$cnt1,$str2,$cnt2 -> $result // KILL all" %} + format %{ "String IndexOf char[] $str1,$cnt1,$str2,$cnt2 -> $result // KILL all" %} ins_encode %{ __ string_indexof($str1$$Register, $str2$$Register, $cnt1$$Register, $cnt2$$Register, (-1), $result$$Register, - $vec$$XMMRegister, $tmp$$Register); + $vec$$XMMRegister, $tmp$$Register, StrIntrinsicNode::UL); + %} + ins_pipe( pipe_slow ); +%} + +instruct string_indexofU_char(eDIRegP str1, eDXRegI cnt1, eAXRegI ch, + eBXRegI result, regD vec1, regD vec2, regD vec3, eCXRegI tmp, eFlagsReg cr) %{ + predicate(UseSSE42Intrinsics); + match(Set result (StrIndexOfChar (Binary str1 cnt1) ch)); + effect(TEMP vec1, TEMP vec2, TEMP vec3, USE_KILL str1, USE_KILL cnt1, USE_KILL ch, TEMP tmp, KILL cr); + format %{ "String IndexOf char[] $str1,$cnt1,$ch -> $result // KILL all" %} + ins_encode %{ + __ string_indexof_char($str1$$Register, $cnt1$$Register, $ch$$Register, $result$$Register, + $vec1$$XMMRegister, $vec2$$XMMRegister, $vec3$$XMMRegister, $tmp$$Register); %} ins_pipe( pipe_slow ); %} // fast array equals -instruct array_equals(eDIRegP ary1, eSIRegP ary2, eAXRegI result, - regD tmp1, regD tmp2, eCXRegI tmp3, eBXRegI tmp4, eFlagsReg cr) +instruct array_equalsB(eDIRegP ary1, eSIRegP ary2, eAXRegI result, + regD tmp1, regD tmp2, eCXRegI tmp3, eBXRegI tmp4, eFlagsReg cr) +%{ + predicate(((AryEqNode*)n)->encoding() == StrIntrinsicNode::LL); + match(Set result (AryEq ary1 ary2)); + effect(TEMP tmp1, TEMP tmp2, USE_KILL ary1, USE_KILL ary2, KILL tmp3, KILL tmp4, KILL cr); + //ins_cost(300); + + format %{ "Array Equals byte[] $ary1,$ary2 -> $result // KILL $tmp1, $tmp2, $tmp3, $tmp4" %} + ins_encode %{ + __ arrays_equals(true, $ary1$$Register, $ary2$$Register, + $tmp3$$Register, $result$$Register, $tmp4$$Register, + $tmp1$$XMMRegister, $tmp2$$XMMRegister, false /* char */); + %} + ins_pipe( pipe_slow ); +%} + +instruct array_equalsC(eDIRegP ary1, eSIRegP ary2, eAXRegI result, + regD tmp1, regD tmp2, eCXRegI tmp3, eBXRegI tmp4, eFlagsReg cr) %{ + predicate(((AryEqNode*)n)->encoding() == StrIntrinsicNode::UU); match(Set result (AryEq ary1 ary2)); effect(TEMP tmp1, TEMP tmp2, USE_KILL ary1, USE_KILL ary2, KILL tmp3, KILL tmp4, KILL cr); //ins_cost(300); - format %{ "Array Equals $ary1,$ary2 -> $result // KILL $tmp1, $tmp2, $tmp3, $tmp4" %} + format %{ "Array Equals char[] $ary1,$ary2 -> $result // KILL $tmp1, $tmp2, $tmp3, $tmp4" %} + ins_encode %{ + __ arrays_equals(true, $ary1$$Register, $ary2$$Register, + $tmp3$$Register, $result$$Register, $tmp4$$Register, + $tmp1$$XMMRegister, $tmp2$$XMMRegister, true /* char */); + %} + ins_pipe( pipe_slow ); +%} + +instruct has_negatives(eSIRegP ary1, eCXRegI len, eAXRegI result, + regD tmp1, regD tmp2, eBXRegI tmp3, eFlagsReg cr) +%{ + match(Set result (HasNegatives ary1 len)); + effect(TEMP tmp1, TEMP tmp2, USE_KILL ary1, USE_KILL len, KILL tmp3, KILL cr); + + format %{ "has negatives byte[] $ary1,$len -> $result // KILL $tmp1, $tmp2, $tmp3" %} + ins_encode %{ + __ has_negatives($ary1$$Register, $len$$Register, + $result$$Register, $tmp3$$Register, + $tmp1$$XMMRegister, $tmp2$$XMMRegister); + %} + ins_pipe( pipe_slow ); +%} + +// fast char[] to byte[] compression +instruct string_compress(eSIRegP src, eDIRegP dst, eDXRegI len, regD tmp1, regD tmp2, regD tmp3, regD tmp4, + eCXRegI tmp5, eAXRegI result, eFlagsReg cr) %{ + match(Set result (StrCompressedCopy src (Binary dst len))); + effect(TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4, USE_KILL src, USE_KILL dst, USE_KILL len, KILL tmp5, KILL cr); + + format %{ "String Compress $src,$dst -> $result // KILL RAX, RCX, RDX" %} + ins_encode %{ + __ char_array_compress($src$$Register, $dst$$Register, $len$$Register, + $tmp1$$XMMRegister, $tmp2$$XMMRegister, $tmp3$$XMMRegister, + $tmp4$$XMMRegister, $tmp5$$Register, $result$$Register); + %} + ins_pipe( pipe_slow ); +%} + +// fast byte[] to char[] inflation +instruct string_inflate(Universe dummy, eSIRegP src, eDIRegP dst, eDXRegI len, + regD tmp1, eCXRegI tmp2, eFlagsReg cr) %{ + match(Set dummy (StrInflatedCopy src (Binary dst len))); + effect(TEMP tmp1, TEMP tmp2, USE_KILL src, USE_KILL dst, USE_KILL len, KILL cr); + + format %{ "String Inflate $src,$dst // KILL $tmp1, $tmp2" %} ins_encode %{ - __ char_arrays_equals(true, $ary1$$Register, $ary2$$Register, - $tmp3$$Register, $result$$Register, $tmp4$$Register, - $tmp1$$XMMRegister, $tmp2$$XMMRegister); + __ byte_array_inflate($src$$Register, $dst$$Register, $len$$Register, + $tmp1$$XMMRegister, $tmp2$$Register); %} ins_pipe( pipe_slow ); %} --- old/src/cpu/x86/vm/x86_64.ad 2015-10-30 19:14:46.128475470 +0100 +++ new/src/cpu/x86/vm/x86_64.ad 2015-10-30 19:14:46.064475468 +0100 @@ -10462,30 +10462,108 @@ ins_pipe( pipe_slow ); %} -instruct string_compare(rdi_RegP str1, rcx_RegI cnt1, rsi_RegP str2, rdx_RegI cnt2, - rax_RegI result, regD tmp1, rFlagsReg cr) +instruct string_compareL(rdi_RegP str1, rcx_RegI cnt1, rsi_RegP str2, rdx_RegI cnt2, + rax_RegI result, regD tmp1, rFlagsReg cr) %{ + predicate(((StrCompNode*)n)->encoding() == StrIntrinsicNode::LL); match(Set result (StrComp (Binary str1 cnt1) (Binary str2 cnt2))); effect(TEMP tmp1, USE_KILL str1, USE_KILL str2, USE_KILL cnt1, USE_KILL cnt2, KILL cr); - format %{ "String Compare $str1,$cnt1,$str2,$cnt2 -> $result // KILL $tmp1" %} + format %{ "String Compare byte[] $str1,$cnt1,$str2,$cnt2 -> $result // KILL $tmp1" %} ins_encode %{ __ string_compare($str1$$Register, $str2$$Register, $cnt1$$Register, $cnt2$$Register, $result$$Register, - $tmp1$$XMMRegister); + $tmp1$$XMMRegister, StrIntrinsicNode::LL); + %} + ins_pipe( pipe_slow ); +%} + +instruct string_compareU(rdi_RegP str1, rcx_RegI cnt1, rsi_RegP str2, rdx_RegI cnt2, + rax_RegI result, regD tmp1, rFlagsReg cr) +%{ + predicate(((StrCompNode*)n)->encoding() == StrIntrinsicNode::UU); + match(Set result (StrComp (Binary str1 cnt1) (Binary str2 cnt2))); + effect(TEMP tmp1, USE_KILL str1, USE_KILL str2, USE_KILL cnt1, USE_KILL cnt2, KILL cr); + + format %{ "String Compare char[] $str1,$cnt1,$str2,$cnt2 -> $result // KILL $tmp1" %} + ins_encode %{ + __ string_compare($str1$$Register, $str2$$Register, + $cnt1$$Register, $cnt2$$Register, $result$$Register, + $tmp1$$XMMRegister, StrIntrinsicNode::UU); + %} + ins_pipe( pipe_slow ); +%} + +instruct string_compareLU(rdi_RegP str1, rcx_RegI cnt1, rsi_RegP str2, rdx_RegI cnt2, + rax_RegI result, regD tmp1, rFlagsReg cr) +%{ + predicate(((StrCompNode*)n)->encoding() == StrIntrinsicNode::LU); + match(Set result (StrComp (Binary str1 cnt1) (Binary str2 cnt2))); + effect(TEMP tmp1, USE_KILL str1, USE_KILL str2, USE_KILL cnt1, USE_KILL cnt2, KILL cr); + + format %{ "String Compare byte[] $str1,$cnt1,$str2,$cnt2 -> $result // KILL $tmp1" %} + ins_encode %{ + __ string_compare($str1$$Register, $str2$$Register, + $cnt1$$Register, $cnt2$$Register, $result$$Register, + $tmp1$$XMMRegister, StrIntrinsicNode::LU); + %} + ins_pipe( pipe_slow ); +%} + +instruct string_compareUL(rsi_RegP str1, rdx_RegI cnt1, rdi_RegP str2, rcx_RegI cnt2, + rax_RegI result, regD tmp1, rFlagsReg cr) +%{ + predicate(((StrCompNode*)n)->encoding() == StrIntrinsicNode::UL); + match(Set result (StrComp (Binary str1 cnt1) (Binary str2 cnt2))); + effect(TEMP tmp1, USE_KILL str1, USE_KILL str2, USE_KILL cnt1, USE_KILL cnt2, KILL cr); + + format %{ "String Compare byte[] $str1,$cnt1,$str2,$cnt2 -> $result // KILL $tmp1" %} + ins_encode %{ + __ string_compare($str2$$Register, $str1$$Register, + $cnt2$$Register, $cnt1$$Register, $result$$Register, + $tmp1$$XMMRegister, StrIntrinsicNode::UL); %} ins_pipe( pipe_slow ); %} // fast search of substring with known size. -instruct string_indexof_con(rdi_RegP str1, rdx_RegI cnt1, rsi_RegP str2, immI int_cnt2, - rbx_RegI result, regD vec, rax_RegI cnt2, rcx_RegI tmp, rFlagsReg cr) +instruct string_indexof_conL(rdi_RegP str1, rdx_RegI cnt1, rsi_RegP str2, immI int_cnt2, + rbx_RegI result, regD vec, rax_RegI cnt2, rcx_RegI tmp, rFlagsReg cr) %{ - predicate(UseSSE42Intrinsics); + predicate(UseSSE42Intrinsics && (((StrIndexOfNode*)n)->encoding() == StrIntrinsicNode::LL)); + match(Set result (StrIndexOf (Binary str1 cnt1) (Binary str2 int_cnt2))); + effect(TEMP vec, USE_KILL str1, USE_KILL str2, USE_KILL cnt1, KILL cnt2, KILL tmp, KILL cr); + + format %{ "String IndexOf byte[] $str1,$cnt1,$str2,$int_cnt2 -> $result // KILL $vec, $cnt1, $cnt2, $tmp" %} + ins_encode %{ + int icnt2 = (int)$int_cnt2$$constant; + if (icnt2 >= 16) { + // IndexOf for constant substrings with size >= 16 elements + // which don't need to be loaded through stack. + __ string_indexofC8($str1$$Register, $str2$$Register, + $cnt1$$Register, $cnt2$$Register, + icnt2, $result$$Register, + $vec$$XMMRegister, $tmp$$Register, StrIntrinsicNode::LL); + } else { + // Small strings are loaded through stack if they cross page boundary. + __ string_indexof($str1$$Register, $str2$$Register, + $cnt1$$Register, $cnt2$$Register, + icnt2, $result$$Register, + $vec$$XMMRegister, $tmp$$Register, StrIntrinsicNode::LL); + } + %} + ins_pipe( pipe_slow ); +%} + +// fast search of substring with known size. +instruct string_indexof_conU(rdi_RegP str1, rdx_RegI cnt1, rsi_RegP str2, immI int_cnt2, + rbx_RegI result, regD vec, rax_RegI cnt2, rcx_RegI tmp, rFlagsReg cr) +%{ + predicate(UseSSE42Intrinsics && (((StrIndexOfNode*)n)->encoding() == StrIntrinsicNode::UU)); match(Set result (StrIndexOf (Binary str1 cnt1) (Binary str2 int_cnt2))); effect(TEMP vec, USE_KILL str1, USE_KILL str2, USE_KILL cnt1, KILL cnt2, KILL tmp, KILL cr); - format %{ "String IndexOf $str1,$cnt1,$str2,$int_cnt2 -> $result // KILL $vec, $cnt1, $cnt2, $tmp" %} + format %{ "String IndexOf char[] $str1,$cnt1,$str2,$int_cnt2 -> $result // KILL $vec, $cnt1, $cnt2, $tmp" %} ins_encode %{ int icnt2 = (int)$int_cnt2$$constant; if (icnt2 >= 8) { @@ -10494,31 +10572,108 @@ __ string_indexofC8($str1$$Register, $str2$$Register, $cnt1$$Register, $cnt2$$Register, icnt2, $result$$Register, - $vec$$XMMRegister, $tmp$$Register); + $vec$$XMMRegister, $tmp$$Register, StrIntrinsicNode::UU); } else { // Small strings are loaded through stack if they cross page boundary. __ string_indexof($str1$$Register, $str2$$Register, $cnt1$$Register, $cnt2$$Register, icnt2, $result$$Register, - $vec$$XMMRegister, $tmp$$Register); + $vec$$XMMRegister, $tmp$$Register, StrIntrinsicNode::UU); } %} ins_pipe( pipe_slow ); %} -instruct string_indexof(rdi_RegP str1, rdx_RegI cnt1, rsi_RegP str2, rax_RegI cnt2, - rbx_RegI result, regD vec, rcx_RegI tmp, rFlagsReg cr) +// fast search of substring with known size. +instruct string_indexof_conUL(rdi_RegP str1, rdx_RegI cnt1, rsi_RegP str2, immI int_cnt2, + rbx_RegI result, regD vec, rax_RegI cnt2, rcx_RegI tmp, rFlagsReg cr) %{ - predicate(UseSSE42Intrinsics); + predicate(UseSSE42Intrinsics && (((StrIndexOfNode*)n)->encoding() == StrIntrinsicNode::UL)); + match(Set result (StrIndexOf (Binary str1 cnt1) (Binary str2 int_cnt2))); + effect(TEMP vec, USE_KILL str1, USE_KILL str2, USE_KILL cnt1, KILL cnt2, KILL tmp, KILL cr); + + format %{ "String IndexOf char[] $str1,$cnt1,$str2,$int_cnt2 -> $result // KILL $vec, $cnt1, $cnt2, $tmp" %} + ins_encode %{ + int icnt2 = (int)$int_cnt2$$constant; + if (icnt2 >= 8) { + // IndexOf for constant substrings with size >= 8 elements + // which don't need to be loaded through stack. + __ string_indexofC8($str1$$Register, $str2$$Register, + $cnt1$$Register, $cnt2$$Register, + icnt2, $result$$Register, + $vec$$XMMRegister, $tmp$$Register, StrIntrinsicNode::UL); + } else { + // Small strings are loaded through stack if they cross page boundary. + __ string_indexof($str1$$Register, $str2$$Register, + $cnt1$$Register, $cnt2$$Register, + icnt2, $result$$Register, + $vec$$XMMRegister, $tmp$$Register, StrIntrinsicNode::UL); + } + %} + ins_pipe( pipe_slow ); +%} + +instruct string_indexofL(rdi_RegP str1, rdx_RegI cnt1, rsi_RegP str2, rax_RegI cnt2, + rbx_RegI result, regD vec, rcx_RegI tmp, rFlagsReg cr) +%{ + predicate(UseSSE42Intrinsics && (((StrIndexOfNode*)n)->encoding() == StrIntrinsicNode::LL)); + match(Set result (StrIndexOf (Binary str1 cnt1) (Binary str2 cnt2))); + effect(TEMP vec, USE_KILL str1, USE_KILL str2, USE_KILL cnt1, USE_KILL cnt2, KILL tmp, KILL cr); + + format %{ "String IndexOf byte[] $str1,$cnt1,$str2,$cnt2 -> $result // KILL all" %} + ins_encode %{ + __ string_indexof($str1$$Register, $str2$$Register, + $cnt1$$Register, $cnt2$$Register, + (-1), $result$$Register, + $vec$$XMMRegister, $tmp$$Register, StrIntrinsicNode::LL); + %} + ins_pipe( pipe_slow ); +%} + +instruct string_indexofU(rdi_RegP str1, rdx_RegI cnt1, rsi_RegP str2, rax_RegI cnt2, + rbx_RegI result, regD vec, rcx_RegI tmp, rFlagsReg cr) +%{ + predicate(UseSSE42Intrinsics && (((StrIndexOfNode*)n)->encoding() == StrIntrinsicNode::UU)); match(Set result (StrIndexOf (Binary str1 cnt1) (Binary str2 cnt2))); effect(TEMP vec, USE_KILL str1, USE_KILL str2, USE_KILL cnt1, USE_KILL cnt2, KILL tmp, KILL cr); - format %{ "String IndexOf $str1,$cnt1,$str2,$cnt2 -> $result // KILL all" %} + format %{ "String IndexOf char[] $str1,$cnt1,$str2,$cnt2 -> $result // KILL all" %} ins_encode %{ __ string_indexof($str1$$Register, $str2$$Register, $cnt1$$Register, $cnt2$$Register, (-1), $result$$Register, - $vec$$XMMRegister, $tmp$$Register); + $vec$$XMMRegister, $tmp$$Register, StrIntrinsicNode::UU); + %} + ins_pipe( pipe_slow ); +%} + +instruct string_indexofUL(rdi_RegP str1, rdx_RegI cnt1, rsi_RegP str2, rax_RegI cnt2, + rbx_RegI result, regD vec, rcx_RegI tmp, rFlagsReg cr) +%{ + predicate(UseSSE42Intrinsics && (((StrIndexOfNode*)n)->encoding() == StrIntrinsicNode::UL)); + match(Set result (StrIndexOf (Binary str1 cnt1) (Binary str2 cnt2))); + effect(TEMP vec, USE_KILL str1, USE_KILL str2, USE_KILL cnt1, USE_KILL cnt2, KILL tmp, KILL cr); + + format %{ "String IndexOf char[] $str1,$cnt1,$str2,$cnt2 -> $result // KILL all" %} + ins_encode %{ + __ string_indexof($str1$$Register, $str2$$Register, + $cnt1$$Register, $cnt2$$Register, + (-1), $result$$Register, + $vec$$XMMRegister, $tmp$$Register, StrIntrinsicNode::UL); + %} + ins_pipe( pipe_slow ); +%} + +instruct string_indexofU_char(rdi_RegP str1, rdx_RegI cnt1, rax_RegI ch, + rbx_RegI result, regD vec1, regD vec2, regD vec3, rcx_RegI tmp, rFlagsReg cr) +%{ + predicate(UseSSE42Intrinsics); + match(Set result (StrIndexOfChar (Binary str1 cnt1) ch)); + effect(TEMP vec1, TEMP vec2, TEMP vec3, USE_KILL str1, USE_KILL cnt1, USE_KILL ch, TEMP tmp, KILL cr); + format %{ "String IndexOf char[] $str1,$cnt1,$ch -> $result // KILL all" %} + ins_encode %{ + __ string_indexof_char($str1$$Register, $cnt1$$Register, $ch$$Register, $result$$Register, + $vec1$$XMMRegister, $vec2$$XMMRegister, $vec3$$XMMRegister, $tmp$$Register); %} ins_pipe( pipe_slow ); %} @@ -10532,26 +10687,86 @@ format %{ "String Equals $str1,$str2,$cnt -> $result // KILL $tmp1, $tmp2, $tmp3" %} ins_encode %{ - __ char_arrays_equals(false, $str1$$Register, $str2$$Register, - $cnt$$Register, $result$$Register, $tmp3$$Register, - $tmp1$$XMMRegister, $tmp2$$XMMRegister); + __ arrays_equals(false, $str1$$Register, $str2$$Register, + $cnt$$Register, $result$$Register, $tmp3$$Register, + $tmp1$$XMMRegister, $tmp2$$XMMRegister, false /* char */); %} ins_pipe( pipe_slow ); %} // fast array equals -instruct array_equals(rdi_RegP ary1, rsi_RegP ary2, rax_RegI result, +instruct array_equalsB(rdi_RegP ary1, rsi_RegP ary2, rax_RegI result, + regD tmp1, regD tmp2, rcx_RegI tmp3, rbx_RegI tmp4, rFlagsReg cr) +%{ + predicate(((AryEqNode*)n)->encoding() == StrIntrinsicNode::LL); + match(Set result (AryEq ary1 ary2)); + effect(TEMP tmp1, TEMP tmp2, USE_KILL ary1, USE_KILL ary2, KILL tmp3, KILL tmp4, KILL cr); + + format %{ "Array Equals byte[] $ary1,$ary2 -> $result // KILL $tmp1, $tmp2, $tmp3, $tmp4" %} + ins_encode %{ + __ arrays_equals(true, $ary1$$Register, $ary2$$Register, + $tmp3$$Register, $result$$Register, $tmp4$$Register, + $tmp1$$XMMRegister, $tmp2$$XMMRegister, false /* char */); + %} + ins_pipe( pipe_slow ); +%} + +instruct array_equalsC(rdi_RegP ary1, rsi_RegP ary2, rax_RegI result, regD tmp1, regD tmp2, rcx_RegI tmp3, rbx_RegI tmp4, rFlagsReg cr) %{ + predicate(((AryEqNode*)n)->encoding() == StrIntrinsicNode::UU); match(Set result (AryEq ary1 ary2)); effect(TEMP tmp1, TEMP tmp2, USE_KILL ary1, USE_KILL ary2, KILL tmp3, KILL tmp4, KILL cr); - //ins_cost(300); - format %{ "Array Equals $ary1,$ary2 -> $result // KILL $tmp1, $tmp2, $tmp3, $tmp4" %} + format %{ "Array Equals char[] $ary1,$ary2 -> $result // KILL $tmp1, $tmp2, $tmp3, $tmp4" %} + ins_encode %{ + __ arrays_equals(true, $ary1$$Register, $ary2$$Register, + $tmp3$$Register, $result$$Register, $tmp4$$Register, + $tmp1$$XMMRegister, $tmp2$$XMMRegister, true /* char */); + %} + ins_pipe( pipe_slow ); +%} + +instruct has_negatives(rsi_RegP ary1, rcx_RegI len, rax_RegI result, + regD tmp1, regD tmp2, rbx_RegI tmp3, rFlagsReg cr) +%{ + match(Set result (HasNegatives ary1 len)); + effect(TEMP tmp1, TEMP tmp2, USE_KILL ary1, USE_KILL len, KILL tmp3, KILL cr); + + format %{ "has negatives byte[] $ary1,$len -> $result // KILL $tmp1, $tmp2, $tmp3" %} + ins_encode %{ + __ has_negatives($ary1$$Register, $len$$Register, + $result$$Register, $tmp3$$Register, + $tmp1$$XMMRegister, $tmp2$$XMMRegister); + %} + ins_pipe( pipe_slow ); +%} + +// fast char[] to byte[] compression +instruct string_compress(rsi_RegP src, rdi_RegP dst, rdx_RegI len, regD tmp1, regD tmp2, regD tmp3, regD tmp4, + rcx_RegI tmp5, rax_RegI result, rFlagsReg cr) %{ + match(Set result (StrCompressedCopy src (Binary dst len))); + effect(TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4, USE_KILL src, USE_KILL dst, USE_KILL len, KILL tmp5, KILL cr); + + format %{ "String Compress $src,$dst -> $result // KILL RAX, RCX, RDX" %} + ins_encode %{ + __ char_array_compress($src$$Register, $dst$$Register, $len$$Register, + $tmp1$$XMMRegister, $tmp2$$XMMRegister, $tmp3$$XMMRegister, + $tmp4$$XMMRegister, $tmp5$$Register, $result$$Register); + %} + ins_pipe( pipe_slow ); +%} + +// fast byte[] to char[] inflation +instruct string_inflate(Universe dummy, rsi_RegP src, rdi_RegP dst, rdx_RegI len, + regD tmp1, rcx_RegI tmp2, rFlagsReg cr) %{ + match(Set dummy (StrInflatedCopy src (Binary dst len))); + effect(TEMP tmp1, TEMP tmp2, USE_KILL src, USE_KILL dst, USE_KILL len, KILL cr); + + format %{ "String Inflate $src,$dst // KILL $tmp1, $tmp2" %} ins_encode %{ - __ char_arrays_equals(true, $ary1$$Register, $ary2$$Register, - $tmp3$$Register, $result$$Register, $tmp4$$Register, - $tmp1$$XMMRegister, $tmp2$$XMMRegister); + __ byte_array_inflate($src$$Register, $dst$$Register, $len$$Register, + $tmp1$$XMMRegister, $tmp2$$Register); %} ins_pipe( pipe_slow ); %} --- old/src/cpu/zero/vm/globals_zero.hpp 2015-10-30 19:14:46.416475479 +0100 +++ new/src/cpu/zero/vm/globals_zero.hpp 2015-10-30 19:14:46.352475477 +0100 @@ -69,6 +69,9 @@ define_pd_global(bool, PreserveFramePointer, false); +// No performance work done here yet. +define_pd_global(bool, CompactStrings, false); + #define ARCH_FLAGS(develop, product, diagnostic, experimental, notproduct, range, constraint) \ \ product(bool, UseFastEmptyMethods, true, \ --- old/src/share/vm/adlc/formssel.cpp 2015-10-30 19:14:46.656475487 +0100 +++ new/src/share/vm/adlc/formssel.cpp 2015-10-30 19:14:46.592475484 +0100 @@ -607,6 +607,8 @@ ( strcmp(_matrule->_rChild->_opType,"StrComp" )==0 || strcmp(_matrule->_rChild->_opType,"StrEquals" )==0 || strcmp(_matrule->_rChild->_opType,"StrIndexOf" )==0 || + strcmp(_matrule->_rChild->_opType,"StrIndexOfChar" )==0 || + strcmp(_matrule->_rChild->_opType,"HasNegatives" )==0 || strcmp(_matrule->_rChild->_opType,"AryEq" )==0 )) return true; @@ -887,11 +889,16 @@ ( strcmp(_matrule->_rChild->_opType,"AryEq" )==0 || strcmp(_matrule->_rChild->_opType,"StrComp" )==0 || strcmp(_matrule->_rChild->_opType,"StrEquals" )==0 || + strcmp(_matrule->_rChild->_opType,"StrInflatedCopy" )==0 || + strcmp(_matrule->_rChild->_opType,"StrCompressedCopy" )==0 || strcmp(_matrule->_rChild->_opType,"StrIndexOf")==0 || + strcmp(_matrule->_rChild->_opType,"StrIndexOfChar")==0 || + strcmp(_matrule->_rChild->_opType,"HasNegatives")==0 || strcmp(_matrule->_rChild->_opType,"EncodeISOArray")==0)) { // String.(compareTo/equals/indexOf) and Arrays.equals // and sun.nio.cs.iso8859_1$Encoder.EncodeISOArray // take 1 control and 1 memory edges. + // Also String.(compressedCopy/inflatedCopy). return 2; } --- old/src/share/vm/adlc/main.cpp 2015-10-30 19:14:46.924475495 +0100 +++ new/src/share/vm/adlc/main.cpp 2015-10-30 19:14:46.856475493 +0100 @@ -221,6 +221,7 @@ AD.addInclude(AD._CPP_file, "oops/method.hpp"); AD.addInclude(AD._CPP_file, "oops/oop.inline.hpp"); AD.addInclude(AD._CPP_file, "opto/cfgnode.hpp"); + AD.addInclude(AD._CPP_file, "opto/intrinsicnode.hpp"); AD.addInclude(AD._CPP_file, "opto/locknode.hpp"); AD.addInclude(AD._CPP_file, "opto/opcodes.hpp"); AD.addInclude(AD._CPP_file, "opto/regalloc.hpp"); @@ -256,6 +257,7 @@ AD.addInclude(AD._DFA_file, "precompiled.hpp"); AD.addInclude(AD._DFA_file, "adfiles", get_basename(AD._HPP_file._name)); AD.addInclude(AD._DFA_file, "opto/cfgnode.hpp"); // Use PROB_MAX in predicate. + AD.addInclude(AD._DFA_file, "opto/intrinsicnode.hpp"); AD.addInclude(AD._DFA_file, "opto/matcher.hpp"); AD.addInclude(AD._DFA_file, "opto/opcodes.hpp"); AD.addInclude(AD._DFA_file, "opto/convertnode.hpp"); --- old/src/share/vm/c1/c1_Compiler.cpp 2015-10-30 19:14:47.168475503 +0100 +++ new/src/share/vm/c1/c1_Compiler.cpp 2015-10-30 19:14:47.104475501 +0100 @@ -225,6 +225,8 @@ case vmIntrinsics::_updateByteBufferCRC32: case vmIntrinsics::_compareAndSwapInt: case vmIntrinsics::_compareAndSwapObject: + case vmIntrinsics::_getCharStringU: + case vmIntrinsics::_putCharStringU: #ifdef TRACE_HAVE_INTRINSICS case vmIntrinsics::_classID: case vmIntrinsics::_threadID: --- old/src/share/vm/c1/c1_GraphBuilder.cpp 2015-10-30 19:14:47.404475510 +0100 +++ new/src/share/vm/c1/c1_GraphBuilder.cpp 2015-10-30 19:14:47.336475508 +0100 @@ -3445,6 +3445,8 @@ case vmIntrinsics::_getAndSetInt : case vmIntrinsics::_getAndSetLong : case vmIntrinsics::_getAndSetObject : append_unsafe_get_and_set_obj(callee, false); return; + case vmIntrinsics::_getCharStringU : append_char_access(callee, false); return; + case vmIntrinsics::_putCharStringU : append_char_access(callee, true); return; default: break; } @@ -4179,6 +4181,30 @@ compilation()->set_has_unsafe_access(true); } +void GraphBuilder::append_char_access(ciMethod* callee, bool is_store) { + // This intrinsic accesses byte[] array as char[] array. Computing the offsets + // correctly requires matched array shapes. + assert (arrayOopDesc::base_offset_in_bytes(T_CHAR) == arrayOopDesc::base_offset_in_bytes(T_BYTE), + "sanity: byte[] and char[] bases agree"); + assert (type2aelembytes(T_CHAR) == type2aelembytes(T_BYTE)*2, + "sanity: byte[] and char[] scales agree"); + + ValueStack* state_before = copy_state_indexed_access(); + compilation()->set_has_access_indexed(true); + Values* args = state()->pop_arguments(callee->arg_size()); + Value array = args->at(0); + Value index = args->at(1); + if (is_store) { + Value value = args->at(2); + Instruction* store = append(new StoreIndexed(array, index, NULL, T_CHAR, value, state_before)); + store->set_flag(Instruction::NeedsRangeCheckFlag, false); + _memory->store_value(value); + } else { + Instruction* load = append(new LoadIndexed(array, index, NULL, T_CHAR, state_before)); + load->set_flag(Instruction::NeedsRangeCheckFlag, false); + push(load->type(), load); + } +} void GraphBuilder::print_inlining(ciMethod* callee, const char* msg, bool success) { CompileLog* log = compilation()->log(); --- old/src/share/vm/c1/c1_GraphBuilder.hpp 2015-10-30 19:14:47.668475519 +0100 +++ new/src/share/vm/c1/c1_GraphBuilder.hpp 2015-10-30 19:14:47.600475516 +0100 @@ -372,6 +372,7 @@ void append_unsafe_put_raw(ciMethod* callee, BasicType t); void append_unsafe_CAS(ciMethod* callee); void append_unsafe_get_and_set_obj(ciMethod* callee, bool is_add); + void append_char_access(ciMethod* callee, bool is_store); void print_inlining(ciMethod* callee, const char* msg = NULL, bool success = true); --- old/src/share/vm/c1/c1_LIRAssembler.hpp 2015-10-30 19:14:47.904475526 +0100 +++ new/src/share/vm/c1/c1_LIRAssembler.hpp 2015-10-30 19:14:47.840475524 +0100 @@ -161,8 +161,6 @@ // particular sparc uses this for delay slot filling. void peephole(LIR_List* list); - void emit_string_compare(LIR_Opr left, LIR_Opr right, LIR_Opr dst, CodeEmitInfo* info); - void return_op(LIR_Opr result); // returns offset of poll instruction --- old/src/share/vm/ci/ciTypeArray.cpp 2015-10-30 19:14:48.144475534 +0100 +++ new/src/share/vm/ci/ciTypeArray.cpp 2015-10-30 19:14:48.080475532 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 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 @@ -46,3 +46,13 @@ #endif //ASSERT return c; } + +// ------------------------------------------------------------------ +// ciTypeArray::byte_at +// +// Implementation of the byte_at method. +jbyte ciTypeArray::byte_at(int index) { + VM_ENTRY_MARK; + assert(index >= 0 && index < length(), "out of range"); + return get_typeArrayOop()->byte_at(index); +} --- old/src/share/vm/ci/ciTypeArray.hpp 2015-10-30 19:14:48.376475541 +0100 +++ new/src/share/vm/ci/ciTypeArray.hpp 2015-10-30 19:14:48.312475539 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 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 @@ -56,6 +56,9 @@ // array will never change. jchar char_at(int index); + // Return byte at index. + jbyte byte_at(int index); + }; #endif // SHARE_VM_CI_CITYPEARRAY_HPP --- old/src/share/vm/classfile/javaClasses.cpp 2015-10-30 19:14:48.612475549 +0100 +++ new/src/share/vm/classfile/javaClasses.cpp 2015-10-30 19:14:48.544475546 +0100 @@ -147,9 +147,8 @@ int java_lang_String::value_offset = 0; -int java_lang_String::offset_offset = 0; -int java_lang_String::count_offset = 0; int java_lang_String::hash_offset = 0; +int java_lang_String::coder_offset = 0; bool java_lang_String::initialized = false; @@ -161,16 +160,39 @@ assert(!initialized, "offsets should be initialized only once"); Klass* k = SystemDictionary::String_klass(); - compute_offset(value_offset, k, vmSymbols::value_name(), vmSymbols::char_array_signature()); - compute_optional_offset(offset_offset, k, vmSymbols::offset_name(), vmSymbols::int_signature()); - compute_optional_offset(count_offset, k, vmSymbols::count_name(), vmSymbols::int_signature()); + compute_offset(value_offset, k, vmSymbols::value_name(), vmSymbols::byte_array_signature()); compute_optional_offset(hash_offset, k, vmSymbols::hash_name(), vmSymbols::int_signature()); + compute_optional_offset(coder_offset, k, vmSymbols::coder_name(), vmSymbols::byte_signature()); initialized = true; } -Handle java_lang_String::basic_create(int length, TRAPS) { +class CompactStringsFixup : public FieldClosure { +private: + bool _value; + +public: + CompactStringsFixup(bool value) : _value(value) {} + + void do_field(fieldDescriptor* fd) { + if (fd->name() == vmSymbols::compact_strings_name()) { + oop mirror = fd->field_holder()->java_mirror(); + assert(fd->field_holder() == SystemDictionary::String_klass(), "Should be String"); + assert(mirror != NULL, "String must have mirror already"); + mirror->bool_field_put(fd->offset(), _value); + } + } +}; + +void java_lang_String::set_compact_strings(bool value) { + CompactStringsFixup fix(value); + InstanceKlass::cast(SystemDictionary::String_klass())->do_local_static_fields(&fix); +} + +Handle java_lang_String::basic_create(int length, bool is_latin1, TRAPS) { assert(initialized, "Must be initialized"); + assert(CompactStrings || !is_latin1, "Must be UTF16 without CompactStrings"); + // Create the String object first, so there's a chance that the String // and the char array it points to end up in the same cache line. oop obj; @@ -179,26 +201,44 @@ // Create the char array. The String object must be handlized here // because GC can happen as a result of the allocation attempt. Handle h_obj(THREAD, obj); - typeArrayOop buffer; - buffer = oopFactory::new_charArray(length, CHECK_NH); + int arr_length = is_latin1 ? length : length << 1; // 2 bytes per UTF16. + typeArrayOop buffer = oopFactory::new_byteArray(arr_length, CHECK_NH);; // Point the String at the char array obj = h_obj(); set_value(obj, buffer); // No need to zero the offset, allocation zero'ed the entire String object - assert(offset(obj) == 0, "initial String offset should be zero"); -//set_offset(obj, 0); - set_count(obj, length); - + set_coder(obj, is_latin1 ? CODER_LATIN1 : CODER_UTF16); return h_obj; } Handle java_lang_String::create_from_unicode(jchar* unicode, int length, TRAPS) { - Handle h_obj = basic_create(length, CHECK_NH); + bool is_latin1 = CompactStrings && UNICODE::is_latin1(unicode, length); + Handle h_obj = basic_create(length, is_latin1, CHECK_NH); typeArrayOop buffer = value(h_obj()); - for (int index = 0; index < length; index++) { - buffer->char_at_put(index, unicode[index]); + assert(TypeArrayKlass::cast(buffer->klass())->element_type() == T_BYTE, "only byte[]"); + if (is_latin1) { + for (int index = 0; index < length; index++) { + buffer->byte_at_put(index, (jbyte)unicode[index]); + } + } else { + for (int index = 0; index < length; index++) { + buffer->char_at_put(index, unicode[index]); + } } + +#ifdef ASSERT + { + ResourceMark rm; + char* expected = UNICODE::as_utf8(unicode, length); + char* actual = as_utf8_string(h_obj()); + if (strcmp(expected, actual) != 0) { + tty->print_cr("Unicode conversion failure: %s --> %s", expected, actual); + ShouldNotReachHere(); + } + } +#endif + return h_obj; } @@ -211,11 +251,40 @@ if (utf8_str == NULL) { return Handle(); } - int length = UTF8::unicode_length(utf8_str); - Handle h_obj = basic_create(length, CHECK_NH); + bool has_multibyte, is_latin1; + int length = UTF8::unicode_length(utf8_str, is_latin1, has_multibyte); + if (!CompactStrings) { + has_multibyte = true; + is_latin1 = false; + } + + Handle h_obj = basic_create(length, is_latin1, CHECK_NH); if (length > 0) { - UTF8::convert_to_unicode(utf8_str, value(h_obj())->char_at_addr(0), length); + if (!has_multibyte) { + strncpy((char*)value(h_obj())->byte_at_addr(0), utf8_str, length); + } else if (is_latin1) { + UTF8::convert_to_unicode(utf8_str, value(h_obj())->byte_at_addr(0), length); + } else { + UTF8::convert_to_unicode(utf8_str, value(h_obj())->char_at_addr(0), length); + } + } + +#ifdef ASSERT + // This check is too strict because the input string is not necessarily valid UTF8. + // For example, it may be created with arbitrary content via jni_NewStringUTF. + /* + { + ResourceMark rm; + const char* expected = utf8_str; + char* actual = as_utf8_string(h_obj()); + if (strcmp(expected, actual) != 0) { + tty->print_cr("String conversion failure: %s --> %s", expected, actual); + ShouldNotReachHere(); + } } + */ +#endif + return h_obj; } @@ -225,11 +294,39 @@ } Handle java_lang_String::create_from_symbol(Symbol* symbol, TRAPS) { - int length = UTF8::unicode_length((char*)symbol->bytes(), symbol->utf8_length()); - Handle h_obj = basic_create(length, CHECK_NH); + const char* utf8_str = (char*)symbol->bytes(); + int utf8_len = symbol->utf8_length(); + + bool has_multibyte, is_latin1; + int length = UTF8::unicode_length(utf8_str, utf8_len, is_latin1, has_multibyte); + if (!CompactStrings) { + has_multibyte = true; + is_latin1 = false; + } + + Handle h_obj = basic_create(length, is_latin1, CHECK_NH); if (length > 0) { - UTF8::convert_to_unicode((char*)symbol->bytes(), value(h_obj())->char_at_addr(0), length); + if (!has_multibyte) { + strncpy((char*)value(h_obj())->byte_at_addr(0), utf8_str, length); + } else if (is_latin1) { + UTF8::convert_to_unicode(utf8_str, value(h_obj())->byte_at_addr(0), length); + } else { + UTF8::convert_to_unicode(utf8_str, value(h_obj())->char_at_addr(0), length); + } } + +#ifdef ASSERT + { + ResourceMark rm; + const char* expected = symbol->as_utf8(); + char* actual = as_utf8_string(h_obj()); + if (strncmp(expected, actual, utf8_len) != 0) { + tty->print_cr("Symbol conversion failure: %s --> %s", expected, actual); + ShouldNotReachHere(); + } + } +#endif + return h_obj; } @@ -261,7 +358,6 @@ // Converts a Java String to a native C string that can be used for // native OS calls. char* java_lang_String::as_platform_dependent_str(Handle java_string, TRAPS) { - typedef char* (*to_platform_string_fn_t)(JNIEnv*, jstring, bool*); static to_platform_string_fn_t _to_platform_string_fn = NULL; @@ -292,13 +388,15 @@ oop obj = java_string(); // Typical usage is to convert all '/' to '.' in string. typeArrayOop value = java_lang_String::value(obj); - int offset = java_lang_String::offset(obj); int length = java_lang_String::length(obj); + bool is_latin1 = java_lang_String::is_latin1(obj); // First check if any from_char exist int index; // Declared outside, used later for (index = 0; index < length; index++) { - if (value->char_at(index + offset) == from_char) { + jchar c = !is_latin1 ? value->char_at(index) : + ((jchar) value->byte_at(index)) & 0xff; + if (c == from_char) { break; } } @@ -307,34 +405,66 @@ return java_string; } - // Create new UNICODE buffer. Must handlize value because GC + // Check if result string will be latin1 + bool to_is_latin1 = false; + + // Replacement char must be latin1 + if (CompactStrings && UNICODE::is_latin1(to_char)) { + if (is_latin1) { + // Source string is latin1 as well + to_is_latin1 = true; + } else if (!UNICODE::is_latin1(from_char)) { + // We are replacing an UTF16 char. Scan string to + // check if result can be latin1 encoded. + to_is_latin1 = true; + for (index = 0; index < length; index++) { + jchar c = value->char_at(index); + if (c != from_char && !UNICODE::is_latin1(c)) { + to_is_latin1 = false; + break; + } + } + } + } + + // Create new UNICODE (or byte) buffer. Must handlize value because GC // may happen during String and char array creation. typeArrayHandle h_value(THREAD, value); - Handle string = basic_create(length, CHECK_NH); - + Handle string = basic_create(length, to_is_latin1, CHECK_NH); typeArrayOop from_buffer = h_value(); - typeArrayOop to_buffer = java_lang_String::value(string()); + typeArrayOop to_buffer = java_lang_String::value(string()); // Copy contents for (index = 0; index < length; index++) { - jchar c = from_buffer->char_at(index + offset); + jchar c = (!is_latin1) ? from_buffer->char_at(index) : + ((jchar) from_buffer->byte_at(index)) & 0xff; if (c == from_char) { c = to_char; } - to_buffer->char_at_put(index, c); + if (!to_is_latin1) { + to_buffer->char_at_put(index, c); + } else { + to_buffer->byte_at_put(index, (jbyte) c); + } } return string; } jchar* java_lang_String::as_unicode_string(oop java_string, int& length, TRAPS) { typeArrayOop value = java_lang_String::value(java_string); - int offset = java_lang_String::offset(java_string); length = java_lang_String::length(java_string); + bool is_latin1 = java_lang_String::is_latin1(java_string); jchar* result = NEW_RESOURCE_ARRAY_RETURN_NULL(jchar, length); if (result != NULL) { - for (int index = 0; index < length; index++) { - result[index] = value->char_at(index + offset); + if (!is_latin1) { + for (int index = 0; index < length; index++) { + result[index] = value->char_at(index); + } + } else { + for (int index = 0; index < length; index++) { + result[index] = ((jchar) value->byte_at(index)) & 0xff; + } } } else { THROW_MSG_0(vmSymbols::java_lang_OutOfMemoryError(), "could not allocate Unicode string"); @@ -348,21 +478,35 @@ if (length == 0) return 0; typeArrayOop value = java_lang_String::value(java_string); - int offset = java_lang_String::offset(java_string); - return java_lang_String::hash_code(value->char_at_addr(offset), length); + bool is_latin1 = java_lang_String::is_latin1(java_string); + + if (is_latin1) { + return java_lang_String::hash_code(value->byte_at_addr(0), length); + } else { + return java_lang_String::hash_code(value->char_at_addr(0), length); + } } char* java_lang_String::as_quoted_ascii(oop java_string) { typeArrayOop value = java_lang_String::value(java_string); - int offset = java_lang_String::offset(java_string); int length = java_lang_String::length(java_string); + bool is_latin1 = java_lang_String::is_latin1(java_string); - jchar* base = (length == 0) ? NULL : value->char_at_addr(offset); - if (base == NULL) return NULL; + if (length == 0) return NULL; - int result_length = UNICODE::quoted_ascii_length(base, length) + 1; - char* result = NEW_RESOURCE_ARRAY(char, result_length); - UNICODE::as_quoted_ascii(base, length, result, result_length); + char* result; + int result_length; + if (!is_latin1) { + jchar* base = value->char_at_addr(0); + result_length = UNICODE::quoted_ascii_length(base, length) + 1; + result = NEW_RESOURCE_ARRAY(char, result_length); + UNICODE::as_quoted_ascii(base, length, result, result_length); + } else { + jbyte* base = value->byte_at_addr(0); + result_length = UNICODE::quoted_ascii_length(base, length) + 1; + result = NEW_RESOURCE_ARRAY(char, result_length); + UNICODE::as_quoted_ascii(base, length, result, result_length); + } assert(result_length >= length + 1, "must not be shorter"); assert(result_length == (int)strlen(result) + 1, "must match"); return result; @@ -370,89 +514,141 @@ unsigned int java_lang_String::hash_string(oop java_string) { int length = java_lang_String::length(java_string); - // Zero length string doesn't hash necessarily hash to zero. + // Zero length string doesn't necessarily hash to zero. if (length == 0) { - return StringTable::hash_string(NULL, 0); + return StringTable::hash_string((jchar*) NULL, 0); } typeArrayOop value = java_lang_String::value(java_string); - int offset = java_lang_String::offset(java_string); - return StringTable::hash_string(value->char_at_addr(offset), length); + bool is_latin1 = java_lang_String::is_latin1(java_string); + if (is_latin1) { + return StringTable::hash_string(value->byte_at_addr(0), length); + } else { + return StringTable::hash_string(value->char_at_addr(0), length); + } } Symbol* java_lang_String::as_symbol(Handle java_string, TRAPS) { oop obj = java_string(); typeArrayOop value = java_lang_String::value(obj); - int offset = java_lang_String::offset(obj); int length = java_lang_String::length(obj); - jchar* base = (length == 0) ? NULL : value->char_at_addr(offset); - Symbol* sym = SymbolTable::lookup_unicode(base, length, THREAD); - return sym; + bool is_latin1 = java_lang_String::is_latin1(obj); + if (!is_latin1) { + jchar* base = (length == 0) ? NULL : value->char_at_addr(0); + Symbol* sym = SymbolTable::lookup_unicode(base, length, THREAD); + return sym; + } else { + ResourceMark rm; + jbyte* position = (length == 0) ? NULL : value->byte_at_addr(0); + const char* base = UNICODE::as_utf8(position, length); + Symbol* sym = SymbolTable::lookup(base, length, THREAD); + return sym; + } } Symbol* java_lang_String::as_symbol_or_null(oop java_string) { typeArrayOop value = java_lang_String::value(java_string); - int offset = java_lang_String::offset(java_string); int length = java_lang_String::length(java_string); - jchar* base = (length == 0) ? NULL : value->char_at_addr(offset); - return SymbolTable::probe_unicode(base, length); + bool is_latin1 = java_lang_String::is_latin1(java_string); + if (!is_latin1) { + jchar* base = (length == 0) ? NULL : value->char_at_addr(0); + return SymbolTable::probe_unicode(base, length); + } else { + ResourceMark rm; + jbyte* position = (length == 0) ? NULL : value->byte_at_addr(0); + const char* base = UNICODE::as_utf8(position, length); + return SymbolTable::probe(base, length); + } } - int java_lang_String::utf8_length(oop java_string) { typeArrayOop value = java_lang_String::value(java_string); - int offset = java_lang_String::offset(java_string); int length = java_lang_String::length(java_string); - jchar* position = (length == 0) ? NULL : value->char_at_addr(offset); - return UNICODE::utf8_length(position, length); + bool is_latin1 = java_lang_String::is_latin1(java_string); + if (length == 0) { + return 0; + } + if (!is_latin1) { + return UNICODE::utf8_length(value->char_at_addr(0), length); + } else { + return UNICODE::utf8_length(value->byte_at_addr(0), length); + } } char* java_lang_String::as_utf8_string(oop java_string) { typeArrayOop value = java_lang_String::value(java_string); - int offset = java_lang_String::offset(java_string); int length = java_lang_String::length(java_string); - jchar* position = (length == 0) ? NULL : value->char_at_addr(offset); - return UNICODE::as_utf8(position, length); + bool is_latin1 = java_lang_String::is_latin1(java_string); + if (!is_latin1) { + jchar* position = (length == 0) ? NULL : value->char_at_addr(0); + return UNICODE::as_utf8(position, length); + } else { + jbyte* position = (length == 0) ? NULL : value->byte_at_addr(0); + return UNICODE::as_utf8(position, length); + } } char* java_lang_String::as_utf8_string(oop java_string, char* buf, int buflen) { typeArrayOop value = java_lang_String::value(java_string); - int offset = java_lang_String::offset(java_string); int length = java_lang_String::length(java_string); - jchar* position = (length == 0) ? NULL : value->char_at_addr(offset); - return UNICODE::as_utf8(position, length, buf, buflen); + bool is_latin1 = java_lang_String::is_latin1(java_string); + if (!is_latin1) { + jchar* position = (length == 0) ? NULL : value->char_at_addr(0); + return UNICODE::as_utf8(position, length, buf, buflen); + } else { + jbyte* position = (length == 0) ? NULL : value->byte_at_addr(0); + return UNICODE::as_utf8(position, length, buf, buflen); + } } char* java_lang_String::as_utf8_string(oop java_string, int start, int len) { typeArrayOop value = java_lang_String::value(java_string); - int offset = java_lang_String::offset(java_string); int length = java_lang_String::length(java_string); assert(start + len <= length, "just checking"); - jchar* position = value->char_at_addr(offset + start); - return UNICODE::as_utf8(position, len); + bool is_latin1 = java_lang_String::is_latin1(java_string); + if (!is_latin1) { + jchar* position = value->char_at_addr(start); + return UNICODE::as_utf8(position, len); + } else { + jbyte* position = value->byte_at_addr(start); + return UNICODE::as_utf8(position, len); + } } char* java_lang_String::as_utf8_string(oop java_string, int start, int len, char* buf, int buflen) { typeArrayOop value = java_lang_String::value(java_string); - int offset = java_lang_String::offset(java_string); int length = java_lang_String::length(java_string); assert(start + len <= length, "just checking"); - jchar* position = value->char_at_addr(offset + start); - return UNICODE::as_utf8(position, len, buf, buflen); + bool is_latin1 = java_lang_String::is_latin1(java_string); + if (!is_latin1) { + jchar* position = value->char_at_addr(start); + return UNICODE::as_utf8(position, len, buf, buflen); + } else { + jbyte* position = value->byte_at_addr(start); + return UNICODE::as_utf8(position, len, buf, buflen); + } } bool java_lang_String::equals(oop java_string, jchar* chars, int len) { assert(java_string->klass() == SystemDictionary::String_klass(), "must be java_string"); typeArrayOop value = java_lang_String::value(java_string); - int offset = java_lang_String::offset(java_string); int length = java_lang_String::length(java_string); if (length != len) { return false; } - for (int i = 0; i < len; i++) { - if (value->char_at(i + offset) != chars[i]) { - return false; + bool is_latin1 = java_lang_String::is_latin1(java_string); + if (!is_latin1) { + for (int i = 0; i < len; i++) { + if (value->char_at(i) != chars[i]) { + return false; + } + } + } else { + for (int i = 0; i < len; i++) { + if ((((jchar) value->byte_at(i)) & 0xff) != chars[i]) { + return false; + } } } return true; @@ -464,17 +660,20 @@ assert(str2->klass() == SystemDictionary::String_klass(), "must be java String"); typeArrayOop value1 = java_lang_String::value(str1); - int offset1 = java_lang_String::offset(str1); int length1 = java_lang_String::length(str1); + bool is_latin1 = java_lang_String::is_latin1(str1); typeArrayOop value2 = java_lang_String::value(str2); - int offset2 = java_lang_String::offset(str2); int length2 = java_lang_String::length(str2); + bool is_latin2 = java_lang_String::is_latin1(str2); - if (length1 != length2) { + if ((length1 != length2) || (is_latin1 != is_latin2)) { + // Strings of different size or with different + // coders are never equal. return false; } - for (int i = 0; i < length1; i++) { - if (value1->char_at(i + offset1) != value2->char_at(i + offset2)) { + int blength1 = value1->length(); + for (int i = 0; i < value1->length(); i++) { + if (value1->byte_at(i) != value2->byte_at(i)) { return false; } } @@ -492,12 +691,13 @@ return; } - int offset = java_lang_String::offset(java_string); int length = java_lang_String::length(java_string); + bool is_latin1 = java_lang_String::is_latin1(java_string); st->print("\""); for (int index = 0; index < length; index++) { - st->print("%c", value->char_at(index + offset)); + st->print("%c", (!is_latin1) ? value->char_at(index) : + ((jchar) value->byte_at(index)) & 0xff ); } st->print("\""); } @@ -1169,10 +1369,13 @@ // ("name as oop" accessor is not necessary) -typeArrayOop java_lang_ThreadGroup::name(oop java_thread_group) { +const char* java_lang_ThreadGroup::name(oop java_thread_group) { oop name = java_thread_group->obj_field(_name_offset); // ThreadGroup.name can be null - return name == NULL ? (typeArrayOop)NULL : java_lang_String::value(name); + if (name != NULL) { + return java_lang_String::as_utf8_string(name); + } + return NULL; } int java_lang_ThreadGroup::nthreads(oop java_thread_group) { @@ -3541,14 +3744,13 @@ // java.lang.String - CHECK_OFFSET("java/lang/String", java_lang_String, value, "[C"); - if (java_lang_String::has_offset_field()) { - CHECK_OFFSET("java/lang/String", java_lang_String, offset, "I"); - CHECK_OFFSET("java/lang/String", java_lang_String, count, "I"); - } + CHECK_OFFSET("java/lang/String", java_lang_String, value, "[B"); if (java_lang_String::has_hash_field()) { CHECK_OFFSET("java/lang/String", java_lang_String, hash, "I"); } + if (java_lang_String::has_coder_field()) { + CHECK_OFFSET("java/lang/String", java_lang_String, coder, "B"); + } // java.lang.Class --- old/src/share/vm/classfile/javaClasses.hpp 2015-10-30 19:14:48.884475557 +0100 +++ new/src/share/vm/classfile/javaClasses.hpp 2015-10-30 19:14:48.824475555 +0100 @@ -53,28 +53,28 @@ class java_lang_String : AllStatic { private: static int value_offset; - static int offset_offset; - static int count_offset; static int hash_offset; + static int coder_offset; static bool initialized; - static Handle basic_create(int length, TRAPS); + static Handle basic_create(int length, bool byte_arr, TRAPS); - static void set_offset(oop string, int offset) { + static void set_coder(oop string, jbyte coder) { assert(initialized, "Must be initialized"); - if (offset_offset > 0) { - string->int_field_put(offset_offset, offset); - } - } - static void set_count( oop string, int count) { - assert(initialized, "Must be initialized"); - if (count_offset > 0) { - string->int_field_put(count_offset, count); + if (coder_offset > 0) { + string->byte_field_put(coder_offset, coder); } } public: + + // Coders + enum Coder { + CODER_LATIN1 = 0, + CODER_UTF16 = 1 + }; + static void compute_offsets(); // Instance creation @@ -86,37 +86,29 @@ static Handle create_from_platform_dependent_str(const char* str, TRAPS); static Handle char_converter(Handle java_string, jchar from_char, jchar to_char, TRAPS); - static bool has_offset_field() { + static bool has_hash_field() { assert(initialized, "Must be initialized"); - return (offset_offset > 0); + return (hash_offset > 0); } - - static bool has_count_field() { + static bool has_coder_field() { assert(initialized, "Must be initialized"); - return (count_offset > 0); + return (coder_offset > 0); } - static bool has_hash_field() { - assert(initialized, "Must be initialized"); - return (hash_offset > 0); - } + static void set_compact_strings(bool value); static int value_offset_in_bytes() { assert(initialized && (value_offset > 0), "Must be initialized"); return value_offset; } - static int count_offset_in_bytes() { - assert(initialized && (count_offset > 0), "Must be initialized"); - return count_offset; - } - static int offset_offset_in_bytes() { - assert(initialized && (offset_offset > 0), "Must be initialized"); - return offset_offset; - } static int hash_offset_in_bytes() { assert(initialized && (hash_offset > 0), "Must be initialized"); return hash_offset; } + static int coder_offset_in_bytes() { + assert(initialized && (coder_offset > 0), "Must be initialized"); + return coder_offset; + } static void set_value_raw(oop string, typeArrayOop buffer) { assert(initialized, "Must be initialized"); @@ -142,28 +134,30 @@ assert(is_instance(java_string), "must be java_string"); return java_string->int_field(hash_offset); } - static int offset(oop java_string) { + static bool is_latin1(oop java_string) { assert(initialized, "Must be initialized"); assert(is_instance(java_string), "must be java_string"); - if (offset_offset > 0) { - return java_string->int_field(offset_offset); + if (coder_offset > 0) { + jbyte coder = java_string->byte_field(coder_offset); + assert(CompactStrings || coder == CODER_UTF16, "Must be UTF16 without CompactStrings"); + return coder == CODER_LATIN1; } else { - return 0; + return false; } } static int length(oop java_string) { assert(initialized, "Must be initialized"); assert(is_instance(java_string), "must be java_string"); - if (count_offset > 0) { - return java_string->int_field(count_offset); - } else { - typeArrayOop value_array = ((typeArrayOop)java_string->obj_field(value_offset)); - if (value_array == NULL) { - return 0; - } else { - return value_array->length(); - } + typeArrayOop value_array = ((typeArrayOop)java_string->obj_field(value_offset)); + if (value_array == NULL) { + return 0; + } + int arr_length = value_array->length(); + if (!is_latin1(java_string)) { + assert((arr_length & 1) == 0, "should be even for UTF16 string"); + arr_length >>= 1; // convert number of bytes to number of elements } + return arr_length; } static int utf8_length(oop java_string); @@ -187,7 +181,7 @@ // hash P(31) from Kernighan & Ritchie // // For this reason, THIS ALGORITHM MUST MATCH String.hashCode(). - template static unsigned int hash_code(T* s, int len) { + static unsigned int hash_code(const jchar* s, int len) { unsigned int h = 0; while (len-- > 0) { h = 31*h + (unsigned int) *s; @@ -195,7 +189,18 @@ } return h; } + + static unsigned int hash_code(const jbyte* s, int len) { + unsigned int h = 0; + while (len-- > 0) { + h = 31*h + (((unsigned int) *s) & 0xFF); + s++; + } + return h; + } + static unsigned int hash_code(oop java_string); + static unsigned int latin1_hash_code(typeArrayOop value, int len); // This is the string hash code used by the StringTable, which may be // the same as String.hashCode or an alternate hash code. @@ -451,7 +456,7 @@ // parent ThreadGroup static oop parent(oop java_thread_group); // name - static typeArrayOop name(oop java_thread_group); + static const char* name(oop java_thread_group); // ("name as oop" accessor is not necessary) // Number of threads in group static int nthreads(oop java_thread_group); --- old/src/share/vm/classfile/stringTable.cpp 2015-10-30 19:14:49.112475564 +0100 +++ new/src/share/vm/classfile/stringTable.cpp 2015-10-30 19:14:49.052475563 +0100 @@ -94,11 +94,16 @@ CompactHashtable StringTable::_shared_table; // Pick hashing algorithm -unsigned int StringTable::hash_string(const jchar* s, int len) { +template +unsigned int StringTable::hash_string(const T* s, int len) { return use_alternate_hashcode() ? AltHashing::murmur3_32(seed(), s, len) : java_lang_String::hash_code(s, len); } +// Explicit instantiation for all supported types. +template unsigned int StringTable::hash_string(const jchar* s, int len); +template unsigned int StringTable::hash_string(const jbyte* s, int len); + oop StringTable::lookup_shared(jchar* name, int len) { // java_lang_String::hash_code() was used to compute hash values in the shared table. Don't // use the hash value from StringTable::hash_string() as it might use alternate hashcode. @@ -409,17 +414,25 @@ for ( ; p != NULL; p = p->next()) { oop s = p->literal(); typeArrayOop value = java_lang_String::value(s); - int offset = java_lang_String::offset(s); int length = java_lang_String::length(s); + bool is_latin1 = java_lang_String::is_latin1(s); if (length <= 0) { st->print("%d: ", length); } else { ResourceMark rm(THREAD); - jchar* chars = (jchar*)value->char_at_addr(offset); - int utf8_length = UNICODE::utf8_length(chars, length); - char* utf8_string = NEW_RESOURCE_ARRAY(char, utf8_length + 1); - UNICODE::convert_to_utf8(chars, length, utf8_string); + int utf8_length; + char* utf8_string; + + if (!is_latin1) { + jchar* chars = value->char_at_addr(0); + utf8_length = UNICODE::utf8_length(chars, length); + utf8_string = UNICODE::as_utf8(chars, length); + } else { + jbyte* bytes = value->byte_at_addr(0); + utf8_length = UNICODE::utf8_length(bytes, length); + utf8_string = UNICODE::as_utf8(bytes, length); + } st->print("%d: ", utf8_length); HashtableTextDump::put_utf8(st, utf8_string, utf8_length); --- old/src/share/vm/classfile/stringTable.hpp 2015-10-30 19:14:49.356475572 +0100 +++ new/src/share/vm/classfile/stringTable.hpp 2015-10-30 19:14:49.292475570 +0100 @@ -111,7 +111,7 @@ // Hashing algorithm, used as the hash value used by the // StringTable for bucket selection and comparison (stored in the // HashtableEntry structures). This is used in the String.intern() method. - static unsigned int hash_string(const jchar* s, int len); + template static unsigned int hash_string(const T* s, int len); // Internal test. static void test_alt_hash() PRODUCT_RETURN; --- old/src/share/vm/classfile/symbolTable.cpp 2015-10-30 19:14:49.588475580 +0100 +++ new/src/share/vm/classfile/symbolTable.cpp 2015-10-30 19:14:49.520475577 +0100 @@ -244,7 +244,7 @@ unsigned int SymbolTable::hash_symbol(const char* s, int len) { return use_alternate_hashcode() ? AltHashing::murmur3_32(seed(), (const jbyte*)s, len) : - java_lang_String::hash_code(s, len); + java_lang_String::hash_code((const jbyte*)s, len); } --- old/src/share/vm/classfile/symbolTable.hpp 2015-10-30 19:14:49.840475588 +0100 +++ new/src/share/vm/classfile/symbolTable.hpp 2015-10-30 19:14:49.776475585 +0100 @@ -165,7 +165,7 @@ // Look up the address of the literal in the SymbolTable for this Symbol* static Symbol** lookup_symbol_addr(Symbol* sym); - // jchar (utf16) version of lookups + // jchar (UTF16) version of lookups static Symbol* lookup_unicode(const jchar* name, int len, TRAPS); static Symbol* lookup_only_unicode(const jchar* name, int len, unsigned int& hash); --- old/src/share/vm/classfile/vmSymbols.cpp 2015-10-30 19:14:50.088475595 +0100 +++ new/src/share/vm/classfile/vmSymbols.cpp 2015-10-30 19:14:50.024475593 +0100 @@ -451,10 +451,26 @@ // the following switch statement. if (!InlineNatives) { switch (id) { - case vmIntrinsics::_indexOf: - case vmIntrinsics::_compareTo: - case vmIntrinsics::_equals: + case vmIntrinsics::_indexOfL: + case vmIntrinsics::_indexOfU: + case vmIntrinsics::_indexOfUL: + case vmIntrinsics::_indexOfIL: + case vmIntrinsics::_indexOfIU: + case vmIntrinsics::_indexOfIUL: + case vmIntrinsics::_indexOfU_char: + case vmIntrinsics::_compareToL: + case vmIntrinsics::_compareToU: + case vmIntrinsics::_compareToLU: + case vmIntrinsics::_compareToUL: + case vmIntrinsics::_equalsL: + case vmIntrinsics::_equalsU: case vmIntrinsics::_equalsC: + case vmIntrinsics::_getCharStringU: + case vmIntrinsics::_putCharStringU: + case vmIntrinsics::_compressStringC: + case vmIntrinsics::_compressStringB: + case vmIntrinsics::_inflateStringC: + case vmIntrinsics::_inflateStringB: case vmIntrinsics::_getAndAddInt: case vmIntrinsics::_getAndAddLong: case vmIntrinsics::_getAndSetInt: @@ -463,6 +479,7 @@ case vmIntrinsics::_loadFence: case vmIntrinsics::_storeFence: case vmIntrinsics::_fullFence: + case vmIntrinsics::_hasNegatives: case vmIntrinsics::_Reference_get: break; default: @@ -645,19 +662,31 @@ // intrinsic mechanism. if (!InlineObjectCopy || !InlineArrayCopy) return true; break; - case vmIntrinsics::_compareTo: + case vmIntrinsics::_compareToL: + case vmIntrinsics::_compareToU: + case vmIntrinsics::_compareToLU: + case vmIntrinsics::_compareToUL: if (!SpecialStringCompareTo) return true; break; - case vmIntrinsics::_indexOf: + case vmIntrinsics::_indexOfL: + case vmIntrinsics::_indexOfU: + case vmIntrinsics::_indexOfUL: + case vmIntrinsics::_indexOfIL: + case vmIntrinsics::_indexOfIU: + case vmIntrinsics::_indexOfIUL: + case vmIntrinsics::_indexOfU_char: if (!SpecialStringIndexOf) return true; break; - case vmIntrinsics::_equals: + case vmIntrinsics::_equalsL: + case vmIntrinsics::_equalsU: if (!SpecialStringEquals) return true; break; + case vmIntrinsics::_equalsB: case vmIntrinsics::_equalsC: if (!SpecialArraysEquals) return true; break; case vmIntrinsics::_encodeISOArray: + case vmIntrinsics::_encodeByteISOArray: if (!SpecialEncodeISOArray) return true; break; case vmIntrinsics::_getCallerClass: @@ -692,6 +721,12 @@ case vmIntrinsics::_subtractExactL: if (!UseMathExactIntrinsics || !InlineMathNatives) return true; break; + case vmIntrinsics::_getCharStringU: + case vmIntrinsics::_putCharStringU: + // Until JDK-8138651 is fixed, we have to rely on a special flag to + // disable these intrinsics for experiments. + if (!StringCharIntrinsics) return true; + break; #endif // COMPILER2 default: return false; --- old/src/share/vm/classfile/vmSymbols.hpp 2015-10-30 19:14:50.328475603 +0100 +++ new/src/share/vm/classfile/vmSymbols.hpp 2015-10-30 19:14:50.260475601 +0100 @@ -53,6 +53,8 @@ template(java_lang_Object, "java/lang/Object") \ template(java_lang_Class, "java/lang/Class") \ template(java_lang_String, "java/lang/String") \ + template(java_lang_StringLatin1, "java/lang/StringLatin1") \ + template(java_lang_StringUTF16, "java/lang/StringUTF16") \ template(java_lang_Thread, "java/lang/Thread") \ template(java_lang_ThreadGroup, "java/lang/ThreadGroup") \ template(java_lang_Cloneable, "java/lang/Cloneable") \ @@ -377,9 +379,9 @@ template(park_event_name, "nativeParkEventPointer") \ template(cache_field_name, "cache") \ template(value_name, "value") \ - template(offset_name, "offset") \ - template(count_name, "count") \ template(hash_name, "hash") \ + template(coder_name, "coder") \ + template(compact_strings_name, "COMPACT_STRINGS") \ template(numberOfLeadingZeros_name, "numberOfLeadingZeros") \ template(numberOfTrailingZeros_name, "numberOfTrailingZeros") \ template(bitCount_name, "bitCount") \ @@ -833,22 +835,66 @@ \ do_intrinsic(_equalsC, java_util_Arrays, equals_name, equalsC_signature, F_S) \ do_signature(equalsC_signature, "([C[C)Z") \ + do_intrinsic(_equalsB, java_util_Arrays, equals_name, equalsB_signature, F_S) \ + do_signature(equalsB_signature, "([B[B)Z") \ \ - do_intrinsic(_compareTo, java_lang_String, compareTo_name, string_int_signature, F_R) \ + do_intrinsic(_compressStringC, java_lang_StringUTF16, compress_name, encodeISOArray_signature, F_S) \ + do_name( compress_name, "compress") \ + do_intrinsic(_compressStringB, java_lang_StringUTF16, compress_name, indexOfI_signature, F_S) \ + do_intrinsic(_inflateStringC, java_lang_StringLatin1, inflate_name, inflateC_signature, F_S) \ + do_name( inflate_name, "inflate") \ + do_signature(inflateC_signature, "([BI[CII)V") \ + do_intrinsic(_inflateStringB, java_lang_StringLatin1, inflate_name, inflateB_signature, F_S) \ + do_signature(inflateB_signature, "([BI[BII)V") \ + do_intrinsic(_toBytesStringU, java_lang_StringUTF16, toBytes_name, toBytesU_signature, F_S) \ + do_name( toBytes_name, "toBytes") \ + do_signature(toBytesU_signature, "([CII)[B") \ + do_intrinsic(_getCharsStringU, java_lang_StringUTF16, getCharsU_name, getCharsU_signature, F_S) \ + do_name( getCharsU_name, "getChars") \ + do_signature(getCharsU_signature, "([BII[CI)V") \ + do_intrinsic(_getCharStringU, java_lang_StringUTF16, getChar_name, getCharStringU_signature, F_S) \ + do_signature(getCharStringU_signature, "([BI)C") \ + do_intrinsic(_putCharStringU, java_lang_StringUTF16, putChar_name, putCharStringU_signature, F_S) \ + do_signature(putCharStringU_signature, "([BII)V") \ + do_intrinsic(_compareToL, java_lang_StringLatin1,compareTo_name, compareTo_indexOf_signature, F_S) \ + do_intrinsic(_compareToU, java_lang_StringUTF16, compareTo_name, compareTo_indexOf_signature, F_S) \ + do_intrinsic(_compareToLU, java_lang_StringLatin1,compareToLU_name, compareTo_indexOf_signature, F_S) \ + do_intrinsic(_compareToUL, java_lang_StringUTF16, compareToUL_name, compareTo_indexOf_signature, F_S) \ + do_signature(compareTo_indexOf_signature, "([B[B)I") \ do_name( compareTo_name, "compareTo") \ - do_intrinsic(_indexOf, java_lang_String, indexOf_name, string_int_signature, F_R) \ + do_name( compareToLU_name, "compareToUTF16") \ + do_name( compareToUL_name, "compareToLatin1") \ + do_intrinsic(_indexOfL, java_lang_StringLatin1,indexOf_name, compareTo_indexOf_signature, F_S) \ + do_intrinsic(_indexOfU, java_lang_StringUTF16, indexOf_name, compareTo_indexOf_signature, F_S) \ + do_intrinsic(_indexOfUL, java_lang_StringUTF16, indexOfUL_name, compareTo_indexOf_signature, F_S) \ + do_intrinsic(_indexOfIL, java_lang_StringLatin1,indexOf_name, indexOfI_signature, F_S) \ + do_intrinsic(_indexOfIU, java_lang_StringUTF16, indexOf_name, indexOfI_signature, F_S) \ + do_intrinsic(_indexOfIUL, java_lang_StringUTF16, indexOfUL_name, indexOfI_signature, F_S) \ + do_intrinsic(_indexOfU_char, java_lang_StringUTF16, indexOfChar_name, indexOfChar_signature, F_S) \ do_name( indexOf_name, "indexOf") \ - do_intrinsic(_equals, java_lang_String, equals_name, object_boolean_signature, F_R) \ + do_name( indexOfChar_name, "indexOfChar") \ + do_name( indexOfUL_name, "indexOfLatin1") \ + do_signature(indexOfI_signature, "([BI[BII)I") \ + do_signature(indexOfChar_signature, "([BIII)I") \ + do_intrinsic(_equalsL, java_lang_StringLatin1,equals_name, equalsB_signature, F_S) \ + do_intrinsic(_equalsU, java_lang_StringUTF16, equals_name, equalsB_signature, F_S) \ \ do_class(java_nio_Buffer, "java/nio/Buffer") \ do_intrinsic(_checkIndex, java_nio_Buffer, checkIndex_name, int_int_signature, F_R) \ do_name( checkIndex_name, "checkIndex") \ \ + do_class(java_lang_StringCoding, "java/lang/StringCoding") \ + do_intrinsic(_hasNegatives, java_lang_StringCoding, hasNegatives_name, hasNegatives_signature, F_S) \ + do_name( hasNegatives_name, "hasNegatives") \ + do_signature(hasNegatives_signature, "([BII)Z") \ + \ do_class(sun_nio_cs_iso8859_1_Encoder, "sun/nio/cs/ISO_8859_1$Encoder") \ do_intrinsic(_encodeISOArray, sun_nio_cs_iso8859_1_Encoder, encodeISOArray_name, encodeISOArray_signature, F_S) \ do_name( encodeISOArray_name, "implEncodeISOArray") \ do_signature(encodeISOArray_signature, "([CI[BII)I") \ \ + do_intrinsic(_encodeByteISOArray, java_lang_StringCoding, encodeISOArray_name, indexOfI_signature, F_S) \ + \ do_class(java_math_BigInteger, "java/math/BigInteger") \ do_intrinsic(_multiplyToLen, java_math_BigInteger, multiplyToLen_name, multiplyToLen_signature, F_S) \ do_name( multiplyToLen_name, "implMultiplyToLen") \ --- old/src/share/vm/gc/g1/g1StringDedupTable.cpp 2015-10-30 19:14:50.584475611 +0100 +++ new/src/share/vm/gc/g1/g1StringDedupTable.cpp 2015-10-30 19:14:50.520475609 +0100 @@ -198,10 +198,11 @@ _table = new G1StringDedupTable(_min_size); } -void G1StringDedupTable::add(typeArrayOop value, unsigned int hash, G1StringDedupEntry** list) { +void G1StringDedupTable::add(typeArrayOop value, bool latin1, unsigned int hash, G1StringDedupEntry** list) { G1StringDedupEntry* entry = _entry_cache->alloc(); entry->set_obj(value); entry->set_hash(hash); + entry->set_latin1(latin1); entry->set_next(*list); *list = entry; _entries++; @@ -226,15 +227,15 @@ bool G1StringDedupTable::equals(typeArrayOop value1, typeArrayOop value2) { return (value1 == value2 || (value1->length() == value2->length() && - (!memcmp(value1->base(T_CHAR), - value2->base(T_CHAR), - value1->length() * sizeof(jchar))))); + (!memcmp(value1->base(T_BYTE), + value2->base(T_BYTE), + value1->length() * sizeof(jbyte))))); } -typeArrayOop G1StringDedupTable::lookup(typeArrayOop value, unsigned int hash, +typeArrayOop G1StringDedupTable::lookup(typeArrayOop value, bool latin1, unsigned int hash, G1StringDedupEntry** list, uintx &count) { for (G1StringDedupEntry* entry = *list; entry != NULL; entry = entry->next()) { - if (entry->hash() == hash) { + if (entry->hash() == hash && entry->latin1() == latin1) { typeArrayOop existing_value = entry->obj(); if (equals(value, existing_value)) { // Match found @@ -248,13 +249,13 @@ return NULL; } -typeArrayOop G1StringDedupTable::lookup_or_add_inner(typeArrayOop value, unsigned int hash) { +typeArrayOop G1StringDedupTable::lookup_or_add_inner(typeArrayOop value, bool latin1, unsigned int hash) { size_t index = hash_to_index(hash); G1StringDedupEntry** list = bucket(index); uintx count = 0; // Lookup in list - typeArrayOop existing_value = lookup(value, hash, list, count); + typeArrayOop existing_value = lookup(value, latin1, hash, list, count); // Check if rehash is needed if (count > _rehash_threshold) { @@ -263,7 +264,7 @@ if (existing_value == NULL) { // Not found, add new entry - add(value, hash, list); + add(value, latin1, hash, list); // Update statistics _entries_added++; @@ -272,15 +273,24 @@ return existing_value; } -unsigned int G1StringDedupTable::hash_code(typeArrayOop value) { +unsigned int G1StringDedupTable::hash_code(typeArrayOop value, bool latin1) { unsigned int hash; int length = value->length(); - const jchar* data = (jchar*)value->base(T_CHAR); - - if (use_java_hash()) { - hash = java_lang_String::hash_code(data, length); + if (latin1) { + const jbyte* data = (jbyte*)value->base(T_BYTE); + if (use_java_hash()) { + hash = java_lang_String::hash_code(data, length); + } else { + hash = AltHashing::murmur3_32(_table->_hash_seed, data, length); + } } else { - hash = AltHashing::murmur3_32(_table->_hash_seed, data, length); + length /= sizeof(jchar) / sizeof(jbyte); // Convert number of bytes to number of chars + const jchar* data = (jchar*)value->base(T_CHAR); + if (use_java_hash()) { + hash = java_lang_String::hash_code(data, length); + } else { + hash = AltHashing::murmur3_32(_table->_hash_seed, data, length); + } } return hash; @@ -299,6 +309,7 @@ return; } + bool latin1 = java_lang_String::is_latin1(java_string); unsigned int hash = 0; if (use_java_hash()) { @@ -308,7 +319,7 @@ if (hash == 0) { // Compute hash - hash = hash_code(value); + hash = hash_code(value, latin1); stat.inc_hashed(); if (use_java_hash() && hash != 0) { @@ -317,7 +328,7 @@ } } - typeArrayOop existing_value = lookup_or_add(value, hash); + typeArrayOop existing_value = lookup_or_add(value, latin1, hash); if (existing_value == value) { // Same value, already known stat.inc_known(); @@ -459,7 +470,8 @@ // destination partitions. finish_rehash() will do a single // threaded transfer of all entries. typeArrayOop value = (typeArrayOop)*p; - unsigned int hash = hash_code(value); + bool latin1 = (*entry)->latin1(); + unsigned int hash = hash_code(value, latin1); (*entry)->set_hash(hash); } @@ -523,7 +535,8 @@ guarantee(G1CollectedHeap::heap()->is_in_reserved(value), "Object must be on the heap"); guarantee(!value->is_forwarded(), "Object must not be forwarded"); guarantee(value->is_typeArray(), "Object must be a typeArrayOop"); - unsigned int hash = hash_code(value); + bool latin1 = (*entry)->latin1(); + unsigned int hash = hash_code(value, latin1); guarantee((*entry)->hash() == hash, "Table entry has inorrect hash"); guarantee(_table->hash_to_index(hash) == bucket, "Table entry has incorrect index"); entry = (*entry)->next_addr(); @@ -536,10 +549,12 @@ G1StringDedupEntry** entry1 = _table->bucket(bucket); while (*entry1 != NULL) { typeArrayOop value1 = (*entry1)->obj(); + bool latin1_1 = (*entry1)->latin1(); G1StringDedupEntry** entry2 = (*entry1)->next_addr(); while (*entry2 != NULL) { typeArrayOop value2 = (*entry2)->obj(); - guarantee(!equals(value1, value2), "Table entries must not have identical arrays"); + bool latin1_2 = (*entry2)->latin1(); + guarantee(latin1_1 != latin1_2 || !equals(value1, value2), "Table entries must not have identical arrays"); entry2 = (*entry2)->next_addr(); } entry1 = (*entry1)->next_addr(); --- old/src/share/vm/gc/g1/g1StringDedupTable.hpp 2015-10-30 19:14:50.832475619 +0100 +++ new/src/share/vm/gc/g1/g1StringDedupTable.hpp 2015-10-30 19:14:50.764475617 +0100 @@ -40,12 +40,14 @@ private: G1StringDedupEntry* _next; unsigned int _hash; + bool _latin1; typeArrayOop _obj; public: G1StringDedupEntry() : _next(NULL), _hash(0), + _latin1(false), _obj(NULL) { } @@ -69,6 +71,14 @@ _hash = hash; } + bool latin1() { + return _latin1; + } + + void set_latin1(bool latin1) { + _latin1 = latin1; + } + typeArrayOop obj() { return _obj; } @@ -152,7 +162,7 @@ } // Adds a new table entry to the given hash bucket. - void add(typeArrayOop value, unsigned int hash, G1StringDedupEntry** list); + void add(typeArrayOop value, bool latin1, unsigned int hash, G1StringDedupEntry** list); // Removes the given table entry from the table. void remove(G1StringDedupEntry** pentry, uint worker_id); @@ -162,20 +172,20 @@ // Returns an existing character array in the given hash bucket, or NULL // if no matching character array exists. - typeArrayOop lookup(typeArrayOop value, unsigned int hash, + typeArrayOop lookup(typeArrayOop value, bool latin1, unsigned int hash, G1StringDedupEntry** list, uintx &count); // Returns an existing character array in the table, or inserts a new // table entry if no matching character array exists. - typeArrayOop lookup_or_add_inner(typeArrayOop value, unsigned int hash); + typeArrayOop lookup_or_add_inner(typeArrayOop value, bool latin1, unsigned int hash); // Thread safe lookup or add of table entry - static typeArrayOop lookup_or_add(typeArrayOop value, unsigned int hash) { + static typeArrayOop lookup_or_add(typeArrayOop value, bool latin1, unsigned int hash) { // Protect the table from concurrent access. Also note that this lock // acts as a fence for _table, which could have been replaced by a new // instance if the table was resized or rehashed. MutexLockerEx ml(StringDedupTable_lock, Mutex::_no_safepoint_check_flag); - return _table->lookup_or_add_inner(value, hash); + return _table->lookup_or_add_inner(value, latin1, hash); } // Returns true if the hashtable is currently using a Java compatible @@ -188,7 +198,7 @@ // Computes the hash code for the given character array, using the // currently active hash function and hash seed. - static unsigned int hash_code(typeArrayOop value); + static unsigned int hash_code(typeArrayOop value, bool latin1); static uintx unlink_or_oops_do(G1StringDedupUnlinkOrOopsDoClosure* cl, size_t partition_begin, --- old/src/share/vm/memory/filemap.cpp 2015-10-30 19:14:51.076475627 +0100 +++ new/src/share/vm/memory/filemap.cpp 2015-10-30 19:14:51.012475625 +0100 @@ -168,6 +168,7 @@ _version = _current_version; _alignment = alignment; _obj_alignment = ObjectAlignmentInBytes; + _compact_strings = CompactStrings; _narrow_oop_mode = Universe::narrow_oop_mode(); _narrow_oop_shift = Universe::narrow_oop_shift(); _max_heap_size = MaxHeapSize; @@ -900,6 +901,13 @@ _obj_alignment, ObjectAlignmentInBytes); return false; } + if (_compact_strings != CompactStrings) { + FileMapInfo::fail_continue("The shared archive file's CompactStrings setting (%s)" + " does not equal the current CompactStrings setting (%s).", + _compact_strings ? "enabled" : "disabled", + CompactStrings ? "enabled" : "disabled"); + return false; + } return true; } --- old/src/share/vm/memory/filemap.hpp 2015-10-30 19:14:51.316475634 +0100 +++ new/src/share/vm/memory/filemap.hpp 2015-10-30 19:14:51.252475632 +0100 @@ -95,6 +95,7 @@ size_t _alignment; // how shared archive should be aligned int _obj_alignment; // value of ObjectAlignmentInBytes int _narrow_oop_shift; // compressed oop encoding shift + bool _compact_strings; // value of CompactStrings uintx _max_heap_size; // java max heap size during dumping Universe::NARROW_OOP_MODE _narrow_oop_mode; // compressed oop encoding mode int _narrow_klass_shift; // save narrow klass base and shift --- old/src/share/vm/memory/metaspaceShared.cpp 2015-10-30 19:14:51.560475642 +0100 +++ new/src/share/vm/memory/metaspaceShared.cpp 2015-10-30 19:14:51.492475640 +0100 @@ -540,7 +540,7 @@ NOT_PRODUCT(SystemDictionary::verify();) - // Copy the the symbol table, string table, and the system dictionary to the shared + // Copy the symbol table, string table, and the system dictionary to the shared // space in usable form. Copy the hashtable // buckets first [read-write], then copy the linked lists of entries // [read-only]. --- old/src/share/vm/oops/instanceKlass.cpp 2015-10-30 19:14:51.800475650 +0100 +++ new/src/share/vm/oops/instanceKlass.cpp 2015-10-30 19:14:51.736475648 +0100 @@ -2921,12 +2921,10 @@ if (this == SystemDictionary::String_klass()) { typeArrayOop value = java_lang_String::value(obj); - juint offset = java_lang_String::offset(obj); juint length = java_lang_String::length(obj); if (value != NULL && value->is_typeArray() && - offset <= (juint) value->length() && - offset + length <= (juint) value->length()) { + length <= (juint) value->length()) { st->print(BULLET"string: "); java_lang_String::print(obj, st); st->cr(); --- old/src/share/vm/oops/typeArrayOop.hpp 2015-10-30 19:14:52.048475658 +0100 +++ new/src/share/vm/oops/typeArrayOop.hpp 2015-10-30 19:14:51.980475655 +0100 @@ -48,47 +48,47 @@ public: jbyte* byte_at_addr(int which) const { - assert(is_within_bounds(which), "index out of bounds"); + assert(is_within_bounds(which), "index %d out of bounds %d", which, length()); return &byte_base()[which]; } jboolean* bool_at_addr(int which) const { - assert(is_within_bounds(which), "index out of bounds"); + assert(is_within_bounds(which), "index %d out of bounds %d", which, length()); return &bool_base()[which]; } jchar* char_at_addr(int which) const { - assert(is_within_bounds(which), "index out of bounds"); + assert(is_within_bounds(which), "index %d out of bounds %d", which, length()); return &char_base()[which]; } jint* int_at_addr(int which) const { - assert(is_within_bounds(which), "index out of bounds"); + assert(is_within_bounds(which), "index %d out of bounds %d", which, length()); return &int_base()[which]; } jshort* short_at_addr(int which) const { - assert(is_within_bounds(which), "index out of bounds"); + assert(is_within_bounds(which), "index %d out of bounds %d", which, length()); return &short_base()[which]; } jushort* ushort_at_addr(int which) const { // for field descriptor arrays - assert(is_within_bounds(which), "index out of bounds"); + assert(is_within_bounds(which), "index %d out of bounds %d", which, length()); return (jushort*) &short_base()[which]; } jlong* long_at_addr(int which) const { - assert(is_within_bounds(which), "index out of bounds"); + assert(is_within_bounds(which), "index %d out of bounds %d", which, length()); return &long_base()[which]; } jfloat* float_at_addr(int which) const { - assert(is_within_bounds(which), "index out of bounds"); + assert(is_within_bounds(which), "index %d out of bounds %d", which, length()); return &float_base()[which]; } jdouble* double_at_addr(int which) const { - assert(is_within_bounds(which), "index out of bounds"); + assert(is_within_bounds(which), "index %d out of bounds %d", which, length()); return &double_base()[which]; } --- old/src/share/vm/opto/c2compiler.cpp 2015-10-30 19:14:52.296475665 +0100 +++ new/src/share/vm/opto/c2compiler.cpp 2015-10-30 19:14:52.232475663 +0100 @@ -180,12 +180,25 @@ } switch (id) { - case vmIntrinsics::_compareTo: + case vmIntrinsics::_compressStringC: + case vmIntrinsics::_compressStringB: + if (!Matcher::has_match_rule(Op_StrCompressedCopy)) return false; + break; + case vmIntrinsics::_inflateStringC: + case vmIntrinsics::_inflateStringB: + if (!Matcher::has_match_rule(Op_StrInflatedCopy)) return false; + break; + case vmIntrinsics::_compareToL: + case vmIntrinsics::_compareToU: + case vmIntrinsics::_compareToLU: + case vmIntrinsics::_compareToUL: if (!Matcher::match_rule_supported(Op_StrComp)) return false; break; - case vmIntrinsics::_equals: + case vmIntrinsics::_equalsL: + case vmIntrinsics::_equalsU: if (!Matcher::match_rule_supported(Op_StrEquals)) return false; break; + case vmIntrinsics::_equalsB: case vmIntrinsics::_equalsC: if (!Matcher::match_rule_supported(Op_AryEq)) return false; break; @@ -193,8 +206,12 @@ if (StubRoutines::unsafe_arraycopy() == NULL) return false; break; case vmIntrinsics::_encodeISOArray: + case vmIntrinsics::_encodeByteISOArray: if (!Matcher::match_rule_supported(Op_EncodeISOArray)) return false; break; + case vmIntrinsics::_hasNegatives: + if (!Matcher::match_rule_supported(Op_HasNegatives)) return false; + break; case vmIntrinsics::_bitCount_i: if (!Matcher::match_rule_supported(Op_PopCountI)) return false; break; @@ -301,7 +318,17 @@ case vmIntrinsics::_min: case vmIntrinsics::_max: case vmIntrinsics::_arraycopy: - case vmIntrinsics::_indexOf: + case vmIntrinsics::_indexOfL: + case vmIntrinsics::_indexOfU: + case vmIntrinsics::_indexOfUL: + case vmIntrinsics::_indexOfIL: + case vmIntrinsics::_indexOfIU: + case vmIntrinsics::_indexOfIUL: + case vmIntrinsics::_indexOfU_char: + case vmIntrinsics::_toBytesStringU: + case vmIntrinsics::_getCharsStringU: + case vmIntrinsics::_getCharStringU: + case vmIntrinsics::_putCharStringU: case vmIntrinsics::_getObject: case vmIntrinsics::_getBoolean: case vmIntrinsics::_getByte: --- old/src/share/vm/opto/classes.hpp 2015-10-30 19:14:52.524475673 +0100 +++ new/src/share/vm/opto/classes.hpp 2015-10-30 19:14:52.460475671 +0100 @@ -135,6 +135,7 @@ macro(FastUnlock) macro(Goto) macro(Halt) +macro(HasNegatives) macro(If) macro(IfFalse) macro(IfTrue) @@ -246,8 +247,11 @@ macro(StoreN) macro(StoreNKlass) macro(StrComp) +macro(StrCompressedCopy) macro(StrEquals) macro(StrIndexOf) +macro(StrIndexOfChar) +macro(StrInflatedCopy) macro(SubD) macro(SubF) macro(SubI) --- old/src/share/vm/opto/escape.cpp 2015-10-30 19:14:52.764475680 +0100 +++ new/src/share/vm/opto/escape.cpp 2015-10-30 19:14:52.696475678 +0100 @@ -560,9 +560,13 @@ break; } case Op_AryEq: + case Op_HasNegatives: case Op_StrComp: case Op_StrEquals: case Op_StrIndexOf: + case Op_StrIndexOfChar: + case Op_StrInflatedCopy: + case Op_StrCompressedCopy: case Op_EncodeISOArray: { add_local_var(n, PointsToNode::ArgEscape); delayed_worklist->push(n); // Process it later. @@ -743,11 +747,15 @@ ELSE_FAIL("Op_StoreP"); } case Op_AryEq: + case Op_HasNegatives: case Op_StrComp: case Op_StrEquals: case Op_StrIndexOf: + case Op_StrIndexOfChar: + case Op_StrInflatedCopy: + case Op_StrCompressedCopy: case Op_EncodeISOArray: { - // char[] arrays passed to string intrinsic do not escape but + // char[]/byte[] arrays passed to string intrinsic do not escape but // they are not scalar replaceable. Adjust escape state for them. // Start from in(2) edge since in(1) is memory edge. for (uint i = 2; i < n->req(); i++) { @@ -2722,17 +2730,34 @@ if (mem->is_LoadStore()) { adr = mem->in(MemNode::Address); } else { - assert(mem->Opcode() == Op_EncodeISOArray, "sanity"); + assert(mem->Opcode() == Op_EncodeISOArray || + mem->Opcode() == Op_StrCompressedCopy, "sanity"); adr = mem->in(3); // Memory edge corresponds to destination array } const Type *at = igvn->type(adr); if (at != Type::TOP) { - assert (at->isa_ptr() != NULL, "pointer type required."); + assert(at->isa_ptr() != NULL, "pointer type required."); int idx = C->get_alias_index(at->is_ptr()); - assert(idx != alias_idx, "Object is not scalar replaceable if a LoadStore node access its field"); - break; + if (idx == alias_idx) { + // Assert in debug mode + assert(false, "Object is not scalar replaceable if a LoadStore node accesses its field"); + break; // In product mode return SCMemProj node + } } result = mem->in(MemNode::Memory); + } else if (result->Opcode() == Op_StrInflatedCopy) { + Node* adr = result->in(3); // Memory edge corresponds to destination array + const Type *at = igvn->type(adr); + if (at != Type::TOP) { + assert(at->isa_ptr() != NULL, "pointer type required."); + int idx = C->get_alias_index(at->is_ptr()); + if (idx == alias_idx) { + // Assert in debug mode + assert(false, "Object is not scalar replaceable if a StrInflatedCopy node accesses its field"); + break; // In product mode return SCMemProj node + } + } + result = result->in(MemNode::Memory); } } if (result->is_Phi()) { @@ -3096,10 +3121,15 @@ } } else { uint op = use->Opcode(); - if (!(op == Op_CmpP || op == Op_Conv2B || + if ((use->in(MemNode::Memory) == n) && + (op == Op_StrCompressedCopy || op == Op_StrInflatedCopy)) { + // They overwrite memory edge corresponding to destination array, + memnode_worklist.append_if_missing(use); + } else if (!(op == Op_CmpP || op == Op_Conv2B || op == Op_CastP2X || op == Op_StoreCM || - op == Op_FastLock || op == Op_AryEq || op == Op_StrComp || - op == Op_StrEquals || op == Op_StrIndexOf)) { + op == Op_FastLock || op == Op_AryEq || op == Op_StrComp || op == Op_HasNegatives || + op == Op_StrCompressedCopy || op == Op_StrInflatedCopy || + op == Op_StrEquals || op == Op_StrIndexOf || op == Op_StrIndexOfChar)) { n->dump(); use->dump(); assert(false, "EA: missing allocation reference path"); @@ -3161,7 +3191,8 @@ n = n->as_MemBar()->proj_out(TypeFunc::Memory); if (n == NULL) continue; - } else if (n->Opcode() == Op_EncodeISOArray) { + } else if (n->Opcode() == Op_StrCompressedCopy || + n->Opcode() == Op_EncodeISOArray) { // get the memory projection n = n->find_out_with(Op_SCMemProj); assert(n->Opcode() == Op_SCMemProj, "memory projection required"); @@ -3216,11 +3247,16 @@ } } else { uint op = use->Opcode(); - if (!(op == Op_StoreCM || + if ((use->in(MemNode::Memory) == n) && + (op == Op_StrCompressedCopy || op == Op_StrInflatedCopy)) { + // They overwrite memory edge corresponding to destination array, + memnode_worklist.append_if_missing(use); + } else if (!(op == Op_StoreCM || (op == Op_CallLeaf && use->as_CallLeaf()->_name != NULL && strcmp(use->as_CallLeaf()->_name, "g1_wb_pre") == 0) || - op == Op_AryEq || op == Op_StrComp || - op == Op_StrEquals || op == Op_StrIndexOf)) { + op == Op_AryEq || op == Op_StrComp || op == Op_HasNegatives || + op == Op_StrCompressedCopy || op == Op_StrInflatedCopy || + op == Op_StrEquals || op == Op_StrIndexOf || op == Op_StrIndexOfChar)) { n->dump(); use->dump(); assert(false, "EA: missing memory path"); --- old/src/share/vm/opto/gcm.cpp 2015-10-30 19:14:53.024475689 +0100 +++ new/src/share/vm/opto/gcm.cpp 2015-10-30 19:14:52.964475687 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -504,8 +504,12 @@ "String equals is a 'load' that does not conflict with any stores"); assert(load_alias_idx || (load->is_Mach() && load->as_Mach()->ideal_Opcode() == Op_StrIndexOf), "String indexOf is a 'load' that does not conflict with any stores"); + assert(load_alias_idx || (load->is_Mach() && load->as_Mach()->ideal_Opcode() == Op_StrIndexOfChar), + "String indexOfChar is a 'load' that does not conflict with any stores"); assert(load_alias_idx || (load->is_Mach() && load->as_Mach()->ideal_Opcode() == Op_AryEq), - "Arrays equals is a 'load' that do not conflict with any stores"); + "Arrays equals is a 'load' that does not conflict with any stores"); + assert(load_alias_idx || (load->is_Mach() && load->as_Mach()->ideal_Opcode() == Op_HasNegatives), + "HasNegatives is a 'load' that does not conflict with any stores"); if (!C->alias_type(load_alias_idx)->is_rewritable()) { // It is impossible to spoil this load by putting stores before it, --- old/src/share/vm/opto/graphKit.cpp 2015-10-30 19:14:53.276475697 +0100 +++ new/src/share/vm/opto/graphKit.cpp 2015-10-30 19:14:53.208475694 +0100 @@ -4266,35 +4266,11 @@ #undef __ - -Node* GraphKit::load_String_offset(Node* ctrl, Node* str) { - if (java_lang_String::has_offset_field()) { - int offset_offset = java_lang_String::offset_offset_in_bytes(); - const TypeInstPtr* string_type = TypeInstPtr::make(TypePtr::NotNull, C->env()->String_klass(), - false, NULL, 0); - const TypePtr* offset_field_type = string_type->add_offset(offset_offset); - int offset_field_idx = C->get_alias_index(offset_field_type); - return make_load(ctrl, - basic_plus_adr(str, str, offset_offset), - TypeInt::INT, T_INT, offset_field_idx, MemNode::unordered); - } else { - return intcon(0); - } -} - Node* GraphKit::load_String_length(Node* ctrl, Node* str) { - if (java_lang_String::has_count_field()) { - int count_offset = java_lang_String::count_offset_in_bytes(); - const TypeInstPtr* string_type = TypeInstPtr::make(TypePtr::NotNull, C->env()->String_klass(), - false, NULL, 0); - const TypePtr* count_field_type = string_type->add_offset(count_offset); - int count_field_idx = C->get_alias_index(count_field_type); - return make_load(ctrl, - basic_plus_adr(str, str, count_offset), - TypeInt::INT, T_INT, count_field_idx, MemNode::unordered); - } else { - return load_array_length(load_String_value(ctrl, str)); - } + Node* len = load_array_length(load_String_value(ctrl, str)); + Node* coder = load_String_coder(ctrl, str); + // Divide length by 2 if coder is UTF16 + return _gvn.transform(new RShiftINode(len, coder)); } Node* GraphKit::load_String_value(Node* ctrl, Node* str) { @@ -4302,9 +4278,9 @@ const TypeInstPtr* string_type = TypeInstPtr::make(TypePtr::NotNull, C->env()->String_klass(), false, NULL, 0); const TypePtr* value_field_type = string_type->add_offset(value_offset); - const TypeAryPtr* value_type = TypeAryPtr::make(TypePtr::NotNull, - TypeAry::make(TypeInt::CHAR,TypeInt::POS), - ciTypeArrayKlass::make(T_CHAR), true, 0); + const TypeAryPtr* value_type = TypeAryPtr::make(TypePtr::NotNull, + TypeAry::make(TypeInt::BYTE, TypeInt::POS), + ciTypeArrayKlass::make(T_BYTE), true, 0); int value_field_idx = C->get_alias_index(value_field_type); Node* load = make_load(ctrl, basic_plus_adr(str, str, value_offset), value_type, T_OBJECT, value_field_idx, MemNode::unordered); @@ -4315,14 +4291,21 @@ return load; } -void GraphKit::store_String_offset(Node* ctrl, Node* str, Node* value) { - int offset_offset = java_lang_String::offset_offset_in_bytes(); - const TypeInstPtr* string_type = TypeInstPtr::make(TypePtr::NotNull, C->env()->String_klass(), - false, NULL, 0); - const TypePtr* offset_field_type = string_type->add_offset(offset_offset); - int offset_field_idx = C->get_alias_index(offset_field_type); - store_to_memory(ctrl, basic_plus_adr(str, offset_offset), - value, T_INT, offset_field_idx, MemNode::unordered); +Node* GraphKit::load_String_coder(Node* ctrl, Node* str) { + if (java_lang_String::has_coder_field()) { + if (!CompactStrings) { + return intcon(java_lang_String::CODER_UTF16); + } + int coder_offset = java_lang_String::coder_offset_in_bytes(); + const TypeInstPtr* string_type = TypeInstPtr::make(TypePtr::NotNull, C->env()->String_klass(), + false, NULL, 0); + const TypePtr* coder_field_type = string_type->add_offset(coder_offset); + int coder_field_idx = C->get_alias_index(coder_field_type); + return make_load(ctrl, basic_plus_adr(str, str, coder_offset), + TypeInt::BYTE, T_BYTE, coder_field_idx, MemNode::unordered); + } else { + return intcon(0); // false + } } void GraphKit::store_String_value(Node* ctrl, Node* str, Node* value) { @@ -4330,19 +4313,76 @@ const TypeInstPtr* string_type = TypeInstPtr::make(TypePtr::NotNull, C->env()->String_klass(), false, NULL, 0); const TypePtr* value_field_type = string_type->add_offset(value_offset); - store_oop_to_object(ctrl, str, basic_plus_adr(str, value_offset), value_field_type, - value, TypeAryPtr::CHARS, T_OBJECT, MemNode::unordered); + value, TypeAryPtr::BYTES, T_OBJECT, MemNode::unordered); } -void GraphKit::store_String_length(Node* ctrl, Node* str, Node* value) { - int count_offset = java_lang_String::count_offset_in_bytes(); +void GraphKit::store_String_coder(Node* ctrl, Node* str, Node* value) { + int coder_offset = java_lang_String::coder_offset_in_bytes(); const TypeInstPtr* string_type = TypeInstPtr::make(TypePtr::NotNull, C->env()->String_klass(), false, NULL, 0); - const TypePtr* count_field_type = string_type->add_offset(count_offset); - int count_field_idx = C->get_alias_index(count_field_type); - store_to_memory(ctrl, basic_plus_adr(str, count_offset), - value, T_INT, count_field_idx, MemNode::unordered); + const TypePtr* coder_field_type = string_type->add_offset(coder_offset); + int coder_field_idx = C->get_alias_index(coder_field_type); + store_to_memory(ctrl, basic_plus_adr(str, coder_offset), + value, T_BYTE, coder_field_idx, MemNode::unordered); +} + +Node* GraphKit::compress_string(Node* src, Node* dst, Node* count) { + assert(Matcher::match_rule_supported(Op_StrCompressedCopy), "Intrinsic not supported"); + uint idx = C->get_alias_index(TypeAryPtr::BYTES); + StrCompressedCopyNode* str = new StrCompressedCopyNode(control(), memory(idx), src, dst, count); + Node* res_mem = _gvn.transform(new SCMemProjNode(str)); + set_memory(res_mem, idx); + return str; +} + +void GraphKit::inflate_string(Node* src, Node* dst, Node* count) { + assert(Matcher::match_rule_supported(Op_StrInflatedCopy), "Intrinsic not supported"); + uint idx = C->get_alias_index(TypeAryPtr::BYTES); + StrInflatedCopyNode* str = new StrInflatedCopyNode(control(), memory(idx), src, dst, count); + set_memory(_gvn.transform(str), idx); +} + +void GraphKit::inflate_string_slow(Node* src, Node* dst, Node* start, Node* count) { + /** + * int i_char = start; + * for (int i_byte = 0; i_byte < count; i_byte++) { + * dst[i_char++] = (char)(src[i_byte] & 0xff); + * } + */ + add_predicate(); + RegionNode* head = new RegionNode(3); + head->init_req(1, control()); + gvn().set_type(head, Type::CONTROL); + record_for_igvn(head); + + Node* i_byte = new PhiNode(head, TypeInt::INT); + i_byte->init_req(1, intcon(0)); + gvn().set_type(i_byte, TypeInt::INT); + record_for_igvn(i_byte); + + Node* i_char = new PhiNode(head, TypeInt::INT); + i_char->init_req(1, start); + gvn().set_type(i_char, TypeInt::INT); + record_for_igvn(i_char); + + Node* mem = PhiNode::make(head, memory(TypeAryPtr::BYTES), Type::MEMORY, TypeAryPtr::BYTES); + gvn().set_type(mem, Type::MEMORY); + record_for_igvn(mem); + set_control(head); + set_memory(mem, TypeAryPtr::BYTES); + Node* ch = load_array_element(control(), src, i_byte, TypeAryPtr::BYTES); + Node* st = store_to_memory(control(), array_element_address(dst, i_char, T_BYTE), + AndI(ch, intcon(0xff)), T_CHAR, TypeAryPtr::BYTES, MemNode::unordered); + + IfNode* iff = create_and_map_if(head, Bool(CmpI(i_byte, count), BoolTest::lt), PROB_FAIR, COUNT_UNKNOWN); + head->init_req(2, IfTrue(iff)); + mem->init_req(2, st); + i_byte->init_req(2, AddI(i_byte, intcon(1))); + i_char->init_req(2, AddI(i_char, intcon(2))); + + set_control(IfFalse(iff)); + set_memory(st, TypeAryPtr::BYTES); } Node* GraphKit::cast_array_to_stable(Node* ary, const TypeAryPtr* ary_type) { --- old/src/share/vm/opto/graphKit.hpp 2015-10-30 19:14:53.544475705 +0100 +++ new/src/share/vm/opto/graphKit.hpp 2015-10-30 19:14:53.476475703 +0100 @@ -864,12 +864,14 @@ bool deoptimize_on_exception = false); // java.lang.String helpers - Node* load_String_offset(Node* ctrl, Node* str); Node* load_String_length(Node* ctrl, Node* str); Node* load_String_value(Node* ctrl, Node* str); - void store_String_offset(Node* ctrl, Node* str, Node* value); - void store_String_length(Node* ctrl, Node* str, Node* value); + Node* load_String_coder(Node* ctrl, Node* str); void store_String_value(Node* ctrl, Node* str, Node* value); + void store_String_coder(Node* ctrl, Node* str, Node* value); + Node* compress_string(Node* src, Node* dst, Node* count); + void inflate_string(Node* src, Node* dst, Node* count); + void inflate_string_slow(Node* src, Node* dst, Node* start, Node* count); // Handy for making control flow IfNode* create_and_map_if(Node* ctrl, Node* tst, float prob, float cnt) { --- old/src/share/vm/opto/idealKit.cpp 2015-10-30 19:14:53.796475713 +0100 +++ new/src/share/vm/opto/idealKit.cpp 2015-10-30 19:14:53.724475711 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 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 @@ -530,7 +530,6 @@ "call node must be constructed correctly"); } - void IdealKit::make_leaf_call_no_fp(const TypeFunc *slow_call_type, address slow_call, const char *leaf_name, --- old/src/share/vm/opto/idealKit.hpp 2015-10-30 19:14:54.056475721 +0100 +++ new/src/share/vm/opto/idealKit.hpp 2015-10-30 19:14:53.988475719 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 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 @@ -183,6 +183,7 @@ Node* AddI(Node* l, Node* r) { return transform(new AddINode(l, r)); } Node* SubI(Node* l, Node* r) { return transform(new SubINode(l, r)); } Node* AndI(Node* l, Node* r) { return transform(new AndINode(l, r)); } + Node* OrI(Node* l, Node* r) { return transform(new OrINode(l, r)); } Node* MaxI(Node* l, Node* r) { return transform(new MaxINode(l, r)); } Node* LShiftI(Node* l, Node* r) { return transform(new LShiftINode(l, r)); } Node* CmpI(Node* l, Node* r) { return transform(new CmpINode(l, r)); } @@ -256,7 +257,6 @@ Node* parm1, Node* parm2, Node* parm3); - }; #endif // SHARE_VM_OPTO_IDEALKIT_HPP --- old/src/share/vm/opto/intrinsicnode.cpp 2015-10-30 19:14:54.300475729 +0100 +++ new/src/share/vm/opto/intrinsicnode.cpp 2015-10-30 19:14:54.236475727 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -36,7 +36,7 @@ //------------------------------Ideal------------------------------------------ // Return a node which is more "ideal" than the current node. Strip out // control copies -Node *StrIntrinsicNode::Ideal(PhaseGVN *phase, bool can_reshape) { +Node* StrIntrinsicNode::Ideal(PhaseGVN* phase, bool can_reshape) { if (remove_dead_region(phase, can_reshape)) return this; // Don't bother trying to transform a dead node if (in(0) && in(0)->is_top()) return NULL; @@ -55,11 +55,29 @@ } //------------------------------Value------------------------------------------ -const Type *StrIntrinsicNode::Value( PhaseTransform *phase ) const { +const Type* StrIntrinsicNode::Value(PhaseTransform* phase) const { if (in(0) && phase->type(in(0)) == Type::TOP) return Type::TOP; return bottom_type(); } +uint StrIntrinsicNode::size_of() const { return sizeof(*this); } + +//============================================================================= +//------------------------------Ideal------------------------------------------ +// Return a node which is more "ideal" than the current node. Strip out +// control copies +Node* StrCompressedCopyNode::Ideal(PhaseGVN* phase, bool can_reshape) { + return remove_dead_region(phase, can_reshape) ? this : NULL; +} + +//============================================================================= +//------------------------------Ideal------------------------------------------ +// Return a node which is more "ideal" than the current node. Strip out +// control copies +Node* StrInflatedCopyNode::Ideal(PhaseGVN* phase, bool can_reshape) { + return remove_dead_region(phase, can_reshape) ? this : NULL; +} + //============================================================================= //------------------------------match_edge------------------------------------- // Do not match memory edge @@ -70,12 +88,12 @@ //------------------------------Ideal------------------------------------------ // Return a node which is more "ideal" than the current node. Strip out // control copies -Node *EncodeISOArrayNode::Ideal(PhaseGVN *phase, bool can_reshape) { +Node* EncodeISOArrayNode::Ideal(PhaseGVN* phase, bool can_reshape) { return remove_dead_region(phase, can_reshape) ? this : NULL; } //------------------------------Value------------------------------------------ -const Type *EncodeISOArrayNode::Value(PhaseTransform *phase) const { +const Type* EncodeISOArrayNode::Value(PhaseTransform* phase) const { if (in(0) && phase->type(in(0)) == Type::TOP) return Type::TOP; return bottom_type(); } --- old/src/share/vm/opto/intrinsicnode.hpp 2015-10-30 19:14:54.540475737 +0100 +++ new/src/share/vm/opto/intrinsicnode.hpp 2015-10-30 19:14:54.476475735 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -35,75 +35,131 @@ // hit (cache is checked with exposed code in gen_subtype_check()). Return // not zero for a miss or zero for a hit. class PartialSubtypeCheckNode : public Node { - public: + public: PartialSubtypeCheckNode(Node* c, Node* sub, Node* super) : Node(c,sub,super) {} virtual int Opcode() const; - virtual const Type *bottom_type() const { return TypeRawPtr::BOTTOM; } + virtual const Type* bottom_type() const { return TypeRawPtr::BOTTOM; } virtual uint ideal_reg() const { return Op_RegP; } }; //------------------------------StrIntrinsic------------------------------- -// Base class for Ideal nodes used in String instrinsic code. +// Base class for Ideal nodes used in String intrinsic code. class StrIntrinsicNode: public Node { - public: + public: + // Possible encodings of the two parameters passed to the string intrinsic. + // 'L' stands for Latin1 and 'U' stands for UTF16. For example, 'LU' means that + // the first string is Latin1 encoded and the second string is UTF16 encoded. + typedef enum ArgEncoding { LL, LU, UL, UU, none } ArgEnc; + + protected: + // Encoding of strings. Used to select the right version of the intrinsic. + const ArgEncoding _encoding; + virtual uint size_of() const; + + public: StrIntrinsicNode(Node* control, Node* char_array_mem, - Node* s1, Node* c1, Node* s2, Node* c2): - Node(control, char_array_mem, s1, c1, s2, c2) { + Node* s1, Node* c1, Node* s2, Node* c2, ArgEncoding encoding): + Node(control, char_array_mem, s1, c1, s2, c2), _encoding(encoding) { } StrIntrinsicNode(Node* control, Node* char_array_mem, - Node* s1, Node* s2, Node* c): - Node(control, char_array_mem, s1, s2, c) { + Node* s1, Node* s2, Node* c, ArgEncoding encoding): + Node(control, char_array_mem, s1, s2, c), _encoding(encoding) { } StrIntrinsicNode(Node* control, Node* char_array_mem, - Node* s1, Node* s2): - Node(control, char_array_mem, s1, s2) { + Node* s1, Node* s2, ArgEncoding encoding): + Node(control, char_array_mem, s1, s2), _encoding(encoding) { } virtual bool depends_only_on_test() const { return false; } - virtual const TypePtr* adr_type() const { return TypeAryPtr::CHARS; } + virtual const TypePtr* adr_type() const { return TypeAryPtr::BYTES; } virtual uint match_edge(uint idx) const; virtual uint ideal_reg() const { return Op_RegI; } - virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); - virtual const Type *Value(PhaseTransform *phase) const; + virtual Node* Ideal(PhaseGVN* phase, bool can_reshape); + virtual const Type* Value(PhaseTransform* phase) const; + ArgEncoding encoding() const { return _encoding; } }; //------------------------------StrComp------------------------------------- class StrCompNode: public StrIntrinsicNode { - public: + public: StrCompNode(Node* control, Node* char_array_mem, - Node* s1, Node* c1, Node* s2, Node* c2): - StrIntrinsicNode(control, char_array_mem, s1, c1, s2, c2) {}; + Node* s1, Node* c1, Node* s2, Node* c2, ArgEncoding encoding): + StrIntrinsicNode(control, char_array_mem, s1, c1, s2, c2, encoding) {}; virtual int Opcode() const; virtual const Type* bottom_type() const { return TypeInt::INT; } }; //------------------------------StrEquals------------------------------------- class StrEqualsNode: public StrIntrinsicNode { - public: + public: StrEqualsNode(Node* control, Node* char_array_mem, - Node* s1, Node* s2, Node* c): - StrIntrinsicNode(control, char_array_mem, s1, s2, c) {}; + Node* s1, Node* s2, Node* c, ArgEncoding encoding): + StrIntrinsicNode(control, char_array_mem, s1, s2, c, encoding) {}; virtual int Opcode() const; virtual const Type* bottom_type() const { return TypeInt::BOOL; } }; //------------------------------StrIndexOf------------------------------------- class StrIndexOfNode: public StrIntrinsicNode { - public: + public: StrIndexOfNode(Node* control, Node* char_array_mem, - Node* s1, Node* c1, Node* s2, Node* c2): - StrIntrinsicNode(control, char_array_mem, s1, c1, s2, c2) {}; + Node* s1, Node* c1, Node* s2, Node* c2, ArgEncoding encoding): + StrIntrinsicNode(control, char_array_mem, s1, c1, s2, c2, encoding) {}; + virtual int Opcode() const; + virtual const Type* bottom_type() const { return TypeInt::INT; } +}; + +//------------------------------StrIndexOfChar------------------------------------- +class StrIndexOfCharNode: public StrIntrinsicNode { + public: + StrIndexOfCharNode(Node* control, Node* char_array_mem, + Node* s1, Node* c1, Node* c, ArgEncoding encoding): + StrIntrinsicNode(control, char_array_mem, s1, c1, c, encoding) {}; + virtual int Opcode() const; + virtual const Type* bottom_type() const { return TypeInt::INT; } +}; + +//--------------------------StrCompressedCopy------------------------------- +class StrCompressedCopyNode: public StrIntrinsicNode { + public: + StrCompressedCopyNode(Node* control, Node* arymem, + Node* s1, Node* s2, Node* c): + StrIntrinsicNode(control, arymem, s1, s2, c, none) {}; virtual int Opcode() const; virtual const Type* bottom_type() const { return TypeInt::INT; } + virtual const TypePtr* adr_type() const { return TypePtr::BOTTOM; } + virtual Node* Ideal(PhaseGVN* phase, bool can_reshape); +}; + +//--------------------------StrInflatedCopy--------------------------------- +class StrInflatedCopyNode: public StrIntrinsicNode { + public: + StrInflatedCopyNode(Node* control, Node* arymem, + Node* s1, Node* s2, Node* c): + StrIntrinsicNode(control, arymem, s1, s2, c, none) {}; + virtual int Opcode() const; + virtual const Type* bottom_type() const { return Type::MEMORY; } + virtual const TypePtr* adr_type() const { return TypePtr::BOTTOM; } + virtual Node* Ideal(PhaseGVN* phase, bool can_reshape); }; //------------------------------AryEq--------------------------------------- class AryEqNode: public StrIntrinsicNode { - public: - AryEqNode(Node* control, Node* char_array_mem, Node* s1, Node* s2): - StrIntrinsicNode(control, char_array_mem, s1, s2) {}; + public: + AryEqNode(Node* control, Node* char_array_mem, + Node* s1, Node* s2, ArgEncoding encoding): + StrIntrinsicNode(control, char_array_mem, s1, s2, encoding) {}; + virtual int Opcode() const; + virtual const Type* bottom_type() const { return TypeInt::BOOL; } +}; + +//------------------------------HasNegatives--------------------------------- +class HasNegativesNode: public StrIntrinsicNode { + public: + HasNegativesNode(Node* control, Node* char_array_mem, Node* s1, Node* c1): + StrIntrinsicNode(control, char_array_mem, s1, c1, none) {}; virtual int Opcode() const; virtual const Type* bottom_type() const { return TypeInt::BOOL; } }; @@ -112,16 +168,16 @@ //------------------------------EncodeISOArray-------------------------------- // encode char[] to byte[] in ISO_8859_1 class EncodeISOArrayNode: public Node { - public: - EncodeISOArrayNode(Node *control, Node* arymem, Node* s1, Node* s2, Node* c): Node(control, arymem, s1, s2, c) {}; + public: + EncodeISOArrayNode(Node* control, Node* arymem, Node* s1, Node* s2, Node* c): Node(control, arymem, s1, s2, c) {}; virtual int Opcode() const; virtual bool depends_only_on_test() const { return false; } virtual const Type* bottom_type() const { return TypeInt::INT; } virtual const TypePtr* adr_type() const { return TypePtr::BOTTOM; } virtual uint match_edge(uint idx) const; virtual uint ideal_reg() const { return Op_RegI; } - virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); - virtual const Type *Value(PhaseTransform *phase) const; + virtual Node* Ideal(PhaseGVN* phase, bool can_reshape); + virtual const Type* Value(PhaseTransform* phase) const; }; #endif // SHARE_VM_OPTO_INTRINSICNODE_HPP --- old/src/share/vm/opto/lcm.cpp 2015-10-30 19:14:54.772475744 +0100 +++ new/src/share/vm/opto/lcm.cpp 2015-10-30 19:14:54.708475742 +0100 @@ -196,8 +196,12 @@ case Op_StrComp: case Op_StrEquals: case Op_StrIndexOf: + case Op_StrIndexOfChar: case Op_AryEq: + case Op_StrInflatedCopy: + case Op_StrCompressedCopy: case Op_EncodeISOArray: + case Op_HasNegatives: // Not a legit memory op for implicit null check regardless of // embedded loads continue; --- old/src/share/vm/opto/library_call.cpp 2015-10-30 19:14:55.060475753 +0100 +++ new/src/share/vm/opto/library_call.cpp 2015-10-30 19:14:54.972475750 +0100 @@ -200,12 +200,16 @@ } Node * load_field_from_object(Node * fromObj, const char * fieldName, const char * fieldTypeString, bool is_exact, bool is_static, ciInstanceKlass * fromKls); - Node* make_string_method_node(int opcode, Node* str1_start, Node* cnt1, Node* str2_start, Node* cnt2); - Node* make_string_method_node(int opcode, Node* str1, Node* str2); - bool inline_string_compareTo(); - bool inline_string_indexOf(); - Node* string_indexOf(Node* string_object, ciTypeArray* target_array, jint offset, jint cache_i, jint md2_i); - bool inline_string_equals(); + 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); + 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); + bool inline_string_char_access(bool is_store); Node* round_double_node(Node* n); bool runtime_math(const TypeFunc* call_type, address funcAddr, const char* funcName); bool inline_math_native(vmIntrinsics::ID id); @@ -251,7 +255,7 @@ bool inline_native_newArray(); bool inline_native_getLength(); bool inline_array_copyOf(bool is_copyOfRange); - bool inline_array_equals(); + bool inline_array_equals(StrIntrinsicNode::ArgEnc ae); void copy_to_clone(Node* obj, Node* alloc_obj, Node* obj_size, bool is_array, bool card_mark); bool inline_native_clone(bool is_virtual); bool inline_native_Reflection_getCallerClass(); @@ -298,6 +302,7 @@ bool inline_updateBytesAdler32(); bool inline_updateByteBufferAdler32(); bool inline_multiplyToLen(); + bool inline_hasNegatives(); bool inline_squareToLen(); bool inline_mulAdd(); bool inline_montgomeryMultiply(); @@ -457,6 +462,7 @@ bool LibraryCallKit::try_to_inline(int predicate) { // Handle symbolic names for otherwise undistinguished boolean switches: const bool is_store = true; + const bool is_compress = true; const bool is_native_ptr = true; const bool is_static = true; const bool is_volatile = true; @@ -511,9 +517,31 @@ case vmIntrinsics::_arraycopy: return inline_arraycopy(); - case vmIntrinsics::_compareTo: return inline_string_compareTo(); - case vmIntrinsics::_indexOf: return inline_string_indexOf(); - case vmIntrinsics::_equals: return inline_string_equals(); + case vmIntrinsics::_compareToL: return inline_string_compareTo(StrIntrinsicNode::LL); + case vmIntrinsics::_compareToU: return inline_string_compareTo(StrIntrinsicNode::UU); + case vmIntrinsics::_compareToLU: return inline_string_compareTo(StrIntrinsicNode::LU); + case vmIntrinsics::_compareToUL: return inline_string_compareTo(StrIntrinsicNode::UL); + + case vmIntrinsics::_indexOfL: return inline_string_indexOf(StrIntrinsicNode::LL); + case vmIntrinsics::_indexOfU: return inline_string_indexOf(StrIntrinsicNode::UU); + case vmIntrinsics::_indexOfUL: return inline_string_indexOf(StrIntrinsicNode::UL); + case vmIntrinsics::_indexOfIL: return inline_string_indexOfI(StrIntrinsicNode::LL); + case vmIntrinsics::_indexOfIU: return inline_string_indexOfI(StrIntrinsicNode::UU); + case vmIntrinsics::_indexOfIUL: return inline_string_indexOfI(StrIntrinsicNode::UL); + case vmIntrinsics::_indexOfU_char: return inline_string_indexOfChar(); + + case vmIntrinsics::_equalsL: return inline_string_equals(StrIntrinsicNode::LL); + case vmIntrinsics::_equalsU: return inline_string_equals(StrIntrinsicNode::UU); + + case vmIntrinsics::_toBytesStringU: return inline_string_toBytesU(); + case vmIntrinsics::_getCharsStringU: return inline_string_getCharsU(); + case vmIntrinsics::_getCharStringU: return inline_string_char_access(!is_store); + case vmIntrinsics::_putCharStringU: return inline_string_char_access( is_store); + + case vmIntrinsics::_compressStringC: + case vmIntrinsics::_compressStringB: return inline_string_copy( is_compress); + case vmIntrinsics::_inflateStringC: + case vmIntrinsics::_inflateStringB: return inline_string_copy(!is_compress); case vmIntrinsics::_getObject: return inline_unsafe_access(!is_native_ptr, !is_store, T_OBJECT, !is_volatile); case vmIntrinsics::_getBoolean: return inline_unsafe_access(!is_native_ptr, !is_store, T_BOOLEAN, !is_volatile); @@ -616,7 +644,8 @@ case vmIntrinsics::_getLength: return inline_native_getLength(); case vmIntrinsics::_copyOf: return inline_array_copyOf(false); case vmIntrinsics::_copyOfRange: return inline_array_copyOf(true); - case vmIntrinsics::_equalsC: return inline_array_equals(); + case vmIntrinsics::_equalsB: return inline_array_equals(StrIntrinsicNode::LL); + case vmIntrinsics::_equalsC: return inline_array_equals(StrIntrinsicNode::UU); case vmIntrinsics::_clone: return inline_native_clone(intrinsic()->is_virtual()); case vmIntrinsics::_isAssignableFrom: return inline_native_subtype_check(); @@ -686,6 +715,7 @@ return inline_ghash_processBlocks(); case vmIntrinsics::_encodeISOArray: + case vmIntrinsics::_encodeByteISOArray: return inline_encodeISOArray(); case vmIntrinsics::_updateCRC32: @@ -710,6 +740,9 @@ case vmIntrinsics::_isCompileConstant: return inline_isCompileConstant(); + case vmIntrinsics::_hasNegatives: + return inline_hasNegatives(); + default: // If you get here, it may be that someone has added a new intrinsic // to the list in vmSymbols.hpp without implementing it here. @@ -875,45 +908,24 @@ //------------------------------make_string_method_node------------------------ -// Helper method for String intrinsic functions. This version is called -// with str1 and str2 pointing to String object nodes. -// -Node* LibraryCallKit::make_string_method_node(int opcode, Node* str1, Node* str2) { - Node* no_ctrl = NULL; - - // Get start addr of string - Node* str1_value = load_String_value(no_ctrl, str1); - Node* str1_offset = load_String_offset(no_ctrl, str1); - Node* str1_start = array_element_address(str1_value, str1_offset, T_CHAR); - - // Get length of string 1 - Node* str1_len = load_String_length(no_ctrl, str1); - - Node* str2_value = load_String_value(no_ctrl, str2); - Node* str2_offset = load_String_offset(no_ctrl, str2); - Node* str2_start = array_element_address(str2_value, str2_offset, T_CHAR); - - Node* str2_len = NULL; +// Helper method for String intrinsic functions. This version is called with +// str1 and str2 pointing to byte[] nodes containing Latin1 or UTF16 encoded +// characters (depending on 'is_byte'). cnt1 and cnt2 are pointing to Int nodes +// containing the lengths of str1 and str2. +Node* LibraryCallKit::make_string_method_node(int opcode, Node* str1_start, Node* cnt1, Node* str2_start, Node* cnt2, StrIntrinsicNode::ArgEnc ae) { Node* result = NULL; - switch (opcode) { case Op_StrIndexOf: - // Get length of string 2 - str2_len = load_String_length(no_ctrl, str2); - - result = new StrIndexOfNode(control(), memory(TypeAryPtr::CHARS), - str1_start, str1_len, str2_start, str2_len); + result = new StrIndexOfNode(control(), memory(TypeAryPtr::BYTES), + str1_start, cnt1, str2_start, cnt2, ae); break; case Op_StrComp: - // Get length of string 2 - str2_len = load_String_length(no_ctrl, str2); - - result = new StrCompNode(control(), memory(TypeAryPtr::CHARS), - str1_start, str1_len, str2_start, str2_len); + result = new StrCompNode(control(), memory(TypeAryPtr::BYTES), + str1_start, cnt1, str2_start, cnt2, ae); break; case Op_StrEquals: - result = new StrEqualsNode(control(), memory(TypeAryPtr::CHARS), - str1_start, str2_start, str1_len); + result = new StrEqualsNode(control(), memory(TypeAryPtr::BYTES), + str1_start, str2_start, cnt1, ae); break; default: ShouldNotReachHere(); @@ -926,129 +938,55 @@ return _gvn.transform(result); } -// Helper method for String intrinsic functions. This version is called -// with str1 and str2 pointing to char[] nodes, with cnt1 and cnt2 pointing -// to Int nodes containing the lenghts of str1 and str2. -// -Node* LibraryCallKit::make_string_method_node(int opcode, Node* str1_start, Node* cnt1, Node* str2_start, Node* cnt2) { - Node* result = NULL; - switch (opcode) { - case Op_StrIndexOf: - result = new StrIndexOfNode(control(), memory(TypeAryPtr::CHARS), - str1_start, cnt1, str2_start, cnt2); - break; - case Op_StrComp: - result = new StrCompNode(control(), memory(TypeAryPtr::CHARS), - str1_start, cnt1, str2_start, cnt2); - break; - case Op_StrEquals: - result = new StrEqualsNode(control(), memory(TypeAryPtr::CHARS), - str1_start, str2_start, cnt1); - break; - default: - ShouldNotReachHere(); - return NULL; - } - - // All these intrinsics have checks. - C->set_has_split_ifs(true); // Has chance for split-if optimization +//------------------------------inline_string_compareTo------------------------ +bool LibraryCallKit::inline_string_compareTo(StrIntrinsicNode::ArgEnc ae) { + Node* arg1 = argument(0); + Node* arg2 = argument(1); - return _gvn.transform(result); -} + // Get start addr and length of first argument + Node* arg1_start = array_element_address(arg1, intcon(0), T_BYTE); + Node* arg1_cnt = load_array_length(arg1); + + // Get start addr and length of second argument + Node* arg2_start = array_element_address(arg2, intcon(0), T_BYTE); + Node* arg2_cnt = load_array_length(arg2); -//------------------------------inline_string_compareTo------------------------ -// public int java.lang.String.compareTo(String anotherString); -bool LibraryCallKit::inline_string_compareTo() { - Node* receiver = null_check(argument(0)); - Node* arg = null_check(argument(1)); - if (stopped()) { - return true; - } - set_result(make_string_method_node(Op_StrComp, receiver, arg)); + Node* result = make_string_method_node(Op_StrComp, arg1_start, arg1_cnt, arg2_start, arg2_cnt, ae); + set_result(result); return true; } //------------------------------inline_string_equals------------------------ -bool LibraryCallKit::inline_string_equals() { - Node* receiver = null_check_receiver(); - // NOTE: Do not null check argument for String.equals() because spec - // allows to specify NULL as argument. - Node* argument = this->argument(1); - if (stopped()) { - return true; - } +bool LibraryCallKit::inline_string_equals(StrIntrinsicNode::ArgEnc ae) { + Node* arg1 = argument(0); + Node* arg2 = argument(1); // paths (plus control) merge - RegionNode* region = new RegionNode(5); + RegionNode* region = new RegionNode(3); Node* phi = new PhiNode(region, TypeInt::BOOL); - // does source == target string? - Node* cmp = _gvn.transform(new CmpPNode(receiver, argument)); - Node* bol = _gvn.transform(new BoolNode(cmp, BoolTest::eq)); - - Node* if_eq = generate_slow_guard(bol, NULL); - if (if_eq != NULL) { - // receiver == argument - phi->init_req(2, intcon(1)); - region->init_req(2, if_eq); - } - - // get String klass for instanceOf - ciInstanceKlass* klass = env()->String_klass(); - if (!stopped()) { - Node* inst = gen_instanceof(argument, makecon(TypeKlassPtr::make(klass))); - Node* cmp = _gvn.transform(new CmpINode(inst, intcon(1))); - Node* bol = _gvn.transform(new BoolNode(cmp, BoolTest::ne)); - - Node* inst_false = generate_guard(bol, NULL, PROB_MIN); - //instanceOf == true, fallthrough - - if (inst_false != NULL) { - phi->init_req(3, intcon(0)); - region->init_req(3, inst_false); - } - } - - if (!stopped()) { - const TypeOopPtr* string_type = TypeOopPtr::make_from_klass(klass); - - // Properly cast the argument to String - argument = _gvn.transform(new CheckCastPPNode(control(), argument, string_type)); - // This path is taken only when argument's type is String:NotNull. - argument = cast_not_null(argument, false); - - Node* no_ctrl = NULL; - - // Get start addr of receiver - Node* receiver_val = load_String_value(no_ctrl, receiver); - Node* receiver_offset = load_String_offset(no_ctrl, receiver); - Node* receiver_start = array_element_address(receiver_val, receiver_offset, T_CHAR); + // Get start addr and length of first argument + Node* arg1_start = array_element_address(arg1, intcon(0), T_BYTE); + Node* arg1_cnt = load_array_length(arg1); + + // Get start addr and length of second argument + Node* arg2_start = array_element_address(arg2, intcon(0), T_BYTE); + Node* arg2_cnt = load_array_length(arg2); - // Get length of receiver - Node* receiver_cnt = load_String_length(no_ctrl, receiver); - - // Get start addr of argument - Node* argument_val = load_String_value(no_ctrl, argument); - Node* argument_offset = load_String_offset(no_ctrl, argument); - Node* argument_start = array_element_address(argument_val, argument_offset, T_CHAR); - - // Get length of argument - Node* argument_cnt = load_String_length(no_ctrl, argument); - - // Check for receiver count != argument count - Node* cmp = _gvn.transform(new CmpINode(receiver_cnt, argument_cnt)); + // Check for arg1_cnt != arg2_cnt + Node* cmp = _gvn.transform(new CmpINode(arg1_cnt, arg2_cnt)); Node* bol = _gvn.transform(new BoolNode(cmp, BoolTest::ne)); Node* if_ne = generate_slow_guard(bol, NULL); if (if_ne != NULL) { - phi->init_req(4, intcon(0)); - region->init_req(4, if_ne); + phi->init_req(2, intcon(0)); + region->init_req(2, if_ne); } // Check for count == 0 is done by assembler code for StrEquals. if (!stopped()) { - Node* equals = make_string_method_node(Op_StrEquals, receiver_start, receiver_cnt, argument_start, argument_cnt); + Node* equals = make_string_method_node(Op_StrEquals, arg1_start, arg1_cnt, arg2_start, arg2_cnt, ae); phi->init_req(1, equals); region->init_req(1, control()); } @@ -1063,289 +1001,462 @@ } //------------------------------inline_array_equals---------------------------- -bool LibraryCallKit::inline_array_equals() { +bool LibraryCallKit::inline_array_equals(StrIntrinsicNode::ArgEnc ae) { + assert(ae == StrIntrinsicNode::UU || ae == StrIntrinsicNode::LL, "unsupported array types"); Node* arg1 = argument(0); Node* arg2 = argument(1); - set_result(_gvn.transform(new AryEqNode(control(), memory(TypeAryPtr::CHARS), arg1, arg2))); + + const TypeAryPtr* mtype = (ae == StrIntrinsicNode::UU) ? TypeAryPtr::CHARS : TypeAryPtr::BYTES; + set_result(_gvn.transform(new AryEqNode(control(), memory(mtype), arg1, arg2, ae))); return true; } -// Java version of String.indexOf(constant string) -// class StringDecl { -// StringDecl(char[] ca) { -// offset = 0; -// count = ca.length; -// value = ca; -// } -// int offset; -// int count; -// char[] value; -// } -// -// static int string_indexOf_J(StringDecl string_object, char[] target_object, -// int targetOffset, int cache_i, int md2) { -// int cache = cache_i; -// int sourceOffset = string_object.offset; -// int sourceCount = string_object.count; -// int targetCount = target_object.length; -// -// int targetCountLess1 = targetCount - 1; -// int sourceEnd = sourceOffset + sourceCount - targetCountLess1; -// -// char[] source = string_object.value; -// char[] target = target_object; -// int lastChar = target[targetCountLess1]; -// -// outer_loop: -// for (int i = sourceOffset; i < sourceEnd; ) { -// int src = source[i + targetCountLess1]; -// if (src == lastChar) { -// // With random strings and a 4-character alphabet, -// // reverse matching at this point sets up 0.8% fewer -// // frames, but (paradoxically) makes 0.3% more probes. -// // Since those probes are nearer the lastChar probe, -// // there is may be a net D$ win with reverse matching. -// // But, reversing loop inhibits unroll of inner loop -// // for unknown reason. So, does running outer loop from -// // (sourceOffset - targetCountLess1) to (sourceOffset + sourceCount) -// for (int j = 0; j < targetCountLess1; j++) { -// if (target[targetOffset + j] != source[i+j]) { -// if ((cache & (1 << source[i+j])) == 0) { -// if (md2 < j+1) { -// i += j+1; -// continue outer_loop; -// } -// } -// i += md2; -// continue outer_loop; -// } -// } -// return i - sourceOffset; -// } -// if ((cache & (1 << src)) == 0) { -// i += targetCountLess1; -// } // using "i += targetCount;" and an "else i++;" causes a jump to jump. -// i++; -// } -// return -1; -// } - -//------------------------------string_indexOf------------------------ -Node* LibraryCallKit::string_indexOf(Node* string_object, ciTypeArray* target_array, jint targetOffset_i, - jint cache_i, jint md2_i) { - - Node* no_ctrl = NULL; - float likely = PROB_LIKELY(0.9); - float unlikely = PROB_UNLIKELY(0.9); - - const int nargs = 0; // no arguments to push back for uncommon trap in predicate - - Node* source = load_String_value(no_ctrl, string_object); - Node* sourceOffset = load_String_offset(no_ctrl, string_object); - Node* sourceCount = load_String_length(no_ctrl, string_object); - - Node* target = _gvn.transform( makecon(TypeOopPtr::make_from_constant(target_array, true))); - jint target_length = target_array->length(); - const TypeAry* target_array_type = TypeAry::make(TypeInt::CHAR, TypeInt::make(0, target_length, Type::WidenMin)); - const TypeAryPtr* target_type = TypeAryPtr::make(TypePtr::BotPTR, target_array_type, target_array->klass(), true, Type::OffsetBot); - - // String.value field is known to be @Stable. - if (UseImplicitStableValues) { - target = cast_array_to_stable(target, target_type); - } - - IdealKit kit(this, false, true); -#define __ kit. - Node* zero = __ ConI(0); - Node* one = __ ConI(1); - Node* cache = __ ConI(cache_i); - Node* md2 = __ ConI(md2_i); - Node* lastChar = __ ConI(target_array->char_at(target_length - 1)); - Node* targetCountLess1 = __ ConI(target_length - 1); - Node* targetOffset = __ ConI(targetOffset_i); - Node* sourceEnd = __ SubI(__ AddI(sourceOffset, sourceCount), targetCountLess1); - - IdealVariable rtn(kit), i(kit), j(kit); __ declarations_done(); - Node* outer_loop = __ make_label(2 /* goto */); - Node* return_ = __ make_label(1); - - __ set(rtn,__ ConI(-1)); - __ loop(this, nargs, i, sourceOffset, BoolTest::lt, sourceEnd); { - Node* i2 = __ AddI(__ value(i), targetCountLess1); - // pin to prohibit loading of "next iteration" value which may SEGV (rare) - Node* src = load_array_element(__ ctrl(), source, i2, TypeAryPtr::CHARS); - __ if_then(src, BoolTest::eq, lastChar, unlikely); { - __ loop(this, nargs, j, zero, BoolTest::lt, targetCountLess1); { - Node* tpj = __ AddI(targetOffset, __ value(j)); - Node* targ = load_array_element(no_ctrl, target, tpj, target_type); - Node* ipj = __ AddI(__ value(i), __ value(j)); - Node* src2 = load_array_element(no_ctrl, source, ipj, TypeAryPtr::CHARS); - __ if_then(targ, BoolTest::ne, src2); { - __ if_then(__ AndI(cache, __ LShiftI(one, src2)), BoolTest::eq, zero); { - __ if_then(md2, BoolTest::lt, __ AddI(__ value(j), one)); { - __ increment(i, __ AddI(__ value(j), one)); - __ goto_(outer_loop); - } __ end_if(); __ dead(j); - }__ end_if(); __ dead(j); - __ increment(i, md2); - __ goto_(outer_loop); - }__ end_if(); - __ increment(j, one); - }__ end_loop(); __ dead(j); - __ set(rtn, __ SubI(__ value(i), sourceOffset)); __ dead(i); - __ goto_(return_); - }__ end_if(); - __ if_then(__ AndI(cache, __ LShiftI(one, src)), BoolTest::eq, zero, likely); { - __ increment(i, targetCountLess1); - }__ end_if(); - __ increment(i, one); - __ bind(outer_loop); - }__ end_loop(); __ dead(i); - __ bind(return_); +//------------------------------inline_hasNegatives------------------------------ +bool LibraryCallKit::inline_hasNegatives() { + if (too_many_traps(Deoptimization::Reason_intrinsic)) return false; - // Final sync IdealKit and GraphKit. - final_sync(kit); - Node* result = __ value(rtn); -#undef __ - C->set_has_loops(true); - return result; + 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; } //------------------------------inline_string_indexOf------------------------ -bool LibraryCallKit::inline_string_indexOf() { - Node* receiver = argument(0); - Node* arg = argument(1); +bool LibraryCallKit::inline_string_indexOf(StrIntrinsicNode::ArgEnc ae) { + if (!Matcher::has_match_rule(Op_StrIndexOf) || !UseSSE42Intrinsics) { + return false; + } + Node* src = argument(0); + Node* tgt = argument(1); - Node* result; - if (Matcher::has_match_rule(Op_StrIndexOf) && - UseSSE42Intrinsics) { - // Generate SSE4.2 version of indexOf - // We currently only have match rules that use SSE4.2 - - receiver = null_check(receiver); - arg = null_check(arg); - if (stopped()) { - return true; - } + // Make the merge point + RegionNode* result_rgn = new RegionNode(4); + Node* result_phi = new PhiNode(result_rgn, TypeInt::INT); + + // Get start addr and length of source string + Node* src_start = array_element_address(src, intcon(0), T_BYTE); + Node* src_count = load_array_length(src); + + // Get start addr and length of substring + Node* tgt_start = array_element_address(tgt, intcon(0), T_BYTE); + Node* tgt_count = load_array_length(tgt); + + if (ae == StrIntrinsicNode::UU || ae == StrIntrinsicNode::UL) { + // Divide src size by 2 if String is UTF16 encoded + src_count = _gvn.transform(new RShiftINode(src_count, intcon(1))); + } + 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); + } - // Make the merge point - RegionNode* result_rgn = new RegionNode(4); - Node* result_phi = new PhiNode(result_rgn, TypeInt::INT); - Node* no_ctrl = NULL; - - // Get start addr of source string - Node* source = load_String_value(no_ctrl, receiver); - Node* source_offset = load_String_offset(no_ctrl, receiver); - Node* source_start = array_element_address(source, source_offset, T_CHAR); - - // Get length of source string - Node* source_cnt = load_String_length(no_ctrl, receiver); - - // Get start addr of substring - Node* substr = load_String_value(no_ctrl, arg); - Node* substr_offset = load_String_offset(no_ctrl, arg); - Node* substr_start = array_element_address(substr, substr_offset, T_CHAR); - - // Get length of source string - Node* substr_cnt = load_String_length(no_ctrl, arg); - - // Check for substr count > string count - Node* cmp = _gvn.transform(new CmpINode(substr_cnt, source_cnt)); - 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()) { - // Check for substr count == 0 - cmp = _gvn.transform(new CmpINode(substr_cnt, 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)); - if (!stopped()) { - result = make_string_method_node(Op_StrIndexOf, source_start, source_cnt, substr_start, substr_cnt); - result_phi->init_req(1, result); - result_rgn->init_req(1, control()); - } - set_control(_gvn.transform(result_rgn)); - record_for_igvn(result_rgn); - result = _gvn.transform(result_phi); - - } else { // Use LibraryCallKit::string_indexOf - // don't intrinsify if argument isn't a constant string. - if (!arg->is_Con()) { - return false; - } - const TypeOopPtr* str_type = _gvn.type(arg)->isa_oopptr(); - if (str_type == NULL) { - return false; + 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[] + Node* tgt = argument(1); // tgt is int ch + 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 + _gvn.transform(result); + + 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_copy--------------------- +// compressIt == true --> generate a compressed copy operation (compress char[]/byte[] to byte[]) +// int StringUTF16.compress(char[] src, int srcOff, byte[] dst, int dstOff, int len) +// 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) { + 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); + Node* dst = argument(2); + Node* dst_offset = argument(3); + Node* length = argument(4); + + // Check for allocation before we add nodes that would confuse + // tightly_coupled_allocation() + AllocateArrayNode* alloc = tightly_coupled_allocation(dst, NULL); + + // Figure out the size and type of the elements we will be copying. + const Type* src_type = src->Value(&_gvn); + const Type* dst_type = dst->Value(&_gvn); + BasicType src_elem = src_type->isa_aryptr()->klass()->as_array_klass()->element_type()->basic_type(); + 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"); + + // 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))); + } + + Node* src_start = array_element_address(src, src_offset, src_elem); + Node* dst_start = array_element_address(dst, dst_offset, dst_elem); + // 'src_start' points to src array + scaled offset + // 'dst_start' points to dst array + scaled offset + Node* count; + if (compress) { + count = compress_string(src_start, dst_start, length); + } else { + inflate_string(src_start, dst_start, length); + } + + if (alloc != NULL) { + if (alloc->maybe_set_complete(&_gvn)) { + // "You break it, you buy it." + InitializeNode* init = alloc->initialization(); + assert(init->is_complete(), "we just did this"); + init->set_complete_with_arraycopy(); + assert(dst->is_CheckCastPP(), "sanity"); + assert(dst->in(0)->in(0) == init, "dest pinned"); } - ciInstanceKlass* klass = env()->String_klass(); - ciObject* str_const = str_type->const_oop(); - if (str_const == NULL || str_const->klass() != klass) { - return false; + // Do not let stores that initialize this object be reordered with + // a subsequent store that would make this object accessible by + // other threads. + // Record what AllocateNode this StoreStore protects so that + // escape analysis can go from the MemBarStoreStoreNode to the + // AllocateNode and eliminate the MemBarStoreStoreNode if possible + // based on the escape status of the AllocateNode. + insert_mem_bar(Op_MemBarStoreStore, alloc->proj_out(AllocateNode::RawAddress)); + } + if (compress) { + set_result(_gvn.transform(count)); + } + return true; +} + +#ifdef _LP64 +#define XTOP ,top() /*additional argument*/ +#else //_LP64 +#define XTOP /*no additional argument*/ +#endif //_LP64 + +//------------------------inline_string_toBytesU-------------------------- +// public static byte[] StringUTF16.toBytes(char[] value, int off, int len) +bool LibraryCallKit::inline_string_toBytesU() { + // Get the arguments. + Node* value = argument(0); + Node* offset = argument(1); + Node* length = argument(2); + + Node* newcopy = NULL; + + // Set the original stack and the reexecute bit for the interpreter to reexecute + // the bytecode that invokes StringUTF16.toBytes() if deoptimization happens. + { PreserveReexecuteState preexecs(this); + jvms()->set_should_reexecute(true); + + // Check if a null path was taken unconditionally. + 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); } - ciInstance* str = str_const->as_instance(); - assert(str != NULL, "must be instance"); + if (stopped()) return true; - ciObject* v = str->field_value_by_offset(java_lang_String::value_offset_in_bytes()).as_object(); - ciTypeArray* pat = v->as_type_array(); // pattern (argument) character array + // Range checks are done by caller. - int o; - int c; - if (java_lang_String::has_offset_field()) { - o = str->field_value_by_offset(java_lang_String::offset_offset_in_bytes()).as_int(); - c = str->field_value_by_offset(java_lang_String::count_offset_in_bytes()).as_int(); + 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); + + // Calculate starting addresses. + Node* src_start = array_element_address(value, offset, T_CHAR); + Node* dst_start = basic_plus_adr(newcopy, arrayOopDesc::base_offset_in_bytes(T_BYTE)); + + // Check if src array address is aligned to HeapWordSize (dst is always aligned) + const TypeInt* toffset = gvn().type(offset)->is_int(); + bool aligned = toffset->is_con() && ((toffset->get_con() * type2aelembytes(T_CHAR)) % HeapWordSize == 0); + + // Figure out which arraycopy runtime method to call (disjoint, uninitialized). + const char* copyfunc_name = "arraycopy"; + address copyfunc_addr = StubRoutines::select_arraycopy_function(T_CHAR, aligned, true, copyfunc_name, true); + Node* call = make_runtime_call(RC_LEAF|RC_NO_FP, + OptoRuntime::fast_arraycopy_Type(), + copyfunc_addr, copyfunc_name, TypeRawPtr::BOTTOM, + src_start, dst_start, ConvI2X(length) XTOP); + // Do not let reads from the cloned object float above the arraycopy. + if (alloc != NULL) { + if (alloc->maybe_set_complete(&_gvn)) { + // "You break it, you buy it." + InitializeNode* init = alloc->initialization(); + assert(init->is_complete(), "we just did this"); + init->set_complete_with_arraycopy(); + assert(newcopy->is_CheckCastPP(), "sanity"); + assert(newcopy->in(0)->in(0) == init, "dest pinned"); + } + // Do not let stores that initialize this object be reordered with + // a subsequent store that would make this object accessible by + // other threads. + // Record what AllocateNode this StoreStore protects so that + // escape analysis can go from the MemBarStoreStoreNode to the + // AllocateNode and eliminate the MemBarStoreStoreNode if possible + // based on the escape status of the AllocateNode. + insert_mem_bar(Op_MemBarStoreStore, alloc->proj_out(AllocateNode::RawAddress)); } else { - o = 0; - c = pat->length(); + insert_mem_bar(Op_MemBarCPUOrder); } + } // original reexecute is set back here - // constant strings have no offset and count == length which - // simplifies the resulting code somewhat so lets optimize for that. - if (o != 0 || c != pat->length()) { - return false; - } + C->set_has_split_ifs(true); // Has chance for split-if optimization + if (!stopped()) { + set_result(newcopy); + } + return true; +} - receiver = null_check(receiver, T_OBJECT); - // NOTE: No null check on the argument is needed since it's a constant String oop. - if (stopped()) { - 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; - // The null string as a pattern always returns 0 (match at beginning of string) - if (c == 0) { - set_result(intcon(0)); - return true; - } + // 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); - // Generate default indexOf - jchar lastChar = pat->char_at(o + (c - 1)); - int cache = 0; - int i; - for (i = 0; i < c - 1; i++) { - assert(i < pat->length(), "out of range"); - cache |= (1 << (pat->char_at(o + i) & (sizeof(cache) * BitsPerByte - 1))); - } + // 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; + } - int md2 = c; - for (i = 0; i < c - 1; i++) { - assert(i < pat->length(), "out of range"); - if (pat->char_at(o + i) == lastChar) { - md2 = (c - 1) - i; + // 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(); + bool aligned = tsrc->is_con() && ((tsrc->get_con() * type2aelembytes(T_BYTE)) % HeapWordSize == 0) && + tdst->is_con() && ((tdst->get_con() * type2aelembytes(T_CHAR)) % HeapWordSize == 0); + + // Figure out which arraycopy runtime method to call (disjoint, uninitialized). + const char* copyfunc_name = "arraycopy"; + address copyfunc_addr = StubRoutines::select_arraycopy_function(T_CHAR, aligned, true, copyfunc_name, true); + Node* call = make_runtime_call(RC_LEAF|RC_NO_FP, + OptoRuntime::fast_arraycopy_Type(), + copyfunc_addr, copyfunc_name, TypeRawPtr::BOTTOM, + src_start, dst_start, ConvI2X(length) XTOP); + // Do not let reads from the cloned object float above the arraycopy. + if (alloc != NULL) { + if (alloc->maybe_set_complete(&_gvn)) { + // "You break it, you buy it." + InitializeNode* init = alloc->initialization(); + assert(init->is_complete(), "we just did this"); + init->set_complete_with_arraycopy(); + assert(dst->is_CheckCastPP(), "sanity"); + assert(dst->in(0)->in(0) == init, "dest pinned"); } + // Do not let stores that initialize this object be reordered with + // a subsequent store that would make this object accessible by + // other threads. + // Record what AllocateNode this StoreStore protects so that + // escape analysis can go from the MemBarStoreStoreNode to the + // AllocateNode and eliminate the MemBarStoreStoreNode if possible + // based on the escape status of the AllocateNode. + insert_mem_bar(Op_MemBarStoreStore, alloc->proj_out(AllocateNode::RawAddress)); + } else { + insert_mem_bar(Op_MemBarCPUOrder); } + } - result = string_indexOf(receiver, pat, o, cache, md2); + C->set_has_split_ifs(true); // Has chance for split-if optimization + return true; +} + +//----------------------inline_string_char_access---------------------------- +// Store/Load char to/from byte[] array. +// static void StringUTF16.putChar(byte[] val, int index, int c) +// static char StringUTF16.getChar(byte[] val, int index) +bool LibraryCallKit::inline_string_char_access(bool is_store) { + Node* value = argument(0); + Node* index = argument(1); + Node* ch = is_store ? argument(2) : NULL; + + // This intrinsic accesses byte[] array as char[] array. Computing the offsets + // correctly requires matched array shapes. + assert (arrayOopDesc::base_offset_in_bytes(T_CHAR) == arrayOopDesc::base_offset_in_bytes(T_BYTE), + "sanity: byte[] and char[] bases agree"); + assert (type2aelembytes(T_CHAR) == type2aelembytes(T_BYTE)*2, + "sanity: byte[] and char[] scales agree"); + + Node* adr = array_element_address(value, index, T_CHAR); + if (is_store) { + (void) store_to_memory(control(), adr, ch, T_CHAR, TypeAryPtr::BYTES, MemNode::unordered); + } else { + ch = make_load(control(), adr, TypeInt::CHAR, T_CHAR, MemNode::unordered); + set_result(ch); } - set_result(result); return true; } @@ -4187,12 +4298,6 @@ return true; } -#ifdef _LP64 -#define XTOP ,top() /*additional argument*/ -#else //_LP64 -#define XTOP /*no additional argument*/ -#endif //_LP64 - //----------------------inline_unsafe_copyMemory------------------------- // public native void sun.misc.Unsafe.copyMemory(Object srcBase, long srcOffset, Object destBase, long destOffset, long bytes); bool LibraryCallKit::inline_unsafe_copyMemory() { @@ -5001,10 +5106,11 @@ // Figure out the size and type of the elements we will be copying. BasicType src_elem = src_type->isa_aryptr()->klass()->as_array_klass()->element_type()->basic_type(); BasicType dst_elem = dst_type->isa_aryptr()->klass()->as_array_klass()->element_type()->basic_type(); - if (src_elem != T_CHAR || dst_elem != T_BYTE) { + if (!((src_elem == T_CHAR) || (src_elem== T_BYTE)) || dst_elem != T_BYTE) { return false; } - Node* src_start = array_element_address(src, src_offset, src_elem); + + Node* src_start = array_element_address(src, src_offset, T_CHAR); Node* dst_start = array_element_address(dst, dst_offset, dst_elem); // 'src_start' points to src array + scaled offset // 'dst_start' points to dst array + scaled offset @@ -5122,7 +5228,7 @@ //-------------inline_squareToLen------------------------------------ bool LibraryCallKit::inline_squareToLen() { - assert(UseSquareToLenIntrinsic, "not implementated on this platform"); + assert(UseSquareToLenIntrinsic, "not implemented on this platform"); address stubAddr = StubRoutines::squareToLen(); if (stubAddr == NULL) { @@ -5168,7 +5274,7 @@ //-------------inline_mulAdd------------------------------------------ bool LibraryCallKit::inline_mulAdd() { - assert(UseMulAddIntrinsic, "not implementated on this platform"); + assert(UseMulAddIntrinsic, "not implemented on this platform"); address stubAddr = StubRoutines::mulAdd(); if (stubAddr == NULL) { --- old/src/share/vm/opto/loopTransform.cpp 2015-10-30 19:14:55.392475764 +0100 +++ new/src/share/vm/opto/loopTransform.cpp 2015-10-30 19:14:55.324475761 +0100 @@ -622,8 +622,10 @@ case Op_StrComp: case Op_StrEquals: case Op_StrIndexOf: + case Op_StrIndexOfChar: case Op_EncodeISOArray: - case Op_AryEq: { + case Op_AryEq: + case Op_HasNegatives: { return false; } #if INCLUDE_RTM_OPT @@ -741,8 +743,10 @@ case Op_StrComp: case Op_StrEquals: case Op_StrIndexOf: + case Op_StrIndexOfChar: case Op_EncodeISOArray: - case Op_AryEq: { + case Op_AryEq: + case Op_HasNegatives: { // Do not unroll a loop with String intrinsics code. // String intrinsics are large and have loops. return false; --- old/src/share/vm/opto/loopnode.cpp 2015-10-30 19:14:55.664475772 +0100 +++ new/src/share/vm/opto/loopnode.cpp 2015-10-30 19:14:55.592475770 +0100 @@ -3494,7 +3494,9 @@ case Op_StrComp: // Does a bunch of load-like effects case Op_StrEquals: case Op_StrIndexOf: + case Op_StrIndexOfChar: case Op_AryEq: + case Op_HasNegatives: pinned = false; } if( pinned ) { --- old/src/share/vm/opto/macro.cpp 2015-10-30 19:14:55.936475781 +0100 +++ new/src/share/vm/opto/macro.cpp 2015-10-30 19:14:55.868475779 +0100 @@ -379,13 +379,25 @@ if (mem->is_LoadStore()) { adr = mem->in(MemNode::Address); } else { - assert(mem->Opcode() == Op_EncodeISOArray, "sanity"); + assert(mem->Opcode() == Op_EncodeISOArray || + mem->Opcode() == Op_StrCompressedCopy, "sanity"); adr = mem->in(3); // Destination array } const TypePtr* atype = adr->bottom_type()->is_ptr(); int adr_idx = phase->C->get_alias_index(atype); if (adr_idx == alias_idx) { - assert(false, "Object is not scalar replaceable if a LoadStore node access its field"); + DEBUG_ONLY(mem->dump();) + assert(false, "Object is not scalar replaceable if a LoadStore node accesses its field"); + return NULL; + } + mem = mem->in(MemNode::Memory); + } else if (mem->Opcode() == Op_StrInflatedCopy) { + Node* adr = mem->in(3); // Destination array + const TypePtr* atype = adr->bottom_type()->is_ptr(); + int adr_idx = phase->C->get_alias_index(atype); + if (adr_idx == alias_idx) { + DEBUG_ONLY(mem->dump();) + assert(false, "Object is not scalar replaceable if a StrInflatedCopy node accesses its field"); return NULL; } mem = mem->in(MemNode::Memory); @@ -516,8 +528,10 @@ } values.at_put(j, val); } else if (val->Opcode() == Op_SCMemProj) { - assert(val->in(0)->is_LoadStore() || val->in(0)->Opcode() == Op_EncodeISOArray, "sanity"); - assert(false, "Object is not scalar replaceable if a LoadStore node access its field"); + assert(val->in(0)->is_LoadStore() || + val->in(0)->Opcode() == Op_EncodeISOArray || + val->in(0)->Opcode() == Op_StrCompressedCopy, "sanity"); + assert(false, "Object is not scalar replaceable if a LoadStore node accesses its field"); return NULL; } else if (val->is_ArrayCopy()) { Node* res = make_arraycopy_load(val->as_ArrayCopy(), offset, val->in(0), ft, phi_type, alloc); --- old/src/share/vm/opto/matcher.cpp 2015-10-30 19:14:56.216475790 +0100 +++ new/src/share/vm/opto/matcher.cpp 2015-10-30 19:14:56.144475788 +0100 @@ -936,9 +936,13 @@ case Op_StrComp: case Op_StrEquals: case Op_StrIndexOf: + case Op_StrIndexOfChar: case Op_AryEq: + case Op_HasNegatives: case Op_MemBarVolatile: case Op_MemBarCPUOrder: // %%% these ideals should have narrower adr_type? + case Op_StrInflatedCopy: + case Op_StrCompressedCopy: case Op_EncodeISOArray: nidx = Compile::AliasIdxTop; nat = NULL; @@ -2156,7 +2160,11 @@ case Op_StrComp: case Op_StrEquals: case Op_StrIndexOf: + case Op_StrIndexOfChar: case Op_AryEq: + case Op_HasNegatives: + case Op_StrInflatedCopy: + case Op_StrCompressedCopy: case Op_EncodeISOArray: set_shared(n); // Force result into register (it will be anyways) break; @@ -2335,7 +2343,8 @@ n->del_req(3); break; } - case Op_StrEquals: { + case Op_StrEquals: + case Op_StrIndexOfChar: { Node *pair1 = new BinaryNode(n->in(2),n->in(3)); n->set_req(2,pair1); n->set_req(3,n->in(4)); @@ -2352,6 +2361,8 @@ n->del_req(4); break; } + case Op_StrCompressedCopy: + case Op_StrInflatedCopy: case Op_EncodeISOArray: { // Restructure into a binary tree for Matching. Node* pair = new BinaryNode(n->in(3), n->in(4)); --- old/src/share/vm/opto/stringopts.cpp 2015-10-30 19:14:56.492475799 +0100 +++ new/src/share/vm/opto/stringopts.cpp 2015-10-30 19:14:56.424475796 +0100 @@ -598,7 +598,7 @@ } // Collect the types needed to talk about the various slices of memory - char_adr_idx = C->get_alias_index(TypeAryPtr::CHARS); + byte_adr_idx = C->get_alias_index(TypeAryPtr::BYTES); // For each locally allocated StringBuffer see if the usages can be // collapsed into a single String construction. @@ -1128,6 +1128,25 @@ } Node* PhaseStringOpts::int_stringSize(GraphKit& kit, Node* arg) { + if (arg->is_Con()) { + // Constant integer. Compute constant length using Integer.sizeTable + int arg_val = arg->get_int(); + int count = 1; + if (arg_val < 0) { + arg_val = -arg_val; + count++; + } + + ciArray* size_table = (ciArray*)size_table_field->constant_value().as_object(); + for (int i = 0; i < size_table->length(); i++) { + if (arg_val <= size_table->element_value(i).as_int()) { + count += i; + break; + } + } + return __ intcon(count); + } + RegionNode *final_merge = new RegionNode(3); kit.gvn().set_type(final_merge, Type::CONTROL); Node* final_size = new PhiNode(final_merge, TypeInt::INT); @@ -1212,77 +1231,34 @@ return final_size; } -void PhaseStringOpts::int_getChars(GraphKit& kit, Node* arg, Node* char_array, Node* start, Node* end) { - RegionNode *final_merge = new RegionNode(4); - kit.gvn().set_type(final_merge, Type::CONTROL); - Node *final_mem = PhiNode::make(final_merge, kit.memory(char_adr_idx), Type::MEMORY, TypeAryPtr::CHARS); - kit.gvn().set_type(final_mem, Type::MEMORY); - - // need to handle Integer.MIN_VALUE specially because negating doesn't make it positive - { - // i == MIN_VALUE - IfNode* iff = kit.create_and_map_if(kit.control(), - __ Bool(__ CmpI(arg, __ intcon(0x80000000)), BoolTest::ne), - PROB_FAIR, COUNT_UNKNOWN); - - Node* old_mem = kit.memory(char_adr_idx); - - kit.set_control(__ IfFalse(iff)); - if (kit.stopped()) { - // Statically not equal to MIN_VALUE so this path is dead - final_merge->init_req(3, kit.control()); - } else { - copy_string(kit, __ makecon(TypeInstPtr::make(C->env()->the_min_jint_string())), - char_array, start); - final_merge->init_req(3, kit.control()); - final_mem->init_req(3, kit.memory(char_adr_idx)); - } - - kit.set_control(__ IfTrue(iff)); - kit.set_memory(old_mem, char_adr_idx); - } - - - // Simplified version of Integer.getChars - - // int q, r; - // int charPos = index; - Node* charPos = end; - - // char sign = 0; - - Node* i = arg; - Node* sign = __ intcon(0); - +// Simplified version of Integer.getChars +void PhaseStringOpts::getChars(GraphKit& kit, Node* arg, Node* dst_array, BasicType bt, Node* end, Node* final_merge, Node* final_mem, int merge_index) { // if (i < 0) { // sign = '-'; // i = -i; // } - { - IfNode* iff = kit.create_and_map_if(kit.control(), - __ Bool(__ CmpI(arg, __ intcon(0)), BoolTest::lt), - PROB_FAIR, COUNT_UNKNOWN); - - RegionNode *merge = new RegionNode(3); - kit.gvn().set_type(merge, Type::CONTROL); - i = new PhiNode(merge, TypeInt::INT); - kit.gvn().set_type(i, TypeInt::INT); - sign = new PhiNode(merge, TypeInt::INT); - kit.gvn().set_type(sign, TypeInt::INT); - - merge->init_req(1, __ IfTrue(iff)); - i->init_req(1, __ SubI(__ intcon(0), arg)); - sign->init_req(1, __ intcon('-')); - merge->init_req(2, __ IfFalse(iff)); - i->init_req(2, arg); - sign->init_req(2, __ intcon(0)); - - kit.set_control(merge); - - C->record_for_igvn(merge); - C->record_for_igvn(i); - C->record_for_igvn(sign); - } + IfNode* iff = kit.create_and_map_if(kit.control(), __ Bool(__ CmpI(arg, __ intcon(0)), BoolTest::lt), + PROB_FAIR, COUNT_UNKNOWN); + + RegionNode* merge = new RegionNode(3); + kit.gvn().set_type(merge, Type::CONTROL); + Node* i = new PhiNode(merge, TypeInt::INT); + kit.gvn().set_type(i, TypeInt::INT); + Node* sign = new PhiNode(merge, TypeInt::INT); + kit.gvn().set_type(sign, TypeInt::INT); + + merge->init_req(1, __ IfTrue(iff)); + i->init_req(1, __ SubI(__ intcon(0), arg)); + sign->init_req(1, __ intcon('-')); + merge->init_req(2, __ IfFalse(iff)); + i->init_req(2, arg); + sign->init_req(2, __ intcon(0)); + + kit.set_control(merge); + + C->record_for_igvn(merge); + C->record_for_igvn(i); + C->record_for_igvn(sign); // for (;;) { // q = i / 10; @@ -1292,126 +1268,409 @@ // if (i == 0) break; // } - { - // Add loop predicate first. - kit.add_predicate(); + // Add loop predicate first. + kit.add_predicate(); + + RegionNode* head = new RegionNode(3); + head->init_req(1, kit.control()); + + kit.gvn().set_type(head, Type::CONTROL); + Node* i_phi = new PhiNode(head, TypeInt::INT); + i_phi->init_req(1, i); + kit.gvn().set_type(i_phi, TypeInt::INT); + Node* charPos = new PhiNode(head, TypeInt::INT); + charPos->init_req(1, end); + kit.gvn().set_type(charPos, TypeInt::INT); + Node* mem = PhiNode::make(head, kit.memory(byte_adr_idx), Type::MEMORY, TypeAryPtr::BYTES); + kit.gvn().set_type(mem, Type::MEMORY); + + kit.set_control(head); + kit.set_memory(mem, byte_adr_idx); + + Node* q = __ DivI(kit.null(), i_phi, __ intcon(10)); + Node* r = __ SubI(i_phi, __ AddI(__ LShiftI(q, __ intcon(3)), + __ LShiftI(q, __ intcon(1)))); + Node* index = __ SubI(charPos, __ intcon((bt == T_BYTE) ? 1 : 2)); + Node* ch = __ AddI(r, __ intcon('0')); + Node* st = __ store_to_memory(kit.control(), kit.array_element_address(dst_array, index, T_BYTE), + ch, bt, byte_adr_idx, MemNode::unordered); + + iff = kit.create_and_map_if(head, __ Bool(__ CmpI(q, __ intcon(0)), BoolTest::ne), + PROB_FAIR, COUNT_UNKNOWN); + Node* ne = __ IfTrue(iff); + Node* eq = __ IfFalse(iff); + + head->init_req(2, ne); + mem->init_req(2, st); + + i_phi->init_req(2, q); + charPos->init_req(2, index); + charPos = index; + + kit.set_control(eq); + kit.set_memory(st, byte_adr_idx); + + C->record_for_igvn(head); + C->record_for_igvn(mem); + C->record_for_igvn(i_phi); + C->record_for_igvn(charPos); - RegionNode *head = new RegionNode(3); - head->init_req(1, kit.control()); - kit.gvn().set_type(head, Type::CONTROL); - Node *i_phi = new PhiNode(head, TypeInt::INT); - i_phi->init_req(1, i); - kit.gvn().set_type(i_phi, TypeInt::INT); - charPos = PhiNode::make(head, charPos); - kit.gvn().set_type(charPos, TypeInt::INT); - Node *mem = PhiNode::make(head, kit.memory(char_adr_idx), Type::MEMORY, TypeAryPtr::CHARS); - kit.gvn().set_type(mem, Type::MEMORY); - kit.set_control(head); - kit.set_memory(mem, char_adr_idx); - - Node* q = __ DivI(NULL, i_phi, __ intcon(10)); - Node* r = __ SubI(i_phi, __ AddI(__ LShiftI(q, __ intcon(3)), - __ LShiftI(q, __ intcon(1)))); - Node* m1 = __ SubI(charPos, __ intcon(1)); - Node* ch = __ AddI(r, __ intcon('0')); - - Node* st = __ store_to_memory(kit.control(), kit.array_element_address(char_array, m1, T_CHAR), - ch, T_CHAR, char_adr_idx, MemNode::unordered); - - - IfNode* iff = kit.create_and_map_if(head, __ Bool(__ CmpI(q, __ intcon(0)), BoolTest::ne), - PROB_FAIR, COUNT_UNKNOWN); - Node* ne = __ IfTrue(iff); - Node* eq = __ IfFalse(iff); - - head->init_req(2, ne); - mem->init_req(2, st); - i_phi->init_req(2, q); - charPos->init_req(2, m1); - - charPos = m1; - - kit.set_control(eq); - kit.set_memory(st, char_adr_idx); - - C->record_for_igvn(head); - C->record_for_igvn(mem); - C->record_for_igvn(i_phi); - C->record_for_igvn(charPos); + // if (sign != 0) { + // buf [--charPos] = sign; + // } + iff = kit.create_and_map_if(kit.control(), __ Bool(__ CmpI(sign, __ intcon(0)), BoolTest::ne), + PROB_FAIR, COUNT_UNKNOWN); + + final_merge->init_req(merge_index + 2, __ IfFalse(iff)); + final_mem->init_req(merge_index + 2, kit.memory(byte_adr_idx)); + + kit.set_control(__ IfTrue(iff)); + if (kit.stopped()) { + final_merge->init_req(merge_index + 1, C->top()); + final_mem->init_req(merge_index + 1, C->top()); + } else { + Node* index = __ SubI(charPos, __ intcon((bt == T_BYTE) ? 1 : 2)); + st = __ store_to_memory(kit.control(), kit.array_element_address(dst_array, index, T_BYTE), + sign, bt, byte_adr_idx, MemNode::unordered); + + final_merge->init_req(merge_index + 1, kit.control()); + final_mem->init_req(merge_index + 1, st); } +} - { - // if (sign != 0) { - // buf [--charPos] = sign; - // } - IfNode* iff = kit.create_and_map_if(kit.control(), - __ Bool(__ CmpI(sign, __ intcon(0)), BoolTest::ne), - PROB_FAIR, COUNT_UNKNOWN); - - final_merge->init_req(2, __ IfFalse(iff)); - final_mem->init_req(2, kit.memory(char_adr_idx)); - - kit.set_control(__ IfTrue(iff)); - if (kit.stopped()) { - final_merge->init_req(1, C->top()); - final_mem->init_req(1, C->top()); - } else { - Node* m1 = __ SubI(charPos, __ intcon(1)); - Node* st = __ store_to_memory(kit.control(), kit.array_element_address(char_array, m1, T_CHAR), - sign, T_CHAR, char_adr_idx, MemNode::unordered); +// Copy the characters representing arg into dst_array starting at start +Node* PhaseStringOpts::int_getChars(GraphKit& kit, Node* arg, Node* dst_array, Node* dst_coder, Node* start, Node* size) { + bool dcon = dst_coder->is_Con(); + bool dbyte = dcon ? (dst_coder->get_int() == java_lang_String::CODER_LATIN1) : false; + Node* end = __ AddI(start, __ LShiftI(size, dst_coder)); + + // The final_merge node has 4 entries in case the encoding is known: + // (0) Control, (1) result w/ sign, (2) result w/o sign, (3) result for Integer.min_value + // or 6 entries in case the encoding is not known: + // (0) Control, (1) Latin1 w/ sign, (2) Latin1 w/o sign, (3) min_value, (4) UTF16 w/ sign, (5) UTF16 w/o sign + RegionNode* final_merge = new RegionNode(dcon ? 4 : 6); + kit.gvn().set_type(final_merge, Type::CONTROL); + + Node* final_mem = PhiNode::make(final_merge, kit.memory(byte_adr_idx), Type::MEMORY, TypeAryPtr::BYTES); + kit.gvn().set_type(final_mem, Type::MEMORY); + + // need to handle arg == Integer.MIN_VALUE specially because negating doesn't make it positive + IfNode* iff = kit.create_and_map_if(kit.control(), __ Bool(__ CmpI(arg, __ intcon(0x80000000)), BoolTest::ne), + PROB_FAIR, COUNT_UNKNOWN); + + Node* old_mem = kit.memory(byte_adr_idx); + + kit.set_control(__ IfFalse(iff)); + if (kit.stopped()) { + // Statically not equal to MIN_VALUE so this path is dead + final_merge->init_req(3, kit.control()); + } else { + copy_string(kit, __ makecon(TypeInstPtr::make(C->env()->the_min_jint_string())), + dst_array, dst_coder, start); + final_merge->init_req(3, kit.control()); + final_mem->init_req(3, kit.memory(byte_adr_idx)); + } + + kit.set_control(__ IfTrue(iff)); + kit.set_memory(old_mem, byte_adr_idx); + + if (!dcon) { + // Check encoding of destination + iff = kit.create_and_map_if(kit.control(), __ Bool(__ CmpI(dst_coder, __ intcon(0)), BoolTest::eq), + PROB_FAIR, COUNT_UNKNOWN); + old_mem = kit.memory(byte_adr_idx); + } + if (!dcon || dbyte) { + // Destination is Latin1, + if (!dcon) { + kit.set_control(__ IfTrue(iff)); + } + getChars(kit, arg, dst_array, T_BYTE, end, final_merge, final_mem); + } + if (!dcon || !dbyte) { + // Destination is UTF16 + int merge_index = 0; + if (!dcon) { + kit.set_control(__ IfFalse(iff)); + kit.set_memory(old_mem, byte_adr_idx); + merge_index = 3; // Account for Latin1 case + } + getChars(kit, arg, dst_array, T_CHAR, end, final_merge, final_mem, merge_index); + } + + // Final merge point for Latin1 and UTF16 case + kit.set_control(final_merge); + kit.set_memory(final_mem, byte_adr_idx); - final_merge->init_req(1, kit.control()); - final_mem->init_req(1, st); + C->record_for_igvn(final_merge); + C->record_for_igvn(final_mem); + return end; +} + +// Copy 'count' bytes/chars from src_array to dst_array starting at index start +void PhaseStringOpts::arraycopy(GraphKit& kit, IdealKit& ideal, Node* src_array, Node* dst_array, BasicType elembt, Node* start, Node* count) { + assert(elembt == T_BYTE || elembt == T_CHAR, "Invalid type for arraycopy"); + + if (elembt == T_CHAR) { + // Get number of chars + count = __ RShiftI(count, __ intcon(1)); + } + + Node* extra = NULL; +#ifdef _LP64 + count = __ ConvI2L(count); + extra = C->top(); +#endif + + Node* src_ptr = __ array_element_address(src_array, __ intcon(0), T_BYTE); + Node* dst_ptr = __ array_element_address(dst_array, start, T_BYTE); + // Check if destination address is aligned to HeapWordSize + const TypeInt* tdst = __ gvn().type(start)->is_int(); + bool aligned = tdst->is_con() && ((tdst->get_con() * type2aelembytes(T_BYTE)) % HeapWordSize == 0); + // Figure out which arraycopy runtime method to call (disjoint, uninitialized). + const char* copyfunc_name = "arraycopy"; + address copyfunc_addr = StubRoutines::select_arraycopy_function(elembt, aligned, true, copyfunc_name, true); + ideal.make_leaf_call_no_fp(OptoRuntime::fast_arraycopy_Type(), copyfunc_addr, copyfunc_name, + TypeAryPtr::BYTES, src_ptr, dst_ptr, count, extra); +} + +#undef __ +#define __ ideal. + +// Copy contents of a Latin1 encoded string from src_array to dst_array +void PhaseStringOpts::copy_latin1_string(GraphKit& kit, IdealKit& ideal, Node* src_array, IdealVariable& count, + Node* dst_array, Node* dst_coder, Node* start) { + bool dcon = dst_coder->is_Con(); + bool dbyte = dcon ? (dst_coder->get_int() == java_lang_String::CODER_LATIN1) : false; + + if (!dcon) { + __ if_then(dst_coder, BoolTest::eq, __ ConI(java_lang_String::CODER_LATIN1)); + } + if (!dcon || dbyte) { + // Destination is Latin1. Simply emit a byte arraycopy. + arraycopy(kit, ideal, src_array, dst_array, T_BYTE, start, __ value(count)); + } + if (!dcon) { + __ else_(); + } + if (!dcon || !dbyte) { + // Destination is UTF16. Inflate src_array into dst_array. + kit.sync_kit(ideal); + if (Matcher::match_rule_supported(Op_StrInflatedCopy)) { + // Use fast intrinsic + Node* src = kit.array_element_address(src_array, kit.intcon(0), T_BYTE); + Node* dst = kit.array_element_address(dst_array, start, T_BYTE); + kit.inflate_string(src, dst, __ value(count)); + } else { + // No intrinsic available, use slow method + kit.inflate_string_slow(src_array, dst_array, start, __ value(count)); } + ideal.sync_kit(&kit); + // Multiply count by two since we now need two bytes per char + __ set(count, __ LShiftI(__ value(count), __ ConI(1))); + } + if (!dcon) { + __ end_if(); + } +} + +// Read two bytes from index and index+1 and convert them to a char +static jchar readChar(ciTypeArray* array, int index) { + int shift_high, shift_low; +#ifdef VM_LITTLE_ENDIAN + shift_high = 0; + shift_low = 8; +#else + shift_high = 8; + shift_low = 0; +#endif - kit.set_control(final_merge); - kit.set_memory(final_mem, char_adr_idx); + jchar b1 = ((jchar) array->byte_at(index)) & 0xff; + jchar b2 = ((jchar) array->byte_at(index+1)) & 0xff; + return (b1 << shift_high) | (b2 << shift_low); +} - C->record_for_igvn(final_merge); - C->record_for_igvn(final_mem); +// Copy contents of constant src_array to dst_array by emitting individual stores +void PhaseStringOpts::copy_constant_string(GraphKit& kit, IdealKit& ideal, ciTypeArray* src_array, IdealVariable& count, + bool src_is_byte, Node* dst_array, Node* dst_coder, Node* start) { + bool dcon = dst_coder->is_Con(); + bool dbyte = dcon ? (dst_coder->get_int() == java_lang_String::CODER_LATIN1) : false; + int length = src_array->length(); + + if (!dcon) { + __ if_then(dst_coder, BoolTest::eq, __ ConI(java_lang_String::CODER_LATIN1)); + } + if (!dcon || dbyte) { + // Destination is Latin1. Copy each byte of src_array into dst_array. + Node* index = start; + for (int i = 0; i < length; i++) { + Node* adr = kit.array_element_address(dst_array, index, T_BYTE); + Node* val = __ ConI(src_array->byte_at(i)); + __ store(__ ctrl(), adr, val, T_BYTE, byte_adr_idx, MemNode::unordered); + index = __ AddI(index, __ ConI(1)); + } + } + if (!dcon) { + __ else_(); + } + if (!dcon || !dbyte) { + // Destination is UTF16. Copy each char of src_array into dst_array. + Node* index = start; + for (int i = 0; i < length; i++) { + Node* adr = kit.array_element_address(dst_array, index, T_BYTE); + jchar val; + if (src_is_byte) { + val = src_array->byte_at(i); + } else { + val = readChar(src_array, i++); + } + __ store(__ ctrl(), adr, __ ConI(val), T_CHAR, byte_adr_idx, MemNode::unordered); + index = __ AddI(index, __ ConI(2)); + } + if (src_is_byte) { + // Multiply count by two since we now need two bytes per char + __ set(count, __ ConI(2 * length)); + } + } + if (!dcon) { + __ end_if(); } } +// Compress copy contents of the byte/char String str into dst_array starting at index start. +Node* PhaseStringOpts::copy_string(GraphKit& kit, Node* str, Node* dst_array, Node* dst_coder, Node* start) { + Node* src_array = kit.load_String_value(kit.control(), str); + + IdealKit ideal(&kit, true, true); + IdealVariable count(ideal); __ declarations_done(); + + if (str->is_Con()) { + // Constant source string + const TypeOopPtr* t = kit.gvn().type(src_array)->isa_oopptr(); + ciTypeArray* src_array_type = t->const_oop()->as_type_array(); -Node* PhaseStringOpts::copy_string(GraphKit& kit, Node* str, Node* char_array, Node* start) { - Node* string = str; - Node* offset = kit.load_String_offset(kit.control(), string); - Node* count = kit.load_String_length(kit.control(), string); - Node* value = kit.load_String_value (kit.control(), string); + // Check encoding of constant string + bool src_is_byte = (get_constant_coder(kit, str) == java_lang_String::CODER_LATIN1); - // copy the contents - if (offset->is_Con() && count->is_Con() && value->is_Con() && count->get_int() < unroll_string_copy_length) { // For small constant strings just emit individual stores. // A length of 6 seems like a good space/speed tradeof. - int c = count->get_int(); - int o = offset->get_int(); - const TypeOopPtr* t = kit.gvn().type(value)->isa_oopptr(); - ciTypeArray* value_array = t->const_oop()->as_type_array(); - for (int e = 0; e < c; e++) { - __ store_to_memory(kit.control(), kit.array_element_address(char_array, start, T_CHAR), - __ intcon(value_array->char_at(o + e)), T_CHAR, char_adr_idx, - MemNode::unordered); - start = __ AddI(start, __ intcon(1)); + __ set(count, __ ConI(src_array_type->length())); + int src_len = src_array_type->length() / (src_is_byte ? 1 : 2); + if (src_len < unroll_string_copy_length) { + // Small constant string + copy_constant_string(kit, ideal, src_array_type, count, src_is_byte, dst_array, dst_coder, start); + } else if (src_is_byte) { + // Source is Latin1 + copy_latin1_string(kit, ideal, src_array, count, dst_array, dst_coder, start); + } else { + // Source is UTF16 (destination too). Simply emit a char arraycopy. + arraycopy(kit, ideal, src_array, dst_array, T_CHAR, start, __ value(count)); } } else { - Node* src_ptr = kit.array_element_address(value, offset, T_CHAR); - Node* dst_ptr = kit.array_element_address(char_array, start, T_CHAR); - Node* c = count; - Node* extra = NULL; -#ifdef _LP64 - c = __ ConvI2L(c); - extra = C->top(); -#endif - Node* call = kit.make_runtime_call(GraphKit::RC_LEAF|GraphKit::RC_NO_FP, - OptoRuntime::fast_arraycopy_Type(), - CAST_FROM_FN_PTR(address, StubRoutines::jshort_disjoint_arraycopy()), - "jshort_disjoint_arraycopy", TypeAryPtr::CHARS, - src_ptr, dst_ptr, c, extra); - start = __ AddI(start, count); + Node* size = kit.load_array_length(src_array); + __ set(count, size); + // Non-constant source string + if (CompactStrings) { + // Emit runtime check for coder + Node* coder = kit.load_String_coder(__ ctrl(), str); + __ if_then(coder, BoolTest::eq, __ ConI(java_lang_String::CODER_LATIN1)); { + // Source is Latin1 + copy_latin1_string(kit, ideal, src_array, count, dst_array, dst_coder, start); + } __ else_(); + } + // Source is UTF16 (destination too). Simply emit a char arraycopy. + arraycopy(kit, ideal, src_array, dst_array, T_CHAR, start, __ value(count)); + + if (CompactStrings) { + __ end_if(); + } } - return start; + + // Finally sync IdealKit and GraphKit. + kit.sync_kit(ideal); + return __ AddI(start, __ value(count)); } +// Compress copy the char into dst_array at index start. +Node* PhaseStringOpts::copy_char(GraphKit& kit, Node* val, Node* dst_array, Node* dst_coder, Node* start) { + bool dcon = (dst_coder != NULL) && dst_coder->is_Con(); + bool dbyte = dcon ? (dst_coder->get_int() == java_lang_String::CODER_LATIN1) : false; + + IdealKit ideal(&kit, true, true); + IdealVariable end(ideal); __ declarations_done(); + Node* adr = kit.array_element_address(dst_array, start, T_BYTE); + if (!dcon){ + __ if_then(dst_coder, BoolTest::eq, __ ConI(java_lang_String::CODER_LATIN1)); + } + if (!dcon || dbyte) { + // Destination is Latin1. Store a byte. + __ store(__ ctrl(), adr, val, T_BYTE, byte_adr_idx, MemNode::unordered); + __ set(end, __ AddI(start, __ ConI(1))); + } + if (!dcon) { + __ else_(); + } + if (!dcon || !dbyte) { + // Destination is UTF16. Store a char. + __ store(__ ctrl(), adr, val, T_CHAR, byte_adr_idx, MemNode::unordered); + __ set(end, __ AddI(start, __ ConI(2))); + } + if (!dcon) { + __ end_if(); + } + // Finally sync IdealKit and GraphKit. + kit.sync_kit(ideal); + return __ value(end); +} + +#undef __ +#define __ kit. + +// Allocate a byte array of specified length. +Node* PhaseStringOpts::allocate_byte_array(GraphKit& kit, IdealKit* ideal, Node* length) { + if (ideal != NULL) { + // Sync IdealKit and graphKit. + kit.sync_kit(*ideal); + } + Node* byte_array = NULL; + { + PreserveReexecuteState preexecs(&kit); + // The original jvms is for an allocation of either a String or + // StringBuffer so no stack adjustment is necessary for proper + // reexecution. If we deoptimize in the slow path the bytecode + // will be reexecuted and the char[] allocation will be thrown away. + kit.jvms()->set_should_reexecute(true); + byte_array = kit.new_array(__ makecon(TypeKlassPtr::make(ciTypeArrayKlass::make(T_BYTE))), + length, 1); + } + + // Mark the allocation so that zeroing is skipped since the code + // below will overwrite the entire array + AllocateArrayNode* byte_alloc = AllocateArrayNode::Ideal_array_allocation(byte_array, _gvn); + byte_alloc->maybe_set_complete(_gvn); + + if (ideal != NULL) { + // Sync IdealKit and graphKit. + ideal->sync_kit(&kit); + } + return byte_array; +} + +jbyte PhaseStringOpts::get_constant_coder(GraphKit& kit, Node* str) { + assert(str->is_Con(), "String must be constant"); + const TypeOopPtr* str_type = kit.gvn().type(str)->isa_oopptr(); + ciInstance* str_instance = str_type->const_oop()->as_instance(); + jbyte coder = str_instance->field_value_by_offset(java_lang_String::coder_offset_in_bytes()).as_byte(); + assert(CompactStrings || (coder == java_lang_String::CODER_UTF16), "Strings must be UTF16 encoded"); + return coder; +} + +int PhaseStringOpts::get_constant_length(GraphKit& kit, Node* str) { + assert(str->is_Con(), "String must be constant"); + Node* src_array = kit.load_String_value(kit.control(), str); + const TypeOopPtr* t = kit.gvn().type(src_array)->isa_oopptr(); + return t->const_oop()->as_type_array()->length(); +} void PhaseStringOpts::replace_string_concat(StringConcat* sc) { // Log a little info about the transformation @@ -1445,7 +1704,6 @@ jvms->set_map(map); map->ensure_stack(jvms, jvms->method()->max_stack()); - // disconnect all the old StringBuilder calls from the graph sc->eliminate_unneeded_control(); @@ -1473,7 +1731,17 @@ // are need for the copying phase. Node* string_sizes = new Node(args); + Node* coder = __ intcon(0); Node* length = __ intcon(0); + // If at least one argument is UTF16 encoded, we can fix the encoding. + bool coder_fixed = false; + + if (!CompactStrings) { + // Fix encoding of result string to UTF16 + coder_fixed = true; + coder = __ intcon(java_lang_String::CODER_UTF16); + } + for (int argi = 0; argi < sc->num_arguments(); argi++) { Node* arg = sc->argument(argi); switch (sc->mode(argi)) { @@ -1491,7 +1759,7 @@ const Type* type = kit.gvn().type(arg); assert(type != TypePtr::NULL_PTR, "missing check"); if (!type->higher_equal(TypeInstPtr::NOTNULL)) { - // Null check with uncommont trap since + // Null check with uncommon trap since // StringBuilder(null) throws exception. // Use special uncommon trap instead of // calling normal do_null_check(). @@ -1509,11 +1777,13 @@ case StringConcat::StringMode: { const Type* type = kit.gvn().type(arg); Node* count = NULL; + Node* arg_coder = NULL; if (type == TypePtr::NULL_PTR) { // replace the argument with the null checked version arg = null_string; sc->set_argument(argi, arg); count = kit.load_String_length(kit.control(), arg); + arg_coder = kit.load_String_coder(kit.control(), arg); } else if (!type->higher_equal(TypeInstPtr::NOTNULL)) { // s = s != null ? s : "null"; // length = length + (s.count - s.offset); @@ -1537,11 +1807,32 @@ arg = phi; sc->set_argument(argi, arg); count = kit.load_String_length(kit.control(), arg); + arg_coder = kit.load_String_coder(kit.control(), arg); } else { // A corresponding nullcheck will be connected during IGVN MemNode::Ideal_common_DU_postCCP // kit.control might be a different test, that can be hoisted above the actual nullcheck // in case, that the control input is not null, Ideal_common_DU_postCCP will not look for a nullcheck. count = kit.load_String_length(NULL, arg); + arg_coder = kit.load_String_coder(NULL, arg); + } + if (arg->is_Con()) { + // Constant string. Get constant coder and length. + jbyte const_coder = get_constant_coder(kit, arg); + int const_length = get_constant_length(kit, arg); + if (const_coder == java_lang_String::CODER_LATIN1) { + // Can be latin1 encoded + arg_coder = __ intcon(const_coder); + count = __ intcon(const_length); + } else { + // Found UTF16 encoded string. Fix result array encoding to UTF16. + coder_fixed = true; + coder = __ intcon(const_coder); + count = __ intcon(const_length / 2); + } + } + + if (!coder_fixed) { + coder = __ OrI(coder, arg_coder); } length = __ AddI(length, count); string_sizes->init_req(argi, NULL); @@ -1549,6 +1840,34 @@ } case StringConcat::CharMode: { // one character only + const TypeInt* t = kit.gvn().type(arg)->is_int(); + if (!coder_fixed && t->is_con()) { + // Constant char + if (t->get_con() <= 255) { + // Can be latin1 encoded + coder = __ OrI(coder, __ intcon(java_lang_String::CODER_LATIN1)); + } else { + // Must be UTF16 encoded. Fix result array encoding to UTF16. + coder_fixed = true; + coder = __ intcon(java_lang_String::CODER_UTF16); + } + } else if (!coder_fixed) { + // Not constant +#undef __ +#define __ ideal. + IdealKit ideal(&kit, true, true); + IdealVariable char_coder(ideal); __ declarations_done(); + // Check if character can be latin1 encoded + __ if_then(arg, BoolTest::le, __ ConI(0xFF)); + __ set(char_coder, __ ConI(java_lang_String::CODER_LATIN1)); + __ else_(); + __ set(char_coder, __ ConI(java_lang_String::CODER_UTF16)); + __ end_if(); + kit.sync_kit(ideal); + coder = __ OrI(coder, __ value(char_coder)); +#undef __ +#define __ kit. + } length = __ AddI(length, __ intcon(1)); break; } @@ -1576,54 +1895,37 @@ Node* result; if (!kit.stopped()) { - Node* char_array = NULL; + assert(CompactStrings || (coder->is_Con() && coder->get_int() == java_lang_String::CODER_UTF16), + "Result string must be UTF16 encoded if CompactStrings is disabled"); + + Node* dst_array = NULL; if (sc->num_arguments() == 1 && - (sc->mode(0) == StringConcat::StringMode || - sc->mode(0) == StringConcat::StringNullCheckMode)) { + (sc->mode(0) == StringConcat::StringMode || + sc->mode(0) == StringConcat::StringNullCheckMode)) { // Handle the case when there is only a single String argument. // In this case, we can just pull the value from the String itself. - char_array = kit.load_String_value(kit.control(), sc->argument(0)); + dst_array = kit.load_String_value(kit.control(), sc->argument(0)); } else { - // length now contains the number of characters needed for the - // char[] so create a new AllocateArray for the char[] - { - PreserveReexecuteState preexecs(&kit); - // The original jvms is for an allocation of either a String or - // StringBuffer so no stack adjustment is necessary for proper - // reexecution. If we deoptimize in the slow path the bytecode - // will be reexecuted and the char[] allocation will be thrown away. - kit.jvms()->set_should_reexecute(true); - char_array = kit.new_array(__ makecon(TypeKlassPtr::make(ciTypeArrayKlass::make(T_CHAR))), - length, 1); - } - - // Mark the allocation so that zeroing is skipped since the code - // below will overwrite the entire array - AllocateArrayNode* char_alloc = AllocateArrayNode::Ideal_array_allocation(char_array, _gvn); - char_alloc->maybe_set_complete(_gvn); + // Allocate destination byte array according to coder + dst_array = allocate_byte_array(kit, NULL, __ LShiftI(length, coder)); - // Now copy the string representations into the final char[] + // Now copy the string representations into the final byte[] Node* start = __ intcon(0); for (int argi = 0; argi < sc->num_arguments(); argi++) { Node* arg = sc->argument(argi); switch (sc->mode(argi)) { case StringConcat::IntMode: { - Node* end = __ AddI(start, string_sizes->in(argi)); - // getChars words backwards so pass the ending point as well as the start - int_getChars(kit, arg, char_array, start, end); - start = end; + start = int_getChars(kit, arg, dst_array, coder, start, string_sizes->in(argi)); break; } case StringConcat::StringNullCheckMode: case StringConcat::StringMode: { - start = copy_string(kit, arg, char_array, start); + start = copy_string(kit, arg, dst_array, coder, start); break; } case StringConcat::CharMode: { - __ store_to_memory(kit.control(), kit.array_element_address(char_array, start, T_CHAR), - arg, T_CHAR, char_adr_idx, MemNode::unordered); - start = __ AddI(start, __ intcon(1)); - break; + start = copy_char(kit, arg, dst_array, coder, start); + break; } default: ShouldNotReachHere(); @@ -1642,12 +1944,9 @@ result = kit.new_instance(__ makecon(TypeKlassPtr::make(C->env()->String_klass()))); } - // Intialize the string - if (java_lang_String::has_offset_field()) { - kit.store_String_offset(kit.control(), result, __ intcon(0)); - kit.store_String_length(kit.control(), result, length); - } - kit.store_String_value(kit.control(), result, char_array); + // Initialize the string + kit.store_String_value(kit.control(), result, dst_array); + kit.store_String_coder(kit.control(), result, coder); } else { result = C->top(); } --- old/src/share/vm/opto/stringopts.hpp 2015-10-30 19:14:56.760475807 +0100 +++ new/src/share/vm/opto/stringopts.hpp 2015-10-30 19:14:56.696475805 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 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 @@ -29,6 +29,7 @@ #include "opto/phaseX.hpp" class StringConcat; +class IdealVariable; class PhaseStringOpts : public Phase { friend class StringConcat; @@ -40,7 +41,7 @@ Unique_Node_List dead_worklist; // Memory slices needed for code gen - int char_adr_idx; + int byte_adr_idx; // Integer.sizeTable - used for int to String conversion ciField* size_table_field; @@ -64,11 +65,37 @@ // Compute the number of characters required to represent the int value Node* int_stringSize(GraphKit& kit, Node* value); - // Copy the characters representing value into char_array starting at start - void int_getChars(GraphKit& kit, Node* value, Node* char_array, Node* start, Node* end); + // Simplified version of Integer.getChars + void getChars(GraphKit& kit, Node* arg, Node* dst_array, BasicType bt, Node* end, Node* final_merge, Node* final_mem, int merge_index = 0); - // Copy of the contents of the String str into char_array starting at index start. - Node* copy_string(GraphKit& kit, Node* str, Node* char_array, Node* start); + // Copy the characters representing arg into dst_array starting at start + Node* int_getChars(GraphKit& kit, Node* arg, Node* dst_array, Node* dst_coder, Node* start, Node* size); + + // Copy contents of the String str into dst_array starting at index start. + Node* copy_string(GraphKit& kit, Node* str, Node* dst_array, Node* dst_coder, Node* start); + + // Copy 'count' bytes/chars from src_array to dst_array starting at index start + void arraycopy(GraphKit& kit, IdealKit& ideal, Node* src_array, Node* dst_array, BasicType elembt, Node* start, Node* count); + + // Copy contents of constant src_array to dst_array by emitting individual stores + void copy_constant_string(GraphKit& kit, IdealKit& ideal, ciTypeArray* src_array, IdealVariable& count, + bool src_is_byte, Node* dst_array, Node* dst_coder, Node* start); + + // Copy contents of a Latin1 encoded string from src_array to dst_array + void copy_latin1_string(GraphKit& kit, IdealKit& ideal, Node* src_array, IdealVariable& count, + Node* dst_array, Node* dst_coder, Node* start); + + // Copy the char into dst_array at index start. + Node* copy_char(GraphKit& kit, Node* val, Node* dst_array, Node* dst_coder, Node* start); + + // Allocate a byte array of specified length. + Node* allocate_byte_array(GraphKit& kit, IdealKit* ideal, Node* length); + + // Returns the coder of a constant string + jbyte get_constant_coder(GraphKit& kit, Node* str); + + // Returns the length of a constant string + int get_constant_length(GraphKit& kit, Node* str); // Clean up any leftover nodes void record_dead_node(Node* node); --- old/src/share/vm/prims/jni.cpp 2015-10-30 19:14:57.044475816 +0100 +++ new/src/share/vm/prims/jni.cpp 2015-10-30 19:14:56.972475814 +0100 @@ -2474,12 +2474,18 @@ typeArrayOop s_value = java_lang_String::value(s); if (s_value != NULL) { int s_len = java_lang_String::length(s); - int s_offset = java_lang_String::offset(s); + bool is_latin1 = java_lang_String::is_latin1(s); buf = NEW_C_HEAP_ARRAY_RETURN_NULL(jchar, s_len + 1, mtInternal); // add one for zero termination /* JNI Specification states return NULL on OOM */ if (buf != NULL) { if (s_len > 0) { - memcpy(buf, s_value->char_at_addr(s_offset), sizeof(jchar)*s_len); + if (!is_latin1) { + memcpy(buf, s_value->char_at_addr(0), sizeof(jchar)*s_len); + } else { + for (int i = 0; i < s_len; i++) { + buf[i] = ((jchar) s_value->byte_at(i)) & 0xff; + } + } } buf[s_len] = 0; //%note jni_5 @@ -3118,9 +3124,15 @@ THROW(vmSymbols::java_lang_StringIndexOutOfBoundsException()); } else { if (len > 0) { - int s_offset = java_lang_String::offset(s); typeArrayOop s_value = java_lang_String::value(s); - memcpy(buf, s_value->char_at_addr(s_offset+start), sizeof(jchar)*len); + bool is_latin1 = java_lang_String::is_latin1(s); + if (!is_latin1) { + memcpy(buf, s_value->char_at_addr(start), sizeof(jchar)*len); + } else { + for (int i = 0; i < len; i++) { + buf[i] = ((jchar) s_value->byte_at(i + start)) & 0xff; + } + } } } JNI_END @@ -3186,18 +3198,23 @@ JNIWrapper("GetStringCritical"); HOTSPOT_JNI_GETSTRINGCRITICAL_ENTRY(env, string, (uintptr_t *) isCopy); GC_locker::lock_critical(thread); - if (isCopy != NULL) { - *isCopy = JNI_FALSE; - } oop s = JNIHandles::resolve_non_null(string); - int s_len = java_lang_String::length(s); typeArrayOop s_value = java_lang_String::value(s); - int s_offset = java_lang_String::offset(s); + bool is_latin1 = java_lang_String::is_latin1(s); + if (isCopy != NULL) { + *isCopy = is_latin1 ? JNI_TRUE : JNI_FALSE; + } const jchar* ret; - if (s_len > 0) { - ret = s_value->char_at_addr(s_offset); + if (!is_latin1) { + ret = s_value->char_at_addr(0); } else { - ret = (jchar*) s_value->base(T_CHAR); + // Inflate latin1 encoded string to UTF16 + int s_len = java_lang_String::length(s); + jchar* buf = NEW_C_HEAP_ARRAY(jchar, s_len, mtInternal); + for (int i = 0; i < s_len; i++) { + buf[i] = ((jchar) s_value->byte_at(i)) & 0xff; + } + ret = &buf[0]; } HOTSPOT_JNI_GETSTRINGCRITICAL_RETURN((uint16_t *) ret); return ret; @@ -3207,7 +3224,14 @@ JNI_ENTRY(void, jni_ReleaseStringCritical(JNIEnv *env, jstring str, const jchar *chars)) JNIWrapper("ReleaseStringCritical"); HOTSPOT_JNI_RELEASESTRINGCRITICAL_ENTRY(env, str, (uint16_t *) chars); - // The str and chars arguments are ignored + // The str and chars arguments are ignored for UTF16 strings + oop s = JNIHandles::resolve_non_null(str); + bool is_latin1 = java_lang_String::is_latin1(s); + if (is_latin1) { + // For latin1 string, free jchar array allocated by earlier call to GetStringCritical. + // This assumes that ReleaseStringCritical bookends GetStringCritical. + FREE_C_HEAP_ARRAY(jchar, chars); + } GC_locker::unlock_critical(thread); HOTSPOT_JNI_RELEASESTRINGCRITICAL_RETURN(); JNI_END --- old/src/share/vm/prims/jvmtiEnv.cpp 2015-10-30 19:14:57.316475825 +0100 +++ new/src/share/vm/prims/jvmtiEnv.cpp 2015-10-30 19:14:57.248475823 +0100 @@ -966,7 +966,7 @@ if (name() != NULL) { n = java_lang_String::as_utf8_string(name()); } else { - n = UNICODE::as_utf8(NULL, 0); + n = UNICODE::as_utf8((jchar*) NULL, 0); } info_ptr->name = (char *) jvmtiMalloc(strlen(n)+1); @@ -1187,15 +1187,14 @@ Handle group_obj (current_thread, JNIHandles::resolve_external_guard(group)); NULL_CHECK(group_obj(), JVMTI_ERROR_INVALID_THREAD_GROUP); - typeArrayHandle name; + const char* name; Handle parent_group; bool is_daemon; ThreadPriority max_priority; { MutexLocker mu(Threads_lock); - name = typeArrayHandle(current_thread, - java_lang_ThreadGroup::name(group_obj())); + name = java_lang_ThreadGroup::name(group_obj()); parent_group = Handle(current_thread, java_lang_ThreadGroup::parent(group_obj())); is_daemon = java_lang_ThreadGroup::is_daemon(group_obj()); max_priority = java_lang_ThreadGroup::maxPriority(group_obj()); @@ -1205,11 +1204,10 @@ info_ptr->max_priority = max_priority; info_ptr->parent = jni_reference(parent_group); - if (name() != NULL) { - const char* n = UNICODE::as_utf8((jchar*) name->base(T_CHAR), name->length()); - info_ptr->name = (char *)jvmtiMalloc(strlen(n)+1); + if (name != NULL) { + info_ptr->name = (char*)jvmtiMalloc(strlen(name)+1); NULL_CHECK(info_ptr->name, JVMTI_ERROR_OUT_OF_MEMORY); - strcpy(info_ptr->name, n); + strcpy(info_ptr->name, name); } else { info_ptr->name = NULL; } --- old/src/share/vm/prims/jvmtiTagMap.cpp 2015-10-30 19:14:57.580475833 +0100 +++ new/src/share/vm/prims/jvmtiTagMap.cpp 2015-10-30 19:14:57.516475831 +0100 @@ -1057,21 +1057,36 @@ // get the string value and length // (string value may be offset from the base) int s_len = java_lang_String::length(str); - int s_offset = java_lang_String::offset(str); + bool is_latin1 = java_lang_String::is_latin1(str); jchar* value; if (s_len > 0) { - value = s_value->char_at_addr(s_offset); + if (!is_latin1) { + value = s_value->char_at_addr(0); + } else { + // Inflate latin1 encoded string to UTF16 + jchar* buf = NEW_C_HEAP_ARRAY(jchar, s_len, mtInternal); + for (int i = 0; i < s_len; i++) { + buf[i] = ((jchar) s_value->byte_at(i)) & 0xff; + } + value = &buf[0]; + } } else { + // Don't use char_at_addr(0) if length is 0 value = (jchar*) s_value->base(T_CHAR); } // invoke the callback - return (*cb)(wrapper->klass_tag(), - wrapper->obj_size(), - wrapper->obj_tag_p(), - value, - (jint)s_len, - user_data); + jint res = (*cb)(wrapper->klass_tag(), + wrapper->obj_size(), + wrapper->obj_tag_p(), + value, + (jint)s_len, + user_data); + + if (is_latin1 && s_len > 0) { + FREE_C_HEAP_ARRAY(jchar, value); + } + return res; } // helper function to invoke string primitive value callback --- old/src/share/vm/runtime/globals.hpp 2015-10-30 19:14:57.852475842 +0100 +++ new/src/share/vm/runtime/globals.hpp 2015-10-30 19:14:57.776475839 +0100 @@ -2889,6 +2889,9 @@ product(bool, AggressiveOpts, false, \ "Enable aggressive optimizations - see arguments.cpp") \ \ + product_pd(bool, CompactStrings, \ + "Enable Strings to use single byte chars in backing store") \ + \ product_pd(uintx, TypeProfileLevel, \ "=XYZ, with Z: Type profiling of arguments at call; " \ "Y: Type profiling of return value at call; " \ @@ -4256,6 +4259,9 @@ "Use the FP register for holding the frame pointer " \ "and not as a general purpose register.") \ \ + diagnostic(bool, StringCharIntrinsics, true, \ + "Inline String*.getChar/putChar intrinsics.") \ + \ diagnostic(bool, CheckIntrinsics, true, \ "When a class C is loaded, check that " \ "(1) all intrinsics defined by the VM for class C are present "\ --- old/src/share/vm/runtime/thread.cpp 2015-10-30 19:14:58.152475851 +0100 +++ new/src/share/vm/runtime/thread.cpp 2015-10-30 19:14:58.084475849 +0100 @@ -2933,12 +2933,8 @@ if (thread_obj != NULL) { oop thread_group = java_lang_Thread::threadGroup(thread_obj); if (thread_group != NULL) { - typeArrayOop name = java_lang_ThreadGroup::name(thread_group); // ThreadGroup.name can be null - if (name != NULL) { - const char* str = UNICODE::as_utf8((jchar*) name->base(T_CHAR), name->length()); - return str; - } + return java_lang_ThreadGroup::name(thread_group); } } return NULL; @@ -2952,12 +2948,8 @@ if (thread_group != NULL) { oop parent = java_lang_ThreadGroup::parent(thread_group); if (parent != NULL) { - typeArrayOop name = java_lang_ThreadGroup::name(parent); // ThreadGroup.name can be null - if (name != NULL) { - const char* str = UNICODE::as_utf8((jchar*) name->base(T_CHAR), name->length()); - return str; - } + return java_lang_ThreadGroup::name(parent); } } } @@ -3304,6 +3296,9 @@ initialize_class(vmSymbols::java_lang_String(), CHECK); + // Inject CompactStrings value after the static initializers for String ran. + java_lang_String::set_compact_strings(CompactStrings); + // Initialize java_lang.System (needed before creating the thread) initialize_class(vmSymbols::java_lang_System(), CHECK); // The VM creates & returns objects of this class. Make sure it's initialized. --- old/src/share/vm/utilities/utf8.cpp 2015-10-30 19:14:58.432475860 +0100 +++ new/src/share/vm/utilities/utf8.cpp 2015-10-30 19:14:58.368475858 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -27,7 +27,7 @@ // Assume the utf8 string is in legal form and has been // checked in the class file parser/format checker. -char* UTF8::next(const char* str, jchar* value) { +template char* UTF8::next(const char* str, T* value) { unsigned const char *ptr = (const unsigned char *)str; unsigned char ch, ch2, ch3; int length = -1; /* bad length */ @@ -68,11 +68,11 @@ } /* end of switch */ if (length <= 0) { - *value = ptr[0]; /* default bad result; */ + *value = (T)ptr[0]; /* default bad result; */ return (char*)(ptr + 1); // make progress somehow } - *value = result; + *value = (T)result; // The assert is correct but the .class file is wrong // assert(UNICODE::utf8_size(result) == length, "checking reverse computation"); @@ -96,12 +96,22 @@ // Count bytes of the form 10xxxxxx and deduct this count // from the total byte count. The utf8 string must be in // legal form which has been verified in the format checker. -int UTF8::unicode_length(const char* str, int len) { +int UTF8::unicode_length(const char* str, int len, bool& is_latin1, bool& has_multibyte) { int num_chars = len; + has_multibyte = false; + is_latin1 = true; + unsigned char prev = 0; for (int i = 0; i < len; i++) { - if ((str[i] & 0xC0) == 0x80) { + unsigned char c = str[i]; + if ((c & 0xC0) == 0x80) { + // Multibyte, check if valid latin1 character. + has_multibyte = true; + if (prev > 0xC3) { + is_latin1 = false; + } --num_chars; } + prev = c; } return num_chars; } @@ -110,17 +120,28 @@ // 10xxxxxx which only appear in multibyte characters. // The utf8 string must be in legal form and has been // verified in the format checker. -int UTF8::unicode_length(const char* str) { +int UTF8::unicode_length(const char* str, bool& is_latin1, bool& has_multibyte) { int num_chars = 0; + has_multibyte = false; + is_latin1 = true; + unsigned char prev = 0; for (const char* p = str; *p; p++) { - if (((*p) & 0xC0) != 0x80) { + unsigned char c = (*p); + if ((c & 0xC0) == 0x80) { + // Multibyte, check if valid latin1 character. + has_multibyte = true; + if (prev > 0xC3) { + is_latin1 = false; + } + } else { num_chars++; } + prev = c; } return num_chars; } -// Writes a jchar a utf8 and returns the end +// Writes a jchar as utf8 and returns the end static u_char* utf8_write(u_char* base, jchar ch) { if ((ch != 0) && (ch <=0x7f)) { base[0] = (u_char) ch; @@ -145,7 +166,7 @@ return base + 3; } -void UTF8::convert_to_unicode(const char* utf8_str, jchar* unicode_str, int unicode_length) { +template void UTF8::convert_to_unicode(const char* utf8_str, T* unicode_str, int unicode_length) { unsigned char ch; const char *ptr = utf8_str; int index = 0; @@ -153,7 +174,7 @@ /* ASCII case loop optimization */ for (; index < unicode_length; index++) { if((ch = ptr[0]) > 0x7F) { break; } - unicode_str[index] = ch; + unicode_str[index] = (T)ch; ptr = (const char *)(ptr + 1); } @@ -162,6 +183,12 @@ } } +// Explicit instantiation for all supported string types. +template char* UTF8::next(const char* str, jchar* value); +template char* UTF8::next(const char* str, jbyte* value); +template void UTF8::convert_to_unicode(const char* utf8_str, jchar* unicode_str, int unicode_length); +template void UTF8::convert_to_unicode(const char* utf8_str, jbyte* unicode_str, int unicode_length); + // returns the quoted ascii length of a 0-terminated utf8 string int UTF8::quoted_ascii_length(const char* utf8_str, int utf8_length) { const char *ptr = utf8_str; @@ -306,9 +333,20 @@ + ((str[4] & 0x0f) << 6) + (str[5] & 0x3f); } - //------------------------------------------------------------------------------------- +bool UNICODE::is_latin1(jchar c) { + return (c <= 0x00FF); +} + +bool UNICODE::is_latin1(jchar* base, int length) { + for (int index = 0; index < length; index++) { + if (base[index] > 0x00FF) { + return false; + } + } + return true; +} int UNICODE::utf8_size(jchar c) { if ((0x0001 <= c) && (c <= 0x007F)) return 1; @@ -316,6 +354,11 @@ return 3; } +int UNICODE::utf8_size(jbyte c) { + if (c >= 0x0001) return 1; + return 2; +} + int UNICODE::utf8_length(jchar* base, int length) { int result = 0; for (int index = 0; index < length; index++) { @@ -327,6 +370,15 @@ return result; } +int UNICODE::utf8_length(jbyte* base, int length) { + int result = 0; + for (int index = 0; index < length; index++) { + jbyte c = base[index]; + result += utf8_size(c); + } + return result; +} + char* UNICODE::as_utf8(jchar* base, int length) { int utf8_len = utf8_length(base, length); u_char* buf = NEW_RESOURCE_ARRAY(u_char, utf8_len + 1); @@ -335,6 +387,26 @@ return result; } +char* UNICODE::as_utf8(jbyte* base, int length) { + int utf8_len = utf8_length(base, length); + u_char* result = NEW_RESOURCE_ARRAY(u_char, utf8_len + 1); + u_char* p = result; + if (utf8_len == length) { + for (int index = 0; index < length; index++) { + *p++ = base[index]; + } + } else { + // Unicode string contains U+0000 which should + // be encoded as 0xC080 in "modified" UTF8. + for (int index = 0; index < length; index++) { + p = utf8_write(p, ((jchar) base[index]) & 0xff); + } + } + *p = '\0'; + assert(p == &result[utf8_len], "length prediction must be correct"); + return (char*) result; +} + char* UNICODE::as_utf8(jchar* base, int length, char* buf, int buflen) { u_char* p = (u_char*)buf; for (int index = 0; index < length; index++) { @@ -347,6 +419,26 @@ return buf; } +char* UNICODE::as_utf8(jbyte* base, int length, char* buf, int buflen) { + u_char* p = (u_char*)buf; + u_char* end = (u_char*)buf + buflen; + for (int index = 0; index < length; index++) { + jbyte c = base[index]; + int sz = utf8_size(c); + buflen -= sz; + if (buflen <= 0) break; // string is truncated + if (sz == 1) { + *p++ = c; + } else { + // Unicode string contains U+0000 which should + // be encoded as 0xC080 in "modified" UTF8. + p = utf8_write(p, ((jchar) c) & 0xff); + } + } + *p = '\0'; + return buf; +} + void UNICODE::convert_to_utf8(const jchar* base, int length, char* utf8_buffer) { for(int index = 0; index < length; index++) { utf8_buffer = (char*)utf8_write((u_char*)utf8_buffer, base[index]); @@ -355,10 +447,11 @@ } // returns the quoted ascii length of a unicode string -int UNICODE::quoted_ascii_length(jchar* base, int length) { +template +int UNICODE::quoted_ascii_length(T* base, int length) { int result = 0; for (int i = 0; i < length; i++) { - jchar c = base[i]; + T c = base[i]; if (c >= 32 && c < 127) { result++; } else { @@ -368,12 +461,13 @@ return result; } -// converts a utf8 string to quoted ascii -void UNICODE::as_quoted_ascii(const jchar* base, int length, char* buf, int buflen) { +// converts a unicode string to quoted ascii +template +void UNICODE::as_quoted_ascii(const T* base, int length, char* buf, int buflen) { char* p = buf; char* end = buf + buflen; for (int index = 0; index < length; index++) { - jchar c = base[index]; + T c = base[index]; if (c >= 32 && c < 127) { if (p + 1 >= end) break; // string is truncated *p++ = (char)c; @@ -386,6 +480,13 @@ *p = '\0'; } +// Explicit instantiation for all supported types. +template int UNICODE::quoted_ascii_length(jbyte* base, int length); +template int UNICODE::quoted_ascii_length(jchar* base, int length); +template void UNICODE::as_quoted_ascii(const jbyte* base, int length, char* buf, int buflen); +template void UNICODE::as_quoted_ascii(const jchar* base, int length, char* buf, int buflen); + + #ifndef PRODUCT void TestAsUtf8() { char res[60]; --- old/src/share/vm/utilities/utf8.hpp 2015-10-30 19:14:58.688475868 +0100 +++ new/src/share/vm/utilities/utf8.hpp 2015-10-30 19:14:58.620475866 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -33,13 +33,21 @@ class UTF8 : AllStatic { public: // returns the unicode length of a 0-terminated utf8 string - static int unicode_length(const char* utf8_str); + static int unicode_length(const char* utf8_str) { + bool is_latin1, has_multibyte; + return unicode_length(utf8_str, is_latin1, has_multibyte); + } + static int unicode_length(const char* utf8_str, bool& is_latin1, bool& has_multibyte); // returns the unicode length of a non-0-terminated utf8 string - static int unicode_length(const char* utf8_str, int len); + static int unicode_length(const char* utf8_str, int len) { + bool is_latin1, has_multibyte; + return unicode_length(utf8_str, len, is_latin1, has_multibyte); + } + static int unicode_length(const char* utf8_str, int len, bool& is_latin1, bool& has_multibyte); // converts a utf8 string to a unicode string - static void convert_to_unicode(const char* utf8_str, jchar* unicode_buffer, int unicode_length); + template static void convert_to_unicode(const char* utf8_str, T* unicode_str, int unicode_length); // returns the quoted ascii length of a utf8 string static int quoted_ascii_length(const char* utf8_str, int utf8_length); @@ -53,7 +61,7 @@ // decodes the current utf8 character, stores the result in value, // and returns the end of the current utf8 chararacter. - static char* next(const char* str, jchar* value); + template static char* next(const char* str, T* value); // decodes the current utf8 character, gets the supplementary character instead of // the surrogate pair when seeing a supplementary character in string, @@ -76,11 +84,19 @@ class UNICODE : AllStatic { public: + // checks if the given unicode character can be encoded as latin1 + static bool is_latin1(jchar c); + + // checks if the given string can be encoded as latin1 + static bool is_latin1(jchar* base, int length); + // returns the utf8 size of a unicode character static int utf8_size(jchar c); + static int utf8_size(jbyte c); // returns the utf8 length of a unicode string static int utf8_length(jchar* base, int length); + static int utf8_length(jbyte* base, int length); // converts a unicode string to utf8 string static void convert_to_utf8(const jchar* base, int length, char* utf8_buffer); @@ -88,13 +104,15 @@ // converts a unicode string to a utf8 string; result is allocated // in resource area unless a buffer is provided. static char* as_utf8(jchar* base, int length); + static char* as_utf8(jbyte* base, int length); static char* as_utf8(jchar* base, int length, char* buf, int buflen); + static char* as_utf8(jbyte* base, int length, char* buf, int buflen); // returns the quoted ascii length of a unicode string - static int quoted_ascii_length(jchar* base, int length); + template static int quoted_ascii_length(T* base, int length); - // converts a utf8 string to quoted ascii - static void as_quoted_ascii(const jchar* base, int length, char* buf, int buflen); + // converts a unicode string to quoted ascii + template static void as_quoted_ascii(const T* base, int length, char* buf, int buflen); }; #endif // SHARE_VM_UTILITIES_UTF8_HPP --- /dev/null 2015-10-30 10:00:53.843909024 +0100 +++ new/test/compiler/intrinsics/string/TestStringIntrinsics.java 2015-10-30 19:14:58.864475874 +0100 @@ -0,0 +1,435 @@ +/* + * 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. + */ + +import java.lang.annotation.*; +import java.lang.reflect.*; +import java.util.Arrays; + +/* + * @test + * @bug 8054307 + * @summary Tests correctness of string related intrinsics and C2 optimizations. + * @run main/timeout=240 TestStringIntrinsics + */ +public class TestStringIntrinsics { + + public enum Operation { + ARR_EQUALS_B, ARR_EQUALS_C, EQUALS, COMPARE_TO, INDEX_OF, INDEX_OF_CON_U, INDEX_OF_CON_L, + INDEX_OF_CON_UL, CONCAT, CONCAT_C, CONCAT_I, CONCAT_M, INDEX_OF_CHAR + } + + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.METHOD) + @interface Test { + Operation op(); + String constString() default ""; + String[] inStrings() default {}; + char[] inChars() default {}; + int[] inInts() default {}; + String[] outStrings() default {}; + } + + public static void main(String[] args) throws Exception { + new TestStringIntrinsics().run(); + } + + public void run() throws Exception { + // Build latin1 and UTF16 strings + StringBuilder latin1Builder = new StringBuilder(); + for (int i = 0; i <= 255; ++i) { + latin1Builder.append((char) i); + } + String latin1 = latin1Builder.toString(); + StringBuilder utf16Builder = new StringBuilder(); + for (int i = 0; i <= 10000; ++i) { + utf16Builder.append((char) i); + } + String utf16 = utf16Builder.toString(); + + // Invoke test methods + for (Method m : TestStringIntrinsics.class.getMethods()) { + if (m.isAnnotationPresent(Test.class)) { + System.out.print("Checking " + m.getName() + "... "); + Operation op = m.getAnnotation(Test.class).op(); + Test antn = m.getAnnotation(Test.class); + if (isStringConcatTest(op)) { + checkStringConcat(op, m, antn); + } else { + checkIntrinsics(op, m, latin1, utf16, antn); + } + System.out.println("Done."); + } + } + } + + private boolean isStringConcatTest(Operation op) { + return op == Operation.CONCAT || + op == Operation.CONCAT_C || + op == Operation.CONCAT_I || + op == Operation.CONCAT_M; + } + + /** + * Checks correctness of the String.equals, String.compareTo and String.indexOf intrinsics. + * -XX:SpecialStringEquals + * -XX:SpecialStringCompareTo + * -XX:SpecialStringIndexOf + */ + private void checkIntrinsics(Operation op, Method m, String latin1, String utf16, Test antn) throws Exception { + for (int i = 0; i < 50_000; ++i) { + // Copy and permute latin1 and UTF16 string + char[] arrL = latin1.toCharArray(); + int indexL = i % arrL.length; + int mod = (arrL.length - arrL[indexL]); + int incL = i % ((mod != 0) ? mod : 1); + arrL[indexL] = (char) ((int) arrL[indexL] + incL); + String latin1Copy = String.valueOf(arrL); + + char[] arrU = utf16.toCharArray(); + int indexU = i % arrU.length; + mod = (arrU.length - arrU[indexU]); + int incU = i % ((mod != 0) ? mod : 1); + arrU[indexU] = (char) ((int) arrU[indexU] + incU); + String utf16Copy = String.valueOf(arrU); + + switch (op) { + case ARR_EQUALS_B: + invokeAndCheck(m, (incL == 0), latin1.getBytes("ISO-8859-1"), latin1Copy.getBytes("ISO-8859-1")); + invokeAndCheck(m, true, new byte[] {1, 2, 3}, new byte[] {1, 2, 3}); + invokeAndCheck(m, true, new byte[] {1}, new byte[] {1}); + invokeAndCheck(m, true, new byte[] {}, new byte[] {}); + break; + case ARR_EQUALS_C: + invokeAndCheck(m, (incU == 0), utf16.toCharArray(), arrU); + break; + case EQUALS: + invokeAndCheck(m, (incL == 0), latin1, latin1Copy); + invokeAndCheck(m, false, latin1, ""); + invokeAndCheck(m, false, "", latin1); + + invokeAndCheck(m, (incU == 0), utf16, utf16Copy); + invokeAndCheck(m, false, utf16, ""); + invokeAndCheck(m, false, "", utf16); + + invokeAndCheck(m, false, latin1, utf16); + break; + case COMPARE_TO: + invokeAndCheck(m, -incL, latin1, latin1Copy); + invokeAndCheck(m, latin1.length(), latin1, ""); + + invokeAndCheck(m, -incU, utf16, utf16Copy); + invokeAndCheck(m, utf16.length(), utf16, ""); + + // Cross coder + char cL = latin1.charAt(indexL); + char cU = utf16.charAt(indexU); + invokeAndCheck(m, cL - cU, latin1, latin1.replace(cL, cU)); + invokeAndCheck(m, cU - cL, utf16, utf16.replace(cU, cL)); + + // Different lengths + invokeAndCheck(m, 1, "ABCD", "ABC"); + invokeAndCheck(m, -1, "\uff21\uff22\uff23", "\uff21\uff22\uff23\uff24"); + invokeAndCheck(m, 1, "ABC\uff24", "ABC"); + invokeAndCheck(m, 3, "ABC\uff24\uff25\uff26", "ABC"); + invokeAndCheck(m, -1, "ABC","ABC\uff24"); + invokeAndCheck(m, -3, "ABC","ABC\uff24\uff25\uff26"); + break; + case INDEX_OF: + invokeAndCheck(m, indexL, latin1, latin1.substring(indexL), (indexL > 42) ? 42 : 0); + invokeAndCheck(m, 0, latin1, "", 0); + + invokeAndCheck(m, indexU, utf16, utf16.substring(indexU), (indexU > 42) ? 42 : 0); + invokeAndCheck(m, 0, utf16, "", 0); + + // Cross coder + invokeAndCheck(m, -1, latin1.substring(0, indexL), utf16.substring(indexU), (indexL > 42) ? 42 : 0); + // Skip latin1 chars in utf16 string + int start = 256; + int end = indexU > start ? indexU : start; + invokeAndCheck(m, end-start, utf16.substring(start, end) + latin1.substring(indexL), latin1.substring(indexL), 0); + break; + case INDEX_OF_CON_L: + invokeAndCheck(m, antn.constString(), latin1); + break; + case INDEX_OF_CON_U: + invokeAndCheck(m, antn.constString(), utf16); + break; + case INDEX_OF_CON_UL: + invokeAndCheck(m, antn.constString(), utf16); + break; + case INDEX_OF_CHAR: + invokeAndCheck(m, 7, "abcdefg\uD800\uDC00", 65536, 0); + invokeAndCheck(m, -1, "abcdefg\uD800\uDC01", 65536, 0); + invokeAndCheck(m, -1, "abcdefg\uD800", 65536, 0); + invokeAndCheck(m, 3, "abc\u0107", 263, 0); + invokeAndCheck(m, -1, "abc\u0108", 263, 0); + invokeAndCheck(m, 7, "abcdefg\u0107", 263, 0); + invokeAndCheck(m, 7, "abcdefg\u0107", 263, -1); + invokeAndCheck(m, 0, "\u0107", 263, 0); + break; + default: + throw new RuntimeException("Unexpected operation."); + } + } + } + + /** + * Checks correctness of the C2 string concatenation optimization. + * -XX:OptimizeStringConcat + */ + private void checkStringConcat(Operation op, Method m, Test antn) throws Exception { + for (int i = 0; i < 50_000; ++i) { + String[] result = antn.outStrings(); + switch(op) { + case CONCAT: + String[] strs = antn.inStrings(); + for (int j = 0; j < strs.length; ++j) { + invokeAndCheck(m, result[j], strs[j]); + } + break; + case CONCAT_C: + char[] ch = antn.inChars(); + for (int j = 0; j < ch.length; ++j) { + invokeAndCheck(m, result[j], ch[j]); + } + break; + case CONCAT_I: + int[] k = antn.inInts(); + for (int j = 0; j < k.length; ++j) { + invokeAndCheck(m, result[j], k[j]); + } + break; + case CONCAT_M: + strs = antn.inStrings(); + ch = antn.inChars(); + k = antn.inInts(); + for (int j = 0; j < strs.length; ++j) { + invokeAndCheck(m, result[j], strs[j], ch[j], k[j]); + } + break; + default: + throw new RuntimeException("Unexpected operation."); + } + } + } + + /** + * Invokes method 'm' by passing arguments 'args' and checks if the + * returned value equals 'expectedResult'. + */ + private void invokeAndCheck(Method m, Object expectedResult, Object... args) throws Exception { + Object result = m.invoke(null, args); + if (!result.equals(expectedResult)) { +// System.out.println("Expected:"); +// System.out.println(expectedResult); +// System.out.println("Returned:"); +// System.out.println(result); + throw new RuntimeException("Result of '" + m.getName() + "' not equal to expected value."); + } + } + + /* + * Constants + */ + static final char charU = '\uff21'; + static final char charL = 'A'; + static final String emptyString = ""; + static final String stringL = "abcdefghijklmnop"; + static final String stringSmallL = "abc"; + static final String stringU = "\u0f21\u0f22\u0f23\u0f24\u0f25\u0f26\u0f27\u0f28"; + static final String stringSmallU = "\u0f21\u0f22\u0f23"; + static final int constInt = 123; + static final int constIntNeg = -123; + + /* + * Arrays.equals + */ + @Test(op = Operation.ARR_EQUALS_B) + public static boolean arrayEqualsB(byte[] a, byte[] b) { + return Arrays.equals(a, b); + } + + @Test(op = Operation.ARR_EQUALS_C) + public static boolean arrayEqualsC(char[] a, char[] b) { + return Arrays.equals(a, b); + } + + /* + * String.equals + */ + @Test(op = Operation.EQUALS) + public static boolean equals(String a, String b) { + return a.equals(b); + } + + /* + * String.compareTo + */ + @Test(op = Operation.COMPARE_TO) + public static int compareTo(String a, String b) { + return a.compareTo(b); + } + + /* + * String.indexOf + */ + @Test(op = Operation.INDEX_OF) + public static int indexOf(String a, String b, int from) { + return a.indexOf(b, from); + } + + @Test(op = Operation.INDEX_OF_CON_U, constString = stringSmallU) + public static String indexOfConstU(String a) { + int result = a.indexOf(stringSmallU); + return a.substring(result, result + stringSmallU.length()); + } + + @Test(op = Operation.INDEX_OF_CON_U, constString = stringU) + public static String indexOfConstLargeU(String a) { + int result = a.indexOf(stringU); + return a.substring(result, result + stringU.length()); + } + + @Test(op = Operation.INDEX_OF_CON_U, constString = emptyString) + public static String indexOfConstEmptyU(String a) { + int result = a.indexOf(emptyString); + return a.substring(result, result + emptyString.length()); + } + + @Test(op = Operation.INDEX_OF_CON_L, constString = stringSmallL) + public static String indexOfConstL(String a) { + int result = a.indexOf(stringSmallL); + return a.substring(result, result + stringSmallL.length()); + } + + @Test(op = Operation.INDEX_OF_CON_L, constString = stringL) + public static String indexOfConstLargeL(String a) { + int result = a.indexOf(stringL); + return a.substring(result, result + stringL.length()); + } + + @Test(op = Operation.INDEX_OF_CON_L, constString = emptyString) + public static String indexOfConstEmptyL(String a) { + int result = a.indexOf(emptyString); + return a.substring(result, result + emptyString.length()); + } + + @Test(op = Operation.INDEX_OF_CON_UL, constString = stringSmallL) + public static String indexOfConstUL(String a) { + int result = a.indexOf(stringSmallL); + return a.substring(result, result + stringSmallL.length()); + } + + @Test(op = Operation.INDEX_OF_CON_UL, constString = stringL) + public static String indexOfConstLargeUL(String a) { + int result = a.indexOf(stringL); + return a.substring(result, result + stringL.length()); + } + + @Test(op = Operation.INDEX_OF_CHAR) + public static int indexOfChar(String a, int ch, int from) { + return a.indexOf(ch, from); + } + + /* + * String concatenation optimization + */ + @Test(op = Operation.CONCAT, inStrings = {"ABC", "\uff21\uff22\uff23"}, outStrings = {"ABC", "\uff21\uff22\uff23"}) + public static String concatString(String a) { + return new StringBuilder().append(a).toString(); + } + + @Test(op = Operation.CONCAT, inStrings = {""}, outStrings = {""}) + public static String concatStringEmpty(String a) { + return new StringBuilder().toString(); + } + + @Test(op = Operation.CONCAT, inStrings = {""}, outStrings = {"null"}) + public static String concatStringNull(String a) { + return new StringBuilder().append((String)null).toString(); + } + + @Test(op = Operation.CONCAT, inStrings = {"ABC", "\uff21\uff22\uff23"}, outStrings = {"abcdefghijklmnopABCabc", "abcdefghijklmnop\uff21\uff22\uff23abc"}) + public static String concatStringConstL(String a) { + return new StringBuilder().append(stringL).append(a).append(stringSmallL).toString(); + } + + @Test(op = Operation.CONCAT, inStrings = {"ABC", "\uff21\uff22\uff23"}, outStrings = {"\u0f21\u0f22\u0f23ABC\u0f21\u0f22\u0f23\u0f24\u0f25\u0f26\u0f27\u0f28", "\u0f21\u0f22\u0f23\uff21\uff22\uff23\u0f21\u0f22\u0f23\u0f24\u0f25\u0f26\u0f27\u0f28"}) + public static String concatStringConstU(String a) { + return new StringBuilder().append(stringSmallU).append(a).append(stringU).toString(); + } + + @Test(op = Operation.CONCAT_C, inChars = {'A', '\uff21'}, outStrings = {"A", "\uff21"}) + public static String concatChar(char a) { + return new StringBuilder().append(a).toString(); + } + + @Test(op = Operation.CONCAT_C, inChars = {'A', '\uff21'}, outStrings = {"abcdefghijklmnopAabcA\uff21", "abcdefghijklmnop\uff21abcA\uff21"}) + public static String concatCharConstL(char a) { + return new StringBuilder().append(stringL).append(a).append(stringSmallL).append(charL).append(charU).toString(); + } + + @Test(op = Operation.CONCAT_C, inChars = {'A', '\uff21'}, outStrings = {"\u0f21\u0f22\u0f23A\u0f21\u0f22\u0f23\u0f24\u0f25\u0f26\u0f27\u0f28\uff21A", "\u0f21\u0f22\u0f23\uff21\u0f21\u0f22\u0f23\u0f24\u0f25\u0f26\u0f27\u0f28\uff21A"}) + public static String concatCharConstU(char a) { + return new StringBuilder().append(stringSmallU).append(a).append(stringU).append(charU).append(charL).toString(); + } + + @Test(op = Operation.CONCAT_I, inInts = {Integer.MIN_VALUE, -42, 42, Integer.MAX_VALUE}, outStrings = {"-2147483648", "-42", "42", "2147483647"}) + public static String concatInt(int a) { + return new StringBuilder().append(a).toString(); + } + + @Test(op = Operation.CONCAT_I, inInts = {Integer.MIN_VALUE, -42, 42, Integer.MAX_VALUE}, outStrings = {"abcdefghijklmnop-2147483648abc123-123", "abcdefghijklmnop-42abc123-123", "abcdefghijklmnop42abc123-123", "abcdefghijklmnop2147483647abc123-123"}) + public static String concatIntConstL(int b) { + return new StringBuilder().append(stringL).append(b).append(stringSmallL).append(constInt).append(constIntNeg).toString(); + } + + @Test(op = Operation.CONCAT_I, inInts = {Integer.MIN_VALUE, -42, 42, Integer.MAX_VALUE}, outStrings = {"\u0f21\u0f22\u0f23-2147483648\u0f21\u0f22\u0f23\u0f24\u0f25\u0f26\u0f27\u0f28123-123", "\u0f21\u0f22\u0f23-42\u0f21\u0f22\u0f23\u0f24\u0f25\u0f26\u0f27\u0f28123-123", "\u0f21\u0f22\u0f2342\u0f21\u0f22\u0f23\u0f24\u0f25\u0f26\u0f27\u0f28123-123", "\u0f21\u0f22\u0f232147483647\u0f21\u0f22\u0f23\u0f24\u0f25\u0f26\u0f27\u0f28123-123"}) + public static String concatIntConstU(int b) { + return new StringBuilder().append(stringSmallU).append(b).append(stringU).append(constInt).append(constIntNeg).toString(); + } + + @Test(op = Operation.CONCAT, inStrings = {""}, outStrings = {"nullabcabcdefghijklmnopA123-123"}) + public static String concatConstL(String a) { + return new StringBuilder().append((String)null).append(stringSmallL).append(stringL).append(charL).append(constInt).append(constIntNeg).toString(); + } + + @Test(op = Operation.CONCAT, inStrings = {""}, outStrings = {"nullabcabcdefghijklmnop\u0f21\u0f22\u0f23\u0f21\u0f22\u0f23\u0f24\u0f25\u0f26\u0f27\u0f28A\uff21123-123"}) + public static String concatConstU(String a) { + return new StringBuilder().append((String)null).append(stringSmallL).append(stringL).append(stringSmallU).append(stringU).append(charL).append(charU).append(constInt).append(constIntNeg).toString(); + } + + @Test(op = Operation.CONCAT_M, + inStrings = {"ABCDEFG", "ABCDEFG", "\u0f21\u0f22\u0f23\u0f24\u0f25\u0f26\u0f27\u0f28", "\u0f21\u0f22\u0f23\u0f24\u0f25\u0f26\u0f27\u0f28"}, + inChars = {'A', '\uff21', 'A', '\uff21'}, + inInts = {Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE}, + outStrings = {"ABCDEFGA-2147483648nullabcdefghijklmnop123-123A\u0f21\u0f22\u0f23\u0f24\u0f25\u0f26\u0f27\u0f28\uff21ABCDEFGA-2147483648null", + "ABCDEFG\uff212147483647nullabcdefghijklmnop123-123A\u0f21\u0f22\u0f23\u0f24\u0f25\u0f26\u0f27\u0f28\uff21ABCDEFG\uff212147483647null", + "\u0f21\u0f22\u0f23\u0f24\u0f25\u0f26\u0f27\u0f28A-2147483648nullabcdefghijklmnop123-123A\u0f21\u0f22\u0f23\u0f24\u0f25\u0f26\u0f27\u0f28\uff21\u0f21\u0f22\u0f23\u0f24\u0f25\u0f26\u0f27\u0f28A-2147483648null", + "\u0f21\u0f22\u0f23\u0f24\u0f25\u0f26\u0f27\u0f28\uff212147483647nullabcdefghijklmnop123-123A\u0f21\u0f22\u0f23\u0f24\u0f25\u0f26\u0f27\u0f28\uff21\u0f21\u0f22\u0f23\u0f24\u0f25\u0f26\u0f27\u0f28\uff212147483647null"}) + public static String concatMixed(String a, char b, int c) { + return new StringBuilder().append(a).append(b).append(c).append((String)null) + .append(stringL).append(constInt).append(constIntNeg).append(charL).append(stringU).append(charU) + .append(a).append(b).append(c).append((String)null).toString(); + } +} --- /dev/null 2015-10-30 10:00:53.843909024 +0100 +++ new/test/runtime/Annotations/TestAnnotatedStringEncoding.java 2015-10-30 19:14:59.076475881 +0100 @@ -0,0 +1,150 @@ +/* + * 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. + */ + +import java.lang.annotation.*; +import java.lang.reflect.*; + +/* + * @test + * @bug 8054307 + * @summary Tests the correct encoding of latin1/UTF16 Strings used in annotations. + */ +public class TestAnnotatedStringEncoding { + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.METHOD) + @interface Test { + String str(); + int index(); + } + + public static void main(String[] args) throws Exception { + new TestAnnotatedStringEncoding().run(); + } + + public void run() { + // Iterate over annotated methods and retrieve the string + for (Method m : this.getClass().getMethods()) { + if (m.isAnnotationPresent(Test.class)) { + // Check if string equals expected value + Test test = m.getAnnotation(Test.class); + String str = test.str(); + int index = test.index(); + if (!str.equals(strValue[index])) { + throw new RuntimeException(m.getName() + " failed: \"" + str + "\" (0x" + Integer.toHexString(str.charAt(0)) + + ") does not equal \"" + strValue[index] + "\" (0x" + Integer.toHexString(strValue[index].charAt(0)) + ") ."); + } + } + } + System.out.println("Test passed."); + } + + public static String[] strValue = { + "\u0000", "\u0020", "\u0021", "\u0080", + "\u00FF", "\u0100", "\u017F", "\u01FF", + "\u07FF", "\u0800", "\uC280", "\uC2BF", + "\uC380", "\uC3BF", "\uC5BF", "\uFFFF", + "\u10000", "\u1FFFFF", "\u200000", + "\u3FFFFFF", "\u4000000", "\u7FFFFFFF", + "ab\uff23\uff24ef\uff27", "\uff21\uff22cd\uff25g", "\u00FF\u00FF\u00FF", "\u00A1\u00A1\u00A1\u00A1", ""}; + + @Test(str = "\u0000", index = 0) + public static void check0() { } + + @Test(str = "\u0020", index = 1) + public static void check1() { } + + @Test(str = "\u0021", index = 2) + public static void check2() { } + + @Test(str = "\u0080", index = 3) + public static void check3() { } + + @Test(str = "\u00FF", index = 4) + public static void check4() { } + + @Test(str = "\u0100", index = 5) + public static void check5() { } + + @Test(str = "\u017F", index = 6) + public static void check6() { } + + @Test(str = "\u01FF", index = 7) + public static void check7() { } + + @Test(str = "\u07FF", index = 8) + public static void check8() { } + + @Test(str = "\u0800", index = 9) + public static void check9() { } + + @Test(str = "\uC280", index = 10) + public static void check10() { } + + @Test(str = "\uC2BF", index = 11) + public static void check11() { } + + @Test(str = "\uC380", index = 12) + public static void check12() { } + + @Test(str = "\uC3BF", index = 13) + public static void check13() { } + + @Test(str = "\uC5BF", index = 14) + public static void check14() { } + + @Test(str = "\uFFFF", index = 15) + public static void check15() { } + + @Test(str = "\u10000", index = 16) + public static void check16() { } + + @Test(str = "\u1FFFFF", index = 17) + public static void check17() { } + + @Test(str = "\u200000", index = 18) + public static void check18() { } + + @Test(str = "\u3FFFFFF", index = 19) + public static void check19() { } + + @Test(str = "\u4000000", index = 20) + public static void check20() { } + + @Test(str = "\u7FFFFFFF", index = 21) + public static void check21() { } + + @Test(str = "ab\uff23\uff24ef\uff27", index = 22) + public static void check22() { } + + @Test(str = "\uff21\uff22cd\uff25g", index = 23) + public static void check23() { } + + @Test(str = "\u00FF\u00FF\u00FF", index = 24) + public static void check24() { } + + @Test(str = "\u00A1\u00A1\u00A1\u00A1", index = 25) + public static void check25() { } + + @Test(str = "", index = 26) + public static void check26() { } +} --- /dev/null 2015-10-30 10:00:53.843909024 +0100 +++ new/test/runtime/SharedArchiveFile/CdsDifferentCompactStrings.java 2015-10-30 19:14:59.300475888 +0100 @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2013, 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 CdsDifferentCompactStrings + * @summary CDS (class data sharing) requires the same -XX:[+-]CompactStrings + * setting between archive creation time and load time. + * @library /testlibrary + * @modules java.base/sun.misc + * java.management + */ + +import jdk.test.lib.*; + +public class CdsDifferentCompactStrings { + public static void main(String[] args) throws Exception { + createAndLoadSharedArchive("+", "-"); + createAndLoadSharedArchive("-", "+"); + } + + private static void createAndLoadSharedArchive(String create, String load) + throws Exception + { + String createCompactStringsArgument = "-XX:" + create + "CompactStrings"; + String loadCompactStringsArgument = "-XX:" + load + "CompactStrings"; + + String filename = "./CdsDifferentCompactStrings" + create + ".jsa"; + + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder( + "-XX:+UnlockDiagnosticVMOptions", + "-XX:SharedArchiveFile=" + filename, + "-Xshare:dump", + createCompactStringsArgument); + + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + output.shouldContain("Loading classes to share"); + output.shouldHaveExitValue(0); + + pb = ProcessTools.createJavaProcessBuilder( + "-XX:+UnlockDiagnosticVMOptions", + "-XX:SharedArchiveFile=" + filename, + "-Xshare:on", + loadCompactStringsArgument, + "-version"); + + output = new OutputAnalyzer(pb.start()); + try { + output.shouldContain("The shared archive file's CompactStrings " + + "setting .* does not equal the current CompactStrings setting"); + } catch (RuntimeException e) { + output.shouldContain("Unable to use shared archive"); + } + output.shouldHaveExitValue(1); + } +}