8141132: JEP 254: Compact Strings
Summary: Adopt a more space-efficient internal representation for strings.
Reviewed-by: alanb, bdelsart, coleenp, iklam, jiangli, jrose, kevinw, naoto, pliden, roland, smarks, twisti
Contributed-by: Brent Christian <brent.christian@oracle.com>, Vivek Deshpande <vivek.r.deshpande@intel.com>, Tobias Hartmann <tobias.hartmann@oracle.com>, Charlie Hunt <charlie.hunt@oracle.com>, Vladimir Kozlov <vladimir.kozlov@oracle.com>, Roger Riggs <roger.riggs@oracle.com>, Xueming Shen <xueming.shen@oracle.com>, Aleksey Shipilev <aleksey.shipilev@oracle.com>, Sandhya Viswanathan <sandhya.viswanathan@intel.com>
--- a/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/OopUtilities.java Mon Nov 02 12:34:27 2015 +0000
+++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/OopUtilities.java Tue Nov 03 09:41:03 2015 +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");
- }
+ 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");
}
- if (offsetField != null && countField != null) {
- return charArrayToString((TypeArray) valueField.getValue(stringOop),
- offsetField.getValue(stringOop),
- countField.getValue(stringOop));
- }
- return charArrayToString((TypeArray) valueField.getValue(stringOop));
+ return byteArrayToString((TypeArray) valueField.getValue(stringOop), coderField.getValue(stringOop));
}
public static String stringOopToEscapedString(Oop stringOop) {
--- a/hotspot/agent/src/share/classes/sun/jvm/hotspot/tools/HeapSummary.java Mon Nov 02 12:34:27 2015 +0000
+++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/tools/HeapSummary.java Tue Nov 03 09:41:03 2015 +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) {
--- a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/Hashtable.java Mon Nov 02 12:34:27 2015 +0000
+++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/Hashtable.java Tue Nov 03 09:41:03 2015 +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;
--- a/hotspot/src/cpu/aarch64/vm/aarch64.ad Mon Nov 02 12:34:27 2015 +0000
+++ b/hotspot/src/cpu/aarch64/vm/aarch64.ad Tue Nov 03 09:41:03 2015 +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);
--- a/hotspot/src/cpu/aarch64/vm/c1_LIRAssembler_aarch64.cpp Mon Nov 02 12:34:27 2015 +0000
+++ b/hotspot/src/cpu/aarch64/vm/c1_LIRAssembler_aarch64.cpp Tue Nov 03 09:41:03 2015 +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();
--- a/hotspot/src/cpu/aarch64/vm/globals_aarch64.hpp Mon Nov 02 12:34:27 2015 +0000
+++ b/hotspot/src/cpu/aarch64/vm/globals_aarch64.hpp Tue Nov 03 09:41:03 2015 +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);
--- a/hotspot/src/cpu/ppc/vm/globals_ppc.hpp Mon Nov 02 12:34:27 2015 +0000
+++ b/hotspot/src/cpu/ppc/vm/globals_ppc.hpp Tue Nov 03 09:41:03 2015 +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) \
\
--- a/hotspot/src/cpu/ppc/vm/ppc.ad Mon Nov 02 12:34:27 2015 +0000
+++ b/hotspot/src/cpu/ppc/vm/ppc.ad Tue Nov 03 09:41:03 2015 +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);
--- a/hotspot/src/cpu/sparc/vm/assembler_sparc.hpp Mon Nov 02 12:34:27 2015 +0000
+++ b/hotspot/src/cpu/sparc/vm/assembler_sparc.hpp Tue Nov 03 09:41:03 2015 +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)); }
--- a/hotspot/src/cpu/sparc/vm/c1_LIRAssembler_sparc.cpp Mon Nov 02 12:34:27 2015 +0000
+++ b/hotspot/src/cpu/sparc/vm/c1_LIRAssembler_sparc.cpp Tue Nov 03 09:41:03 2015 +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) {
--- a/hotspot/src/cpu/sparc/vm/globals_sparc.hpp Mon Nov 02 12:34:27 2015 +0000
+++ b/hotspot/src/cpu/sparc/vm/globals_sparc.hpp Tue Nov 03 09:41:03 2015 +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, \
--- a/hotspot/src/cpu/sparc/vm/macroAssembler_sparc.cpp Mon Nov 02 12:34:27 2015 +0000
+++ b/hotspot/src/cpu/sparc/vm/macroAssembler_sparc.cpp Tue Nov 03 09:41:03 2015 +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");
-
- // Note: limit contains number of bytes (2*char_elements) != 0.
- andcc(limit, 0x2, chr1); // trailing character ?
+#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);
+
+ 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
-
- // Caller should set it:
- // add(G0, 1, result); // equals
+ delayed()->lduw(ary1, limit, result); // hoisted
+
+ 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");
--- a/hotspot/src/cpu/sparc/vm/macroAssembler_sparc.hpp Mon Nov 02 12:34:27 2015 +0000
+++ b/hotspot/src/cpu/sparc/vm/macroAssembler_sparc.hpp Tue Nov 03 09:41:03 2015 +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);
--- a/hotspot/src/cpu/sparc/vm/sparc.ad Mon Nov 02 12:34:27 2015 +0000
+++ b/hotspot/src/cpu/sparc/vm/sparc.ad Tue Nov 03 09:41:03 2015 +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 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 $str1,$cnt1,$str2,$cnt2 -> $result // KILL $tmp" %}
- ins_encode( enc_String_Compare(str1, str2, cnt1, cnt2, result) );
+ 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_equals(o0RegP str1, o1RegP str2, g3RegI cnt, notemp_iRegI result,
- o7RegI tmp, flagsReg ccr) %{
+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_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( enc_Array_Equals(ary1, ary2, tmp1, result));
+ 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 %{
+ __ 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);
%}
--- a/hotspot/src/cpu/x86/vm/assembler_x86.cpp Mon Nov 02 12:34:27 2015 +0000
+++ b/hotspot/src/cpu/x86/vm/assembler_x86.cpp Tue Nov 03 09:41:03 2015 +0100
@@ -3036,6 +3036,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,
@@ -3108,6 +3137,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());
@@ -5370,6 +5410,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;
--- a/hotspot/src/cpu/x86/vm/assembler_x86.hpp Mon Nov 02 12:34:27 2015 +0000
+++ b/hotspot/src/cpu/x86/vm/assembler_x86.hpp Tue Nov 03 09:41:03 2015 +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
@@ -1682,6 +1682,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);
@@ -1698,6 +1704,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
@@ -2116,6 +2124,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);
--- a/hotspot/src/cpu/x86/vm/c1_LIRAssembler_x86.cpp Mon Nov 02 12:34:27 2015 +0000
+++ b/hotspot/src/cpu/x86/vm/c1_LIRAssembler_x86.cpp Tue Nov 03 09:41:03 2015 +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()) {
--- a/hotspot/src/cpu/x86/vm/globals_x86.hpp Mon Nov 02 12:34:27 2015 +0000
+++ b/hotspot/src/cpu/x86/vm/globals_x86.hpp Tue Nov 03 09:41:03 2015 +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) \
--- a/hotspot/src/cpu/x86/vm/macroAssembler_x86.cpp Mon Nov 02 12:34:27 2015 +0000
+++ b/hotspot/src/cpu/x86/vm/macroAssembler_x86.cpp Tue Nov 03 09:41:03 2015 +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");
-
- // This method uses pcmpestri instruction with bound registers
+ assert(ae != StrIntrinsicNode::LU, "Invalid encoding");
+
+ // 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<<scale1));
} // (int_cnt2 > 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<<scale1;
+ int tail_off2 = int_cnt2<<scale2;
+ if (ae == StrIntrinsicNode::UL) {
+ pmovzxbw(vec, Address(str2, cnt2, scale2, tail_off2));
+ } else {
+ movdqu(vec, Address(str2, cnt2, scale2, tail_off2));
+ }
+ pcmpestri(vec, Address(result, cnt2, scale1, tail_off1), mode);
} else {
// calculate index in register to avoid integer overflow (int_cnt2*2)
movl(tmp, int_cnt2);
addptr(tmp, cnt2);
- movdqu(vec, Address(str2, tmp, Address::times_2, 0));
- pcmpestri(vec, Address(result, tmp, Address::times_2, 0), 0x0d);
+ if (ae == StrIntrinsicNode::UL) {
+ pmovzxbw(vec, Address(str2, tmp, scale2, 0));
+ } else {
+ movdqu(vec, Address(str2, tmp, scale2, 0));
+ }
+ pcmpestri(vec, Address(result, tmp, scale1, 0), mode);
}
// Need to reload strings pointers if not matched whole vector
jcc(Assembler::noOverflow, RELOAD_SUBSTR); // OF == 0
- addptr(cnt2, 8);
+ addptr(cnt2, stride);
jcc(Assembler::negative, SCAN_SUBSTR);
// Fall through if found full substring
@@ -6446,7 +6476,9 @@
// Found result if we matched full small substring.
// Compute substr offset
subptr(result, str1);
- shrl(result, 1); // index
+ if (ae == StrIntrinsicNode::UU || ae == StrIntrinsicNode::UL) {
+ shrl(result, 1); // index
+ }
bind(EXIT);
} // string_indexofC8
@@ -6455,9 +6487,12 @@
void MacroAssembler::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) {
ShortBranchVerifier sbv(this);
assert(UseSSE42Intrinsics, "SSE4.2 is required");
+ assert(ae != StrIntrinsicNode::LU, "Invalid encoding");
+
//
// int_cnt2 is length of small (< 8 chars) constant substring
// or (-1) for non constant substring in which case its length
@@ -6467,18 +6502,22 @@
// if (substr.count > 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<<scale2);
+ movdqu(vec, Address(str2, tail_off-16));
+ psrldq(vec, 16-tail_off);
+ }
}
} else { // not constant substring
- cmpl(cnt2, 8);
+ cmpl(cnt2, stride);
jccb(Assembler::aboveEqual, BIG_STRINGS); // Both strings are big enough
// We can read beyond string if srt+16 does not cross page boundary
@@ -6521,12 +6577,17 @@
// Move small strings to stack to allow load 16 bytes into vec.
subptr(rsp, 16);
- int stk_offset = wordSize-2;
+ int stk_offset = wordSize-(1<<scale2);
push(cnt2);
bind(COPY_SUBSTR);
- load_unsigned_short(result, Address(str2, cnt2, Address::times_2, -2));
- movw(Address(rsp, cnt2, Address::times_2, stk_offset), result);
+ if (ae == StrIntrinsicNode::LL || ae == StrIntrinsicNode::UL) {
+ load_unsigned_byte(result, Address(str2, cnt2, scale2, -1));
+ movb(Address(rsp, cnt2, scale2, stk_offset), result);
+ } else if (ae == StrIntrinsicNode::UU) {
+ load_unsigned_short(result, Address(str2, cnt2, scale2, -2));
+ movw(Address(rsp, cnt2, scale2, stk_offset), result);
+ }
decrement(cnt2);
jccb(Assembler::notZero, COPY_SUBSTR);
@@ -6535,7 +6596,7 @@
} // non constant
bind(CHECK_STR);
- cmpl(cnt1, 8);
+ cmpl(cnt1, stride);
jccb(Assembler::aboveEqual, BIG_STRINGS);
// Check cross page boundary.
@@ -6545,7 +6606,7 @@
jccb(Assembler::belowEqual, BIG_STRINGS);
subptr(rsp, 16);
- int stk_offset = -2;
+ int stk_offset = -(1<<scale1);
if (int_cnt2 < 0) { // not constant
push(cnt2);
stk_offset += wordSize;
@@ -6553,8 +6614,13 @@
movl(cnt2, cnt1);
bind(COPY_STR);
- load_unsigned_short(result, Address(str1, cnt2, Address::times_2, -2));
- movw(Address(rsp, cnt2, Address::times_2, stk_offset), result);
+ if (ae == StrIntrinsicNode::LL) {
+ load_unsigned_byte(result, Address(str1, cnt2, scale1, -1));
+ movb(Address(rsp, cnt2, scale1, stk_offset), result);
+ } else {
+ load_unsigned_short(result, Address(str1, cnt2, scale1, -2));
+ movw(Address(rsp, cnt2, scale1, stk_offset), result);
+ }
decrement(cnt2);
jccb(Assembler::notZero, COPY_STR);
@@ -6566,7 +6632,11 @@
bind(BIG_STRINGS);
// Load substring.
if (int_cnt2 < 0) { // -1
- movdqu(vec, Address(str2, 0));
+ if (ae == StrIntrinsicNode::UL) {
+ pmovzxbw(vec, Address(str2, 0));
+ } else {
+ movdqu(vec, Address(str2, 0));
+ }
push(cnt2); // substr count
push(str2); // substr addr
push(str1); // string addr
@@ -6597,37 +6667,43 @@
bind(RELOAD_SUBSTR);
movptr(str2, Address(rsp, 2*wordSize));
movl(cnt2, Address(rsp, 3*wordSize));
- movdqu(vec, Address(str2, 0));
+ if (ae == StrIntrinsicNode::UL) {
+ pmovzxbw(vec, Address(str2, 0));
+ } else {
+ movdqu(vec, Address(str2, 0));
+ }
// We came here after the beginning of the substring was
// matched but the rest of it was not so we need to search
// again. Start from the next element after the previous match.
subptr(str1, result); // Restore counter
- shrl(str1, 1);
+ if (ae == StrIntrinsicNode::UU || ae == StrIntrinsicNode::UL) {
+ shrl(str1, 1);
+ }
addl(cnt1, str1);
decrementl(cnt1); // Shift to next element
cmpl(cnt1, cnt2);
jccb(Assembler::negative, RET_NOT_FOUND); // Left less then substring
- addptr(result, 2);
+ addptr(result, (1<<scale1));
} // non constant
// Scan string for start of substr in 16-byte vectors
bind(SCAN_TO_SUBSTR);
assert(cnt1 == rdx && cnt2 == rax && tmp == rcx, "pcmpestri");
- 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
addptr(result, 16);
bind(ADJUST_STR);
- cmpl(cnt1, 8); // Do not read beyond string
+ cmpl(cnt1, stride); // Do not read beyond string
jccb(Assembler::greaterEqual, SCAN_TO_SUBSTR);
// Back-up string to avoid reading beyond string.
- lea(result, Address(result, cnt1, Address::times_2, -16));
- movl(cnt1, 8);
+ lea(result, Address(result, cnt1, scale1, -16));
+ movl(cnt1, stride);
jmpb(SCAN_TO_SUBSTR);
// Found a potential substr
@@ -6646,13 +6722,12 @@
bind(FOUND_SUBSTR);
// Compute start addr of substr
- lea(result, Address(result, tmp, Address::times_2));
-
+ lea(result, Address(result, tmp, scale1));
if (int_cnt2 > 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);
+
+ 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);
- // Check if the strings start at the same location.
- cmpptr(str1, str2);
- 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);
-}
-
-// 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) {
+ if(ae == StrIntrinsicNode::UL) {
+ negl(result);
+ }
+}
+
+// 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;
+
+ // len == 0
+ testl(len, len);
+ jcc(Assembler::zero, FALSE_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(T_CHAR);
-
- // Check the input args
- cmpptr(ary1, ary2);
- jcc(Assembler::equal, TRUE_LABEL);
+ 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
--- a/hotspot/src/cpu/x86/vm/macroAssembler_x86.hpp Mon Nov 02 12:34:27 2015 +0000
+++ b/hotspot/src/cpu/x86/vm/macroAssembler_x86.hpp Tue Nov 03 09:41:03 2015 +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
@@ -1212,32 +1211,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,
@@ -1332,6 +1349,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
};
--- a/hotspot/src/cpu/x86/vm/x86_32.ad Mon Nov 02 12:34:27 2015 +0000
+++ b/hotspot/src/cpu/x86/vm/x86_32.ad Tue Nov 03 09:41:03 2015 +0100
@@ -11435,16 +11435,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, 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);
+ $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 );
%}
@@ -11457,21 +11503,78 @@
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) {
+ // 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::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, StrIntrinsicNode::UU);
+ }
+ %}
+ ins_pipe( pipe_slow );
+%}
+
+// 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) {
@@ -11480,47 +11583,154 @@
__ string_indexofC8($str1$$Register, $str2$$Register,
$cnt1$$Register, $cnt2$$Register,
icnt2, $result$$Register,
- $vec$$XMMRegister, $tmp$$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);
+ $vec$$XMMRegister, $tmp$$Register, StrIntrinsicNode::UL);
}
%}
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);
+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 $str1,$cnt1,$str2,$cnt2 -> $result // KILL all" %}
+ 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);
+ $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 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(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" %}
- ins_encode %{
- __ char_arrays_equals(true, $ary1$$Register, $ary2$$Register,
- $tmp3$$Register, $result$$Register, $tmp4$$Register,
- $tmp1$$XMMRegister, $tmp2$$XMMRegister);
+ 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 %{
+ __ byte_array_inflate($src$$Register, $dst$$Register, $len$$Register,
+ $tmp1$$XMMRegister, $tmp2$$Register);
%}
ins_pipe( pipe_slow );
%}
--- a/hotspot/src/cpu/x86/vm/x86_64.ad Mon Nov 02 12:34:27 2015 +0000
+++ b/hotspot/src/cpu/x86/vm/x86_64.ad Tue Nov 03 09:41:03 2015 +0100
@@ -10447,30 +10447,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, 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);
+ $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)
-%{
- predicate(UseSSE42Intrinsics);
+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 && (((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 $str1,$cnt1,$str2,$int_cnt2 -> $result // KILL $vec, $cnt1, $cnt2, $tmp" %}
+ 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 char[] $str1,$cnt1,$str2,$int_cnt2 -> $result // KILL $vec, $cnt1, $cnt2, $tmp" %}
ins_encode %{
int icnt2 = (int)$int_cnt2$$constant;
if (icnt2 >= 8) {
@@ -10479,31 +10557,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, StrIntrinsicNode::UU);
+ }
+ %}
+ ins_pipe( pipe_slow );
+%}
+
+// 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 && (((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);
+ $vec$$XMMRegister, $tmp$$Register, StrIntrinsicNode::UL);
}
%}
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)
-%{
- predicate(UseSSE42Intrinsics);
+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 $str1,$cnt1,$str2,$cnt2 -> $result // KILL all" %}
+ 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);
+ $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 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(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 );
%}
@@ -10517,26 +10672,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" %}
- ins_encode %{
- __ char_arrays_equals(true, $ary1$$Register, $ary2$$Register,
- $tmp3$$Register, $result$$Register, $tmp4$$Register,
- $tmp1$$XMMRegister, $tmp2$$XMMRegister);
+
+ 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 %{
+ __ byte_array_inflate($src$$Register, $dst$$Register, $len$$Register,
+ $tmp1$$XMMRegister, $tmp2$$Register);
%}
ins_pipe( pipe_slow );
%}
--- a/hotspot/src/cpu/zero/vm/globals_zero.hpp Mon Nov 02 12:34:27 2015 +0000
+++ b/hotspot/src/cpu/zero/vm/globals_zero.hpp Tue Nov 03 09:41:03 2015 +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, \
--- a/hotspot/src/share/vm/adlc/formssel.cpp Mon Nov 02 12:34:27 2015 +0000
+++ b/hotspot/src/share/vm/adlc/formssel.cpp Tue Nov 03 09:41:03 2015 +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;
}
--- a/hotspot/src/share/vm/adlc/main.cpp Mon Nov 02 12:34:27 2015 +0000
+++ b/hotspot/src/share/vm/adlc/main.cpp Tue Nov 03 09:41:03 2015 +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");
--- a/hotspot/src/share/vm/c1/c1_Compiler.cpp Mon Nov 02 12:34:27 2015 +0000
+++ b/hotspot/src/share/vm/c1/c1_Compiler.cpp Tue Nov 03 09:41:03 2015 +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:
--- a/hotspot/src/share/vm/c1/c1_GraphBuilder.cpp Mon Nov 02 12:34:27 2015 +0000
+++ b/hotspot/src/share/vm/c1/c1_GraphBuilder.cpp Tue Nov 03 09:41:03 2015 +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();
--- a/hotspot/src/share/vm/c1/c1_GraphBuilder.hpp Mon Nov 02 12:34:27 2015 +0000
+++ b/hotspot/src/share/vm/c1/c1_GraphBuilder.hpp Tue Nov 03 09:41:03 2015 +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);
--- a/hotspot/src/share/vm/c1/c1_LIRAssembler.hpp Mon Nov 02 12:34:27 2015 +0000
+++ b/hotspot/src/share/vm/c1/c1_LIRAssembler.hpp Tue Nov 03 09:41:03 2015 +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
--- a/hotspot/src/share/vm/ci/ciTypeArray.cpp Mon Nov 02 12:34:27 2015 +0000
+++ b/hotspot/src/share/vm/ci/ciTypeArray.cpp Tue Nov 03 09:41:03 2015 +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);
+}
--- a/hotspot/src/share/vm/ci/ciTypeArray.hpp Mon Nov 02 12:34:27 2015 +0000
+++ b/hotspot/src/share/vm/ci/ciTypeArray.hpp Tue Nov 03 09:41:03 2015 +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
--- a/hotspot/src/share/vm/classfile/javaClasses.cpp Mon Nov 02 12:34:27 2015 +0000
+++ b/hotspot/src/share/vm/classfile/javaClasses.cpp Tue Nov 03 09:41:03 2015 +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);
-
- jchar* base = (length == 0) ? NULL : value->char_at_addr(offset);
- if (base == NULL) 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);
+ bool is_latin1 = java_lang_String::is_latin1(java_string);
+
+ if (length == 0) return NULL;
+
+ 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);
-
- if (length1 != length2) {
+ bool is_latin2 = java_lang_String::is_latin1(str2);
+
+ 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
--- a/hotspot/src/share/vm/classfile/javaClasses.hpp Mon Nov 02 12:34:27 2015 +0000
+++ b/hotspot/src/share/vm/classfile/javaClasses.hpp Tue Nov 03 09:41:03 2015 +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() {
- assert(initialized, "Must be initialized");
- return (offset_offset > 0);
- }
-
- static bool has_count_field() {
- assert(initialized, "Must be initialized");
- return (count_offset > 0);
- }
-
static bool has_hash_field() {
assert(initialized, "Must be initialized");
return (hash_offset > 0);
}
+ static bool has_coder_field() {
+ assert(initialized, "Must be initialized");
+ return (coder_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 <typename T> 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);
--- a/hotspot/src/share/vm/classfile/stringTable.cpp Mon Nov 02 12:34:27 2015 +0000
+++ b/hotspot/src/share/vm/classfile/stringTable.cpp Tue Nov 03 09:41:03 2015 +0100
@@ -94,11 +94,16 @@
CompactHashtable<oop, char> StringTable::_shared_table;
// Pick hashing algorithm
-unsigned int StringTable::hash_string(const jchar* s, int len) {
+template<typename T>
+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<jchar>(const jchar* s, int len);
+template unsigned int StringTable::hash_string<jbyte>(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);
--- a/hotspot/src/share/vm/classfile/stringTable.hpp Mon Nov 02 12:34:27 2015 +0000
+++ b/hotspot/src/share/vm/classfile/stringTable.hpp Tue Nov 03 09:41:03 2015 +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<typename T> static unsigned int hash_string(const T* s, int len);
// Internal test.
static void test_alt_hash() PRODUCT_RETURN;
--- a/hotspot/src/share/vm/classfile/symbolTable.cpp Mon Nov 02 12:34:27 2015 +0000
+++ b/hotspot/src/share/vm/classfile/symbolTable.cpp Tue Nov 03 09:41:03 2015 +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);
}
--- a/hotspot/src/share/vm/classfile/symbolTable.hpp Mon Nov 02 12:34:27 2015 +0000
+++ b/hotspot/src/share/vm/classfile/symbolTable.hpp Tue Nov 03 09:41:03 2015 +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);
--- a/hotspot/src/share/vm/classfile/vmSymbols.cpp Mon Nov 02 12:34:27 2015 +0000
+++ b/hotspot/src/share/vm/classfile/vmSymbols.cpp Tue Nov 03 09:41:03 2015 +0100
@@ -425,10 +425,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:
@@ -437,6 +453,7 @@
case vmIntrinsics::_loadFence:
case vmIntrinsics::_storeFence:
case vmIntrinsics::_fullFence:
+ case vmIntrinsics::_hasNegatives:
case vmIntrinsics::_Reference_get:
break;
default:
@@ -619,19 +636,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:
@@ -666,6 +695,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;
--- a/hotspot/src/share/vm/classfile/vmSymbols.hpp Mon Nov 02 12:34:27 2015 +0000
+++ b/hotspot/src/share/vm/classfile/vmSymbols.hpp Tue Nov 03 09:41:03 2015 +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") \
@@ -381,9 +383,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") \
@@ -837,22 +839,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") \
--- a/hotspot/src/share/vm/gc/g1/g1StringDedupTable.cpp Mon Nov 02 12:34:27 2015 +0000
+++ b/hotspot/src/share/vm/gc/g1/g1StringDedupTable.cpp Tue Nov 03 09:41:03 2015 +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();
--- a/hotspot/src/share/vm/gc/g1/g1StringDedupTable.hpp Mon Nov 02 12:34:27 2015 +0000
+++ b/hotspot/src/share/vm/gc/g1/g1StringDedupTable.hpp Tue Nov 03 09:41:03 2015 +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,
--- a/hotspot/src/share/vm/memory/filemap.cpp Mon Nov 02 12:34:27 2015 +0000
+++ b/hotspot/src/share/vm/memory/filemap.cpp Tue Nov 03 09:41:03 2015 +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;
}
--- a/hotspot/src/share/vm/memory/filemap.hpp Mon Nov 02 12:34:27 2015 +0000
+++ b/hotspot/src/share/vm/memory/filemap.hpp Tue Nov 03 09:41:03 2015 +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
--- a/hotspot/src/share/vm/memory/metaspaceShared.cpp Mon Nov 02 12:34:27 2015 +0000
+++ b/hotspot/src/share/vm/memory/metaspaceShared.cpp Tue Nov 03 09:41:03 2015 +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].
--- a/hotspot/src/share/vm/oops/instanceKlass.cpp Mon Nov 02 12:34:27 2015 +0000
+++ b/hotspot/src/share/vm/oops/instanceKlass.cpp Tue Nov 03 09:41:03 2015 +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();
--- a/hotspot/src/share/vm/oops/typeArrayOop.hpp Mon Nov 02 12:34:27 2015 +0000
+++ b/hotspot/src/share/vm/oops/typeArrayOop.hpp Tue Nov 03 09:41:03 2015 +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];
}
--- a/hotspot/src/share/vm/opto/c2compiler.cpp Mon Nov 02 12:34:27 2015 +0000
+++ b/hotspot/src/share/vm/opto/c2compiler.cpp Tue Nov 03 09:41:03 2015 +0100
@@ -181,12 +181,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;
@@ -194,8 +207,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;
@@ -302,7 +319,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:
--- a/hotspot/src/share/vm/opto/classes.hpp Mon Nov 02 12:34:27 2015 +0000
+++ b/hotspot/src/share/vm/opto/classes.hpp Tue Nov 03 09:41:03 2015 +0100
@@ -136,6 +136,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)
--- a/hotspot/src/share/vm/opto/escape.cpp Mon Nov 02 12:34:27 2015 +0000
+++ b/hotspot/src/share/vm/opto/escape.cpp Tue Nov 03 09:41:03 2015 +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");
--- a/hotspot/src/share/vm/opto/gcm.cpp Mon Nov 02 12:34:27 2015 +0000
+++ b/hotspot/src/share/vm/opto/gcm.cpp Tue Nov 03 09:41:03 2015 +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,
--- a/hotspot/src/share/vm/opto/graphKit.cpp Mon Nov 02 12:34:27 2015 +0000
+++ b/hotspot/src/share/vm/opto/graphKit.cpp Tue Nov 03 09:41:03 2015 +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) {
--- a/hotspot/src/share/vm/opto/graphKit.hpp Mon Nov 02 12:34:27 2015 +0000
+++ b/hotspot/src/share/vm/opto/graphKit.hpp Tue Nov 03 09:41:03 2015 +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) {
--- a/hotspot/src/share/vm/opto/idealKit.cpp Mon Nov 02 12:34:27 2015 +0000
+++ b/hotspot/src/share/vm/opto/idealKit.cpp Tue Nov 03 09:41:03 2015 +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,
--- a/hotspot/src/share/vm/opto/idealKit.hpp Mon Nov 02 12:34:27 2015 +0000
+++ b/hotspot/src/share/vm/opto/idealKit.hpp Tue Nov 03 09:41:03 2015 +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
--- a/hotspot/src/share/vm/opto/intrinsicnode.cpp Mon Nov 02 12:34:27 2015 +0000
+++ b/hotspot/src/share/vm/opto/intrinsicnode.cpp Tue Nov 03 09:41:03 2015 +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();
}
--- a/hotspot/src/share/vm/opto/intrinsicnode.hpp Mon Nov 02 12:34:27 2015 +0000
+++ b/hotspot/src/share/vm/opto/intrinsicnode.hpp Tue Nov 03 09:41:03 2015 +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
--- a/hotspot/src/share/vm/opto/lcm.cpp Mon Nov 02 12:34:27 2015 +0000
+++ b/hotspot/src/share/vm/opto/lcm.cpp Tue Nov 03 09:41:03 2015 +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;
--- a/hotspot/src/share/vm/opto/library_call.cpp Mon Nov 02 12:34:27 2015 +0000
+++ b/hotspot/src/share/vm/opto/library_call.cpp Tue Nov 03 09:41:03 2015 +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();
@@ -458,6 +463,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;
@@ -512,9 +518,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);
@@ -617,7 +645,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();
@@ -687,6 +716,7 @@
return inline_ghash_processBlocks();
case vmIntrinsics::_encodeISOArray:
+ case vmIntrinsics::_encodeByteISOArray:
return inline_encodeISOArray();
case vmIntrinsics::_updateCRC32:
@@ -711,6 +741,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.
@@ -876,75 +909,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;
- 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);
- 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);
- break;
- case Op_StrEquals:
- result = new StrEqualsNode(control(), memory(TypeAryPtr::CHARS),
- str1_start, str2_start, str1_len);
- break;
- default:
- ShouldNotReachHere();
- return NULL;
- }
-
- // All these intrinsics have checks.
- C->set_has_split_ifs(true); // Has chance for split-if optimization
-
- 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) {
+// 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:
- result = new StrIndexOfNode(control(), memory(TypeAryPtr::CHARS),
- str1_start, cnt1, str2_start, cnt2);
+ result = new StrIndexOfNode(control(), memory(TypeAryPtr::BYTES),
+ str1_start, cnt1, str2_start, cnt2, ae);
break;
case Op_StrComp:
- result = new StrCompNode(control(), memory(TypeAryPtr::CHARS),
- str1_start, cnt1, str2_start, cnt2);
+ 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, cnt1);
+ result = new StrEqualsNode(control(), memory(TypeAryPtr::BYTES),
+ str1_start, str2_start, cnt1, ae);
break;
default:
ShouldNotReachHere();
@@ -958,98 +940,54 @@
}
//------------------------------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));
+bool LibraryCallKit::inline_string_compareTo(StrIntrinsicNode::ArgEnc ae) {
+ Node* arg1 = argument(0);
+ Node* arg2 = argument(1);
+
+ // 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);
+
+ 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 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));
+ // 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);
+
+ // 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());
}
@@ -1064,289 +1002,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_);
-
- // Final sync IdealKit and GraphKit.
- final_sync(kit);
- Node* result = __ value(rtn);
-#undef __
- C->set_has_loops(true);
- return result;
+//------------------------------inline_hasNegatives------------------------------
+bool LibraryCallKit::inline_hasNegatives() {
+ if (too_many_traps(Deoptimization::Reason_intrinsic)) return false;
+
+ assert(callee()->signature()->size() == 3, "hasNegatives has 3 parameters");
+ // no receiver since it is static method
+ Node* ba = argument(0);
+ Node* offset = argument(1);
+ Node* len = argument(2);
+
+ RegionNode* bailout = new RegionNode(1);
+ record_for_igvn(bailout);
+
+ // offset must not be negative.
+ generate_negative_guard(offset, bailout);
+
+ // offset + length must not exceed length of ba.
+ generate_limit_guard(offset, len, load_array_length(ba), bailout);
+
+ if (bailout->req() > 1) {
+ PreserveJVMState pjvms(this);
+ set_control(_gvn.transform(bailout));
+ uncommon_trap(Deoptimization::Reason_intrinsic,
+ Deoptimization::Action_maybe_recompile);
+ }
+ if (!stopped()) {
+ Node* ba_start = array_element_address(ba, offset, T_BYTE);
+ Node* result = new HasNegativesNode(control(), memory(TypeAryPtr::BYTES), ba_start, len);
+ set_result(_gvn.transform(result));
+ }
+ return true;
}
//------------------------------inline_string_indexOf------------------------
-bool LibraryCallKit::inline_string_indexOf() {
- Node* receiver = argument(0);
- Node* arg = 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);
- 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(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()) {
- 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());
+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);
+
+ // 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);
+ }
+
+ 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);
}
- 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;
- }
- ciInstanceKlass* klass = env()->String_klass();
- ciObject* str_const = str_type->const_oop();
- if (str_const == NULL || str_const->klass() != klass) {
- return false;
- }
- ciInstance* str = str_const->as_instance();
- assert(str != NULL, "must be instance");
-
- 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
-
- 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();
- } else {
- o = 0;
- c = pat->length();
+ }
+
+ if (!stopped()) {
+ Node* result = make_string_method_node(Op_StrIndexOf, src_start, src_count, tgt_start, tgt_count, ae);
+ result_phi->init_req(1, result);
+ result_rgn->init_req(1, control());
+ }
+ set_control(_gvn.transform(result_rgn));
+ record_for_igvn(result_rgn);
+ set_result(_gvn.transform(result_phi));
+
+ return true;
+}
+
+//-----------------------------inline_string_indexOf-----------------------
+bool LibraryCallKit::inline_string_indexOfI(StrIntrinsicNode::ArgEnc ae) {
+ if (!Matcher::has_match_rule(Op_StrIndexOf) || !UseSSE42Intrinsics) {
+ return false;
+ }
+ assert(callee()->signature()->size() == 5, "String.indexOf() has 5 arguments");
+ Node* src = argument(0); // byte[]
+ Node* src_count = argument(1);
+ Node* tgt = argument(2); // byte[]
+ Node* tgt_count = argument(3);
+ Node* from_index = argument(4);
+
+ // Java code which calls this method has range checks for from_index value.
+ src_count = _gvn.transform(new SubINode(src_count, from_index));
+
+ // Multiply byte array index by 2 if String is UTF16 encoded
+ Node* src_offset = (ae == StrIntrinsicNode::LL) ? from_index : _gvn.transform(new LShiftINode(from_index, intcon(1)));
+ Node* src_start = array_element_address(src, src_offset, T_BYTE);
+ Node* tgt_start = array_element_address(tgt, intcon(0), T_BYTE);
+
+ Node* result = make_string_method_node(Op_StrIndexOf, src_start, src_count, tgt_start, tgt_count, ae);
+
+ // The result is index relative to from_index if substring was found, -1 otherwise.
+ // Generate code which will fold into cmove.
+ RegionNode* region = new RegionNode(3);
+ Node* phi = new PhiNode(region, TypeInt::INT);
+
+ Node* cmp = _gvn.transform(new CmpINode(result, intcon(0)));
+ Node* bol = _gvn.transform(new BoolNode(cmp, BoolTest::lt));
+
+ Node* if_lt = generate_slow_guard(bol, NULL);
+ if (if_lt != NULL) {
+ // result == -1
+ phi->init_req(2, result);
+ region->init_req(2, if_lt);
+ }
+ if (!stopped()) {
+ result = _gvn.transform(new AddINode(result, from_index));
+ phi->init_req(1, result);
+ region->init_req(1, control());
+ }
+
+ set_control(_gvn.transform(region));
+ record_for_igvn(region);
+ set_result(_gvn.transform(phi));
+
+ return true;
+}
+
+//-----------------------------inline_string_indexOfChar-----------------------
+bool LibraryCallKit::inline_string_indexOfChar() {
+ if (!Matcher::has_match_rule(Op_StrIndexOfChar) || !(UseSSE > 4)) {
+ return false;
+ }
+ assert(callee()->signature()->size() == 4, "String.indexOfChar() has 4 arguments");
+ Node* src = argument(0); // byte[]
+ 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");
}
-
- // 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;
+ // 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);
}
-
- 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;
- }
-
- // The null string as a pattern always returns 0 (match at beginning of string)
- if (c == 0) {
- set_result(intcon(0));
- return true;
+ if (stopped()) return true;
+
+ // Range checks are done by caller.
+
+ Node* size = _gvn.transform(new LShiftINode(length, intcon(1)));
+ Node* klass_node = makecon(TypeKlassPtr::make(ciTypeArrayKlass::make(T_BYTE)));
+ newcopy = new_array(klass_node, size, 0); // no arguments to push
+ AllocateArrayNode* alloc = tightly_coupled_allocation(newcopy, NULL);
+
+ // 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 {
+ insert_mem_bar(Op_MemBarCPUOrder);
}
-
- // 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)));
+ } // original reexecute is set back here
+
+ C->set_has_split_ifs(true); // Has chance for split-if optimization
+ if (!stopped()) {
+ set_result(newcopy);
+ }
+ return true;
+}
+
+//------------------------inline_string_getCharsU--------------------------
+// public void StringUTF16.getChars(byte[] value, int srcBegin, int srcEnd, char dst[], int dstBegin)
+bool LibraryCallKit::inline_string_getCharsU() {
+ if (too_many_traps(Deoptimization::Reason_intrinsic)) return false;
+
+ // Get the arguments.
+ Node* value = argument(0);
+ Node* src_begin = argument(1);
+ Node* src_end = argument(2); // exclusive offset (i < src_end)
+ Node* dst = argument(3);
+ Node* dst_begin = argument(4);
+
+ // Check for allocation before we add nodes that would confuse
+ // tightly_coupled_allocation()
+ AllocateArrayNode* alloc = tightly_coupled_allocation(dst, NULL);
+
+ // Check if a null path was taken unconditionally.
+ value = null_check(value);
+ dst = null_check(dst);
+ if (stopped()) {
+ return true;
+ }
+
+ // Range checks are done by caller.
+
+ // Get length and convert char[] offset to byte[] offset
+ Node* length = _gvn.transform(new SubINode(src_end, src_begin));
+ src_begin = _gvn.transform(new LShiftINode(src_begin, intcon(1)));
+
+ if (!stopped()) {
+ // Calculate starting addresses.
+ Node* src_start = array_element_address(value, src_begin, T_BYTE);
+ Node* dst_start = array_element_address(dst, dst_begin, T_CHAR);
+
+ // Check if array addresses are aligned to HeapWordSize
+ const TypeInt* tsrc = gvn().type(src_begin)->is_int();
+ const TypeInt* tdst = gvn().type(dst_begin)->is_int();
+ 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);
}
-
- 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;
- }
- }
-
- result = string_indexOf(receiver, pat, o, cache, md2);
- }
- set_result(result);
+ }
+
+ 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);
+ }
return true;
}
@@ -4189,12 +4300,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() {
@@ -5003,10 +5108,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
@@ -5124,7 +5230,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) {
@@ -5170,7 +5276,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) {
--- a/hotspot/src/share/vm/opto/loopTransform.cpp Mon Nov 02 12:34:27 2015 +0000
+++ b/hotspot/src/share/vm/opto/loopTransform.cpp Tue Nov 03 09:41:03 2015 +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;
--- a/hotspot/src/share/vm/opto/loopnode.cpp Mon Nov 02 12:34:27 2015 +0000
+++ b/hotspot/src/share/vm/opto/loopnode.cpp Tue Nov 03 09:41:03 2015 +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 ) {
--- a/hotspot/src/share/vm/opto/macro.cpp Mon Nov 02 12:34:27 2015 +0000
+++ b/hotspot/src/share/vm/opto/macro.cpp Tue Nov 03 09:41:03 2015 +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);
--- a/hotspot/src/share/vm/opto/matcher.cpp Mon Nov 02 12:34:27 2015 +0000
+++ b/hotspot/src/share/vm/opto/matcher.cpp Tue Nov 03 09:41:03 2015 +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;
@@ -2336,7 +2344,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));
@@ -2353,6 +2362,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));
--- a/hotspot/src/share/vm/opto/stringopts.cpp Mon Nov 02 12:34:27 2015 +0000
+++ b/hotspot/src/share/vm/opto/stringopts.cpp Tue Nov 03 09:41:03 2015 +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);
+ 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);
+ 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));
+ 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);
+ kit.set_control(merge);
- C->record_for_igvn(merge);
- C->record_for_igvn(i);
- C->record_for_igvn(sign);
- }
+ 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());
- 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);
+ 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);
- 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);
-
+ kit.set_control(head);
+ kit.set_memory(mem, byte_adr_idx);
- 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);
+ 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);
- head->init_req(2, ne);
- mem->init_req(2, st);
- i_phi->init_req(2, q);
- charPos->init_req(2, m1);
-
- charPos = m1;
+ 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);
- kit.set_control(eq);
- kit.set_memory(st, char_adr_idx);
+ head->init_req(2, ne);
+ mem->init_req(2, st);
+
+ i_phi->init_req(2, q);
+ charPos->init_req(2, index);
+ charPos = index;
- C->record_for_igvn(head);
- C->record_for_igvn(mem);
- C->record_for_igvn(i_phi);
- C->record_for_igvn(charPos);
- }
+ kit.set_control(eq);
+ kit.set_memory(st, byte_adr_idx);
- {
- // 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);
+ C->record_for_igvn(head);
+ C->record_for_igvn(mem);
+ C->record_for_igvn(i_phi);
+ C->record_for_igvn(charPos);
- final_merge->init_req(2, __ IfFalse(iff));
- final_mem->init_req(2, kit.memory(char_adr_idx));
+ // 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(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);
+ 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(1, kit.control());
- final_mem->init_req(1, st);
- }
-
- kit.set_control(final_merge);
- kit.set_memory(final_mem, char_adr_idx);
-
- C->record_for_igvn(final_merge);
- C->record_for_igvn(final_mem);
+ final_merge->init_req(merge_index + 1, kit.control());
+ final_mem->init_req(merge_index + 1, st);
}
}
+// 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));
-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);
+ // 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);
+
+ 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;
- // copy the contents
- if (offset->is_Con() && count->is_Con() && value->is_Con() && count->get_int() < unroll_string_copy_length) {
+ 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
+
+ jchar b1 = ((jchar) array->byte_at(index)) & 0xff;
+ jchar b2 = ((jchar) array->byte_at(index+1)) & 0xff;
+ return (b1 << shift_high) | (b2 << shift_low);
+}
+
+// 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();
+
+ // Check encoding of constant string
+ bool src_is_byte = (get_constant_coder(kit, str) == java_lang_String::CODER_LATIN1);
+
// 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);
- }
+ // Allocate destination byte array according to coder
+ dst_array = allocate_byte_array(kit, NULL, __ LShiftI(length, coder));
- // 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);
-
- // 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();
}
--- a/hotspot/src/share/vm/opto/stringopts.hpp Mon Nov 02 12:34:27 2015 +0000
+++ b/hotspot/src/share/vm/opto/stringopts.hpp Tue Nov 03 09:41:03 2015 +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 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 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 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);
--- a/hotspot/src/share/vm/prims/jni.cpp Mon Nov 02 12:34:27 2015 +0000
+++ b/hotspot/src/share/vm/prims/jni.cpp Tue Nov 03 09:41:03 2015 +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
--- a/hotspot/src/share/vm/prims/jvmtiEnv.cpp Mon Nov 02 12:34:27 2015 +0000
+++ b/hotspot/src/share/vm/prims/jvmtiEnv.cpp Tue Nov 03 09:41:03 2015 +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;
}
--- a/hotspot/src/share/vm/prims/jvmtiTagMap.cpp Mon Nov 02 12:34:27 2015 +0000
+++ b/hotspot/src/share/vm/prims/jvmtiTagMap.cpp Tue Nov 03 09:41:03 2015 +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
--- a/hotspot/src/share/vm/runtime/globals.hpp Mon Nov 02 12:34:27 2015 +0000
+++ b/hotspot/src/share/vm/runtime/globals.hpp Tue Nov 03 09:41:03 2015 +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; " \
@@ -4259,6 +4262,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 "\
--- a/hotspot/src/share/vm/runtime/thread.cpp Mon Nov 02 12:34:27 2015 +0000
+++ b/hotspot/src/share/vm/runtime/thread.cpp Tue Nov 03 09:41:03 2015 +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.
--- a/hotspot/src/share/vm/utilities/utf8.cpp Mon Nov 02 12:34:27 2015 +0000
+++ b/hotspot/src/share/vm/utilities/utf8.cpp Tue Nov 03 09:41:03 2015 +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<typename T> 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<typename T> 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<jchar>(const char* str, jchar* value);
+template char* UTF8::next<jbyte>(const char* str, jbyte* value);
+template void UTF8::convert_to_unicode<jchar>(const char* utf8_str, jchar* unicode_str, int unicode_length);
+template void UTF8::convert_to_unicode<jbyte>(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<typename T>
+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<typename T>
+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>(jbyte* base, int length);
+template int UNICODE::quoted_ascii_length<jchar>(jchar* base, int length);
+template void UNICODE::as_quoted_ascii<jbyte>(const jbyte* base, int length, char* buf, int buflen);
+template void UNICODE::as_quoted_ascii<jchar>(const jchar* base, int length, char* buf, int buflen);
+
+
#ifndef PRODUCT
void TestAsUtf8() {
char res[60];
--- a/hotspot/src/share/vm/utilities/utf8.hpp Mon Nov 02 12:34:27 2015 +0000
+++ b/hotspot/src/share/vm/utilities/utf8.hpp Tue Nov 03 09:41:03 2015 +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<typename T> 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<typename T> 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<typename T> 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<typename T> static void as_quoted_ascii(const T* base, int length, char* buf, int buflen);
};
#endif // SHARE_VM_UTILITIES_UTF8_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/compiler/intrinsics/string/TestStringIntrinsics.java Tue Nov 03 09:41:03 2015 +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 Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/runtime/Annotations/TestAnnotatedStringEncoding.java Tue Nov 03 09:41:03 2015 +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 Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/runtime/SharedArchiveFile/CdsDifferentCompactStrings.java Tue Nov 03 09:41:03 2015 +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);
+ }
+}