8141132: JEP 254: Compact Strings
Adopt a more space-efficient internal representation for strings. Co-authored-by: Brent Christian <brent.christian@oracle.com> Co-authored-by: Vivek Deshpande <vivek.r.deshpande@intel.com> Co-authored-by: Charlie Hunt <charlie.hunt@oracle.com> Co-authored-by: Vladimir Kozlov <vladimir.kozlov@oracle.com> Co-authored-by: Roger Riggs <roger.riggs@oracle.com> Co-authored-by: Xueming Shen <xueming.shen@oracle.com> Co-authored-by: Aleksey Shipilev <aleksey.shipilev@oracle.com> Co-authored-by: Sandhya Viswanathan <sandhya.viswanathan@intel.com> Reviewed-by: alanb, bdelsart, coleenp, iklam, jiangli, jrose, kevinw, naoto, pliden, roland, smarks, twisti
This commit is contained in:
parent
4e24e2cc6c
commit
7af927f9c1
@ -40,8 +40,7 @@ public class OopUtilities implements /* imports */ JVMTIThreadState {
|
||||
// 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 @@ public class OopUtilities implements /* imports */ JVMTIThreadState {
|
||||
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 class OopUtilities implements /* imports */ JVMTIThreadState {
|
||||
}
|
||||
|
||||
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) {
|
||||
|
@ -268,8 +268,8 @@ public class HeapSummary extends Tool {
|
||||
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) {
|
||||
|
@ -61,9 +61,8 @@ public class Hashtable extends BasicHashtable {
|
||||
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;
|
||||
|
@ -14150,6 +14150,7 @@ instruct partialSubtypeCheckVsZero(iRegP_R4 sub, iRegP_R0 super, iRegP_R2 temp,
|
||||
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_compare(iRegP_R1 str1, iRegI_R2 cnt1, iRegP_R3 str2, iRegI_R4 cn
|
||||
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 @@ instruct string_indexof_con(iRegP_R1 str1, iRegI_R4 cnt1, iRegP_R3 str2,
|
||||
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_indexof_con(iRegP_R1 str1, iRegI_R4 cnt1, iRegP_R3 str2,
|
||||
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 string_equals(iRegP_R1 str1, iRegP_R3 str2, iRegI_R4 cnt,
|
||||
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);
|
||||
|
||||
|
@ -483,15 +483,6 @@ int LIR_Assembler::emit_deopt_handler() {
|
||||
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();
|
||||
|
@ -79,6 +79,9 @@ define_pd_global(uintx, CMSYoungGenPerWorker, 64*M); // default max size of CMS
|
||||
|
||||
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);
|
||||
|
||||
|
@ -72,6 +72,9 @@ define_pd_global(size_t, CMSYoungGenPerWorker, 16*M); // Default max size of CM
|
||||
|
||||
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) \
|
||||
\
|
||||
|
@ -2054,11 +2054,11 @@ const bool Matcher::match_rule_supported(int opcode) {
|
||||
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 @@ instruct string_indexOf_imm1_char(iRegIdst result, iRegPsrc haystack, iRegIsrc h
|
||||
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 @@ instruct string_indexOf_imm1(iRegIdst result, iRegPsrc haystack, iRegIsrc haycnt
|
||||
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 @@ instruct string_indexOf_imm(iRegIdst result, iRegPsrc haystack, rscratch1RegI ha
|
||||
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 @@ instruct string_indexOf(iRegIdst result, iRegPsrc haystack, rscratch1RegI haycnt
|
||||
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 @@ instruct string_equals_imm(iRegPsrc str1, iRegPsrc str2, uimmI15 cntImm, iRegIds
|
||||
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 @@ instruct string_equals(iRegPsrc str1, iRegPsrc str2, iRegIsrc cnt, iRegIdst resu
|
||||
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 @@ instruct string_equals(iRegPsrc str1, iRegPsrc str2, iRegIsrc cnt, iRegIdst resu
|
||||
// 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);
|
||||
|
@ -124,6 +124,8 @@ class Assembler : public AbstractAssembler {
|
||||
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 @@ class Assembler : public AbstractAssembler {
|
||||
fnegd_opf = 0x06,
|
||||
|
||||
alignaddr_opf = 0x18,
|
||||
bmask_opf = 0x19,
|
||||
|
||||
fadds_opf = 0x41,
|
||||
faddd_opf = 0x42,
|
||||
@ -204,6 +207,7 @@ class Assembler : public AbstractAssembler {
|
||||
|
||||
fmuls_opf = 0x49,
|
||||
fmuld_opf = 0x4a,
|
||||
bshuffle_opf = 0x4c,
|
||||
fdivs_opf = 0x4d,
|
||||
fdivd_opf = 0x4e,
|
||||
|
||||
@ -1226,6 +1230,9 @@ public:
|
||||
|
||||
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)); }
|
||||
|
@ -232,118 +232,6 @@ void LIR_Assembler::osr_entry() {
|
||||
}
|
||||
|
||||
|
||||
// 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) {
|
||||
|
@ -86,6 +86,8 @@ define_pd_global(size_t, CMSYoungGenPerWorker, 16*M); // default max size of CM
|
||||
|
||||
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, \
|
||||
|
@ -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 @@ void MacroAssembler::reinit_heapbase() {
|
||||
}
|
||||
}
|
||||
|
||||
// Compare char[] arrays aligned to 4 bytes.
|
||||
void MacroAssembler::char_arrays_equals(Register ary1, Register ary2,
|
||||
Register limit, Register result,
|
||||
Register chr1, Register chr2, Label& Ldone) {
|
||||
Label Lvector, Lloop;
|
||||
assert(chr1 == result, "should be the same");
|
||||
#ifdef COMPILER2
|
||||
|
||||
// Note: limit contains number of bytes (2*char_elements) != 0.
|
||||
andcc(limit, 0x2, chr1); // trailing character ?
|
||||
// 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 @@ void MacroAssembler::char_arrays_equals(Register ary1, Register ary2,
|
||||
add(ary2, limit, ary2);
|
||||
neg(limit, limit);
|
||||
|
||||
lduw(ary1, limit, chr1);
|
||||
lduw(ary1, limit, result);
|
||||
bind(Lloop);
|
||||
lduw(ary2, limit, chr2);
|
||||
cmp(chr1, chr2);
|
||||
lduw(ary2, limit, tmp);
|
||||
cmp(result, tmp);
|
||||
br(Assembler::notEqual, true, Assembler::pt, Ldone);
|
||||
delayed()->mov(G0, result); // not equal
|
||||
inccc(limit, 2*sizeof(jchar));
|
||||
// annul LDUW if branch is not taken to prevent access past end of array
|
||||
br(Assembler::notZero, true, Assembler::pt, Lloop);
|
||||
delayed()->lduw(ary1, limit, chr1); // hoisted
|
||||
delayed()->lduw(ary1, limit, result); // hoisted
|
||||
|
||||
// Caller should set it:
|
||||
// add(G0, 1, result); // equals
|
||||
add(G0, 1, result); // equals
|
||||
bind(Ldone);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// Use BIS for zeroing (count is in bytes).
|
||||
void MacroAssembler::bis_zeroing(Register to, Register count, Register temp, Label& Ldone) {
|
||||
assert(UseBlockZeroing && VM_Version::has_block_zeroing(), "only works with BIS zeroing");
|
||||
|
@ -1433,10 +1433,31 @@ public:
|
||||
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);
|
||||
|
||||
|
@ -2905,232 +2905,6 @@ enc_class Fast_Unlock(iRegP oop, iRegP box, o7RegP scratch, iRegP scratch2) %{
|
||||
__ 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 @@ instruct clear_array_bis_2(g1RegX cnt, o0RegP base, iRegX tmp, Universe dummy, f
|
||||
ins_pipe(long_memory_op);
|
||||
%}
|
||||
|
||||
instruct string_compare(o0RegP str1, o1RegP str2, g3RegI cnt1, g4RegI cnt2, notemp_iRegI result,
|
||||
o7RegI tmp, flagsReg ccr) %{
|
||||
instruct string_compareL(o0RegP str1, o1RegP str2, g3RegI cnt1, g4RegI cnt2, notemp_iRegI result,
|
||||
o7RegI tmp, flagsReg ccr) %{
|
||||
predicate(((StrCompNode*)n)->encoding() == StrIntrinsicNode::LL);
|
||||
match(Set result (StrComp (Binary str1 cnt1) (Binary str2 cnt2)));
|
||||
effect(USE_KILL str1, USE_KILL str2, USE_KILL cnt1, USE_KILL cnt2, KILL ccr, KILL tmp);
|
||||
ins_cost(300);
|
||||
format %{ "String Compare $str1,$cnt1,$str2,$cnt2 -> $result // KILL $tmp" %}
|
||||
ins_encode( enc_String_Compare(str1, str2, cnt1, cnt2, result) );
|
||||
format %{ "String Compare byte[] $str1,$cnt1,$str2,$cnt2 -> $result // KILL $tmp" %}
|
||||
ins_encode %{
|
||||
__ string_compare($str1$$Register, $str2$$Register,
|
||||
$cnt1$$Register, $cnt2$$Register,
|
||||
$tmp$$Register, $tmp$$Register,
|
||||
$result$$Register, StrIntrinsicNode::LL);
|
||||
%}
|
||||
ins_pipe(long_memory_op);
|
||||
%}
|
||||
|
||||
instruct string_equals(o0RegP str1, o1RegP str2, g3RegI cnt, notemp_iRegI result,
|
||||
o7RegI tmp, flagsReg ccr) %{
|
||||
instruct string_compareU(o0RegP str1, o1RegP str2, g3RegI cnt1, g4RegI cnt2, notemp_iRegI result,
|
||||
o7RegI tmp, flagsReg ccr) %{
|
||||
predicate(((StrCompNode*)n)->encoding() == StrIntrinsicNode::UU);
|
||||
match(Set result (StrComp (Binary str1 cnt1) (Binary str2 cnt2)));
|
||||
effect(USE_KILL str1, USE_KILL str2, USE_KILL cnt1, USE_KILL cnt2, KILL ccr, KILL tmp);
|
||||
ins_cost(300);
|
||||
format %{ "String Compare char[] $str1,$cnt1,$str2,$cnt2 -> $result // KILL $tmp" %}
|
||||
ins_encode %{
|
||||
__ string_compare($str1$$Register, $str2$$Register,
|
||||
$cnt1$$Register, $cnt2$$Register,
|
||||
$tmp$$Register, $tmp$$Register,
|
||||
$result$$Register, StrIntrinsicNode::UU);
|
||||
%}
|
||||
ins_pipe(long_memory_op);
|
||||
%}
|
||||
|
||||
instruct string_compareLU(o0RegP str1, o1RegP str2, g3RegI cnt1, g4RegI cnt2, notemp_iRegI result,
|
||||
o7RegI tmp1, g1RegI tmp2, flagsReg ccr) %{
|
||||
predicate(((StrCompNode*)n)->encoding() == StrIntrinsicNode::LU);
|
||||
match(Set result (StrComp (Binary str1 cnt1) (Binary str2 cnt2)));
|
||||
effect(USE_KILL str1, USE_KILL str2, USE_KILL cnt1, USE_KILL cnt2, KILL ccr, KILL tmp1, KILL tmp2);
|
||||
ins_cost(300);
|
||||
format %{ "String Compare byte[] $str1,$cnt1,$str2,$cnt2 -> $result // KILL $tmp1,$tmp2" %}
|
||||
ins_encode %{
|
||||
__ string_compare($str1$$Register, $str2$$Register,
|
||||
$cnt1$$Register, $cnt2$$Register,
|
||||
$tmp1$$Register, $tmp2$$Register,
|
||||
$result$$Register, StrIntrinsicNode::LU);
|
||||
%}
|
||||
ins_pipe(long_memory_op);
|
||||
%}
|
||||
|
||||
instruct string_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);
|
||||
%}
|
||||
|
||||
|
@ -3036,6 +3036,35 @@ void Assembler::pcmpestri(XMMRegister dst, XMMRegister src, int imm8) {
|
||||
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 @@ void Assembler::pmovzxbw(XMMRegister dst, XMMRegister src) {
|
||||
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 @@ void Assembler::vpbroadcastd(XMMRegister dst, XMMRegister src) {
|
||||
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;
|
||||
|
@ -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 @@ private:
|
||||
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 @@ private:
|
||||
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 @@ private:
|
||||
// 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);
|
||||
|
@ -509,86 +509,6 @@ int LIR_Assembler::emit_deopt_handler() {
|
||||
}
|
||||
|
||||
|
||||
// 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()) {
|
||||
|
@ -91,6 +91,8 @@ define_pd_global(size_t, CMSYoungGenPerWorker, 64*M); // default max size of CM
|
||||
|
||||
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) \
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -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 @@ public:
|
||||
// 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 @@ public:
|
||||
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
|
||||
|
||||
};
|
||||
|
@ -11435,16 +11435,62 @@ instruct rep_fast_stosb(eCXRegI cnt, eDIRegP base, eAXRegI zero, Universe dummy,
|
||||
ins_pipe( pipe_slow );
|
||||
%}
|
||||
|
||||
instruct string_compare(eDIRegP str1, eCXRegI cnt1, eSIRegP str2, eDXRegI cnt2,
|
||||
eAXRegI result, regD tmp1, eFlagsReg cr) %{
|
||||
instruct string_compareL(eDIRegP str1, eCXRegI cnt1, eSIRegP str2, eDXRegI cnt2,
|
||||
eAXRegI result, regD tmp1, eFlagsReg cr) %{
|
||||
predicate(((StrCompNode*)n)->encoding() == StrIntrinsicNode::LL);
|
||||
match(Set result (StrComp (Binary str1 cnt1) (Binary str2 cnt2)));
|
||||
effect(TEMP tmp1, USE_KILL str1, USE_KILL str2, USE_KILL cnt1, USE_KILL cnt2, KILL cr);
|
||||
|
||||
format %{ "String Compare $str1,$cnt1,$str2,$cnt2 -> $result // KILL $tmp1" %}
|
||||
format %{ "String Compare byte[] $str1,$cnt1,$str2,$cnt2 -> $result // KILL $tmp1" %}
|
||||
ins_encode %{
|
||||
__ string_compare($str1$$Register, $str2$$Register,
|
||||
$cnt1$$Register, $cnt2$$Register, $result$$Register,
|
||||
$tmp1$$XMMRegister);
|
||||
$tmp1$$XMMRegister, StrIntrinsicNode::LL);
|
||||
%}
|
||||
ins_pipe( pipe_slow );
|
||||
%}
|
||||
|
||||
instruct string_compareU(eDIRegP str1, eCXRegI cnt1, eSIRegP str2, eDXRegI cnt2,
|
||||
eAXRegI result, regD tmp1, eFlagsReg cr) %{
|
||||
predicate(((StrCompNode*)n)->encoding() == StrIntrinsicNode::UU);
|
||||
match(Set result (StrComp (Binary str1 cnt1) (Binary str2 cnt2)));
|
||||
effect(TEMP tmp1, USE_KILL str1, USE_KILL str2, USE_KILL cnt1, USE_KILL cnt2, KILL cr);
|
||||
|
||||
format %{ "String Compare char[] $str1,$cnt1,$str2,$cnt2 -> $result // KILL $tmp1" %}
|
||||
ins_encode %{
|
||||
__ string_compare($str1$$Register, $str2$$Register,
|
||||
$cnt1$$Register, $cnt2$$Register, $result$$Register,
|
||||
$tmp1$$XMMRegister, StrIntrinsicNode::UU);
|
||||
%}
|
||||
ins_pipe( pipe_slow );
|
||||
%}
|
||||
|
||||
instruct string_compareLU(eDIRegP str1, eCXRegI cnt1, eSIRegP str2, eDXRegI cnt2,
|
||||
eAXRegI result, regD tmp1, eFlagsReg cr) %{
|
||||
predicate(((StrCompNode*)n)->encoding() == StrIntrinsicNode::LU);
|
||||
match(Set result (StrComp (Binary str1 cnt1) (Binary str2 cnt2)));
|
||||
effect(TEMP tmp1, USE_KILL str1, USE_KILL str2, USE_KILL cnt1, USE_KILL cnt2, KILL cr);
|
||||
|
||||
format %{ "String Compare byte[] $str1,$cnt1,$str2,$cnt2 -> $result // KILL $tmp1" %}
|
||||
ins_encode %{
|
||||
__ string_compare($str1$$Register, $str2$$Register,
|
||||
$cnt1$$Register, $cnt2$$Register, $result$$Register,
|
||||
$tmp1$$XMMRegister, StrIntrinsicNode::LU);
|
||||
%}
|
||||
ins_pipe( pipe_slow );
|
||||
%}
|
||||
|
||||
instruct string_compareUL(eSIRegP str1, eDXRegI cnt1, eDIRegP str2, eCXRegI cnt2,
|
||||
eAXRegI result, regD tmp1, eFlagsReg cr) %{
|
||||
predicate(((StrCompNode*)n)->encoding() == StrIntrinsicNode::UL);
|
||||
match(Set result (StrComp (Binary str1 cnt1) (Binary str2 cnt2)));
|
||||
effect(TEMP tmp1, USE_KILL str1, USE_KILL str2, USE_KILL cnt1, USE_KILL cnt2, KILL cr);
|
||||
|
||||
format %{ "String Compare byte[] $str1,$cnt1,$str2,$cnt2 -> $result // KILL $tmp1" %}
|
||||
ins_encode %{
|
||||
__ string_compare($str2$$Register, $str1$$Register,
|
||||
$cnt2$$Register, $cnt1$$Register, $result$$Register,
|
||||
$tmp1$$XMMRegister, StrIntrinsicNode::UL);
|
||||
%}
|
||||
ins_pipe( pipe_slow );
|
||||
%}
|
||||
@ -11457,21 +11503,50 @@ instruct string_equals(eDIRegP str1, eSIRegP str2, eCXRegI cnt, eAXRegI result,
|
||||
|
||||
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) {
|
||||
@ -11480,47 +11555,182 @@ instruct string_indexof_con(eDIRegP str1, eDXRegI cnt1, eSIRegP str2, immI int_c
|
||||
__ string_indexofC8($str1$$Register, $str2$$Register,
|
||||
$cnt1$$Register, $cnt2$$Register,
|
||||
icnt2, $result$$Register,
|
||||
$vec$$XMMRegister, $tmp$$Register);
|
||||
$vec$$XMMRegister, $tmp$$Register, StrIntrinsicNode::UU);
|
||||
} else {
|
||||
// Small strings are loaded through stack if they cross page boundary.
|
||||
__ string_indexof($str1$$Register, $str2$$Register,
|
||||
$cnt1$$Register, $cnt2$$Register,
|
||||
icnt2, $result$$Register,
|
||||
$vec$$XMMRegister, $tmp$$Register);
|
||||
$vec$$XMMRegister, $tmp$$Register, StrIntrinsicNode::UU);
|
||||
}
|
||||
%}
|
||||
ins_pipe( pipe_slow );
|
||||
%}
|
||||
|
||||
instruct string_indexof(eDIRegP str1, eDXRegI cnt1, eSIRegP str2, eAXRegI cnt2,
|
||||
eBXRegI result, regD vec, eCXRegI tmp, eFlagsReg cr) %{
|
||||
predicate(UseSSE42Intrinsics);
|
||||
// fast search of substring with known size.
|
||||
instruct string_indexof_conUL(eDIRegP str1, eDXRegI cnt1, eSIRegP str2, immI int_cnt2,
|
||||
eBXRegI result, regD vec, eAXRegI cnt2, eCXRegI tmp, eFlagsReg cr) %{
|
||||
predicate(UseSSE42Intrinsics && (((StrIndexOfNode*)n)->encoding() == StrIntrinsicNode::UL));
|
||||
match(Set result (StrIndexOf (Binary str1 cnt1) (Binary str2 int_cnt2)));
|
||||
effect(TEMP vec, USE_KILL str1, USE_KILL str2, USE_KILL cnt1, KILL cnt2, KILL tmp, KILL cr);
|
||||
|
||||
format %{ "String IndexOf char[] $str1,$cnt1,$str2,$int_cnt2 -> $result // KILL $vec, $cnt1, $cnt2, $tmp" %}
|
||||
ins_encode %{
|
||||
int icnt2 = (int)$int_cnt2$$constant;
|
||||
if (icnt2 >= 8) {
|
||||
// IndexOf for constant substrings with size >= 8 elements
|
||||
// which don't need to be loaded through stack.
|
||||
__ string_indexofC8($str1$$Register, $str2$$Register,
|
||||
$cnt1$$Register, $cnt2$$Register,
|
||||
icnt2, $result$$Register,
|
||||
$vec$$XMMRegister, $tmp$$Register, StrIntrinsicNode::UL);
|
||||
} else {
|
||||
// Small strings are loaded through stack if they cross page boundary.
|
||||
__ string_indexof($str1$$Register, $str2$$Register,
|
||||
$cnt1$$Register, $cnt2$$Register,
|
||||
icnt2, $result$$Register,
|
||||
$vec$$XMMRegister, $tmp$$Register, StrIntrinsicNode::UL);
|
||||
}
|
||||
%}
|
||||
ins_pipe( pipe_slow );
|
||||
%}
|
||||
|
||||
instruct string_indexofL(eDIRegP str1, eDXRegI cnt1, eSIRegP str2, eAXRegI cnt2,
|
||||
eBXRegI result, regD vec, eCXRegI tmp, eFlagsReg cr) %{
|
||||
predicate(UseSSE42Intrinsics && (((StrIndexOfNode*)n)->encoding() == StrIntrinsicNode::LL));
|
||||
match(Set result (StrIndexOf (Binary str1 cnt1) (Binary str2 cnt2)));
|
||||
effect(TEMP vec, USE_KILL str1, USE_KILL str2, USE_KILL cnt1, USE_KILL cnt2, KILL tmp, KILL cr);
|
||||
|
||||
format %{ "String IndexOf $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 $ary1,$ary2 -> $result // KILL $tmp1, $tmp2, $tmp3, $tmp4" %}
|
||||
format %{ "Array Equals byte[] $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);
|
||||
__ 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 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 );
|
||||
%}
|
||||
|
@ -10447,30 +10447,108 @@ instruct rep_fast_stosb(rcx_RegL cnt, rdi_RegP base, rax_RegI zero, Universe dum
|
||||
ins_pipe( pipe_slow );
|
||||
%}
|
||||
|
||||
instruct string_compare(rdi_RegP str1, rcx_RegI cnt1, rsi_RegP str2, rdx_RegI cnt2,
|
||||
rax_RegI result, regD tmp1, rFlagsReg cr)
|
||||
instruct string_compareL(rdi_RegP str1, rcx_RegI cnt1, rsi_RegP str2, rdx_RegI cnt2,
|
||||
rax_RegI result, regD tmp1, rFlagsReg cr)
|
||||
%{
|
||||
predicate(((StrCompNode*)n)->encoding() == StrIntrinsicNode::LL);
|
||||
match(Set result (StrComp (Binary str1 cnt1) (Binary str2 cnt2)));
|
||||
effect(TEMP tmp1, USE_KILL str1, USE_KILL str2, USE_KILL cnt1, USE_KILL cnt2, KILL cr);
|
||||
|
||||
format %{ "String Compare $str1,$cnt1,$str2,$cnt2 -> $result // KILL $tmp1" %}
|
||||
format %{ "String Compare byte[] $str1,$cnt1,$str2,$cnt2 -> $result // KILL $tmp1" %}
|
||||
ins_encode %{
|
||||
__ string_compare($str1$$Register, $str2$$Register,
|
||||
$cnt1$$Register, $cnt2$$Register, $result$$Register,
|
||||
$tmp1$$XMMRegister);
|
||||
$tmp1$$XMMRegister, StrIntrinsicNode::LL);
|
||||
%}
|
||||
ins_pipe( pipe_slow );
|
||||
%}
|
||||
|
||||
instruct string_compareU(rdi_RegP str1, rcx_RegI cnt1, rsi_RegP str2, rdx_RegI cnt2,
|
||||
rax_RegI result, regD tmp1, rFlagsReg cr)
|
||||
%{
|
||||
predicate(((StrCompNode*)n)->encoding() == StrIntrinsicNode::UU);
|
||||
match(Set result (StrComp (Binary str1 cnt1) (Binary str2 cnt2)));
|
||||
effect(TEMP tmp1, USE_KILL str1, USE_KILL str2, USE_KILL cnt1, USE_KILL cnt2, KILL cr);
|
||||
|
||||
format %{ "String Compare char[] $str1,$cnt1,$str2,$cnt2 -> $result // KILL $tmp1" %}
|
||||
ins_encode %{
|
||||
__ string_compare($str1$$Register, $str2$$Register,
|
||||
$cnt1$$Register, $cnt2$$Register, $result$$Register,
|
||||
$tmp1$$XMMRegister, StrIntrinsicNode::UU);
|
||||
%}
|
||||
ins_pipe( pipe_slow );
|
||||
%}
|
||||
|
||||
instruct string_compareLU(rdi_RegP str1, rcx_RegI cnt1, rsi_RegP str2, rdx_RegI cnt2,
|
||||
rax_RegI result, regD tmp1, rFlagsReg cr)
|
||||
%{
|
||||
predicate(((StrCompNode*)n)->encoding() == StrIntrinsicNode::LU);
|
||||
match(Set result (StrComp (Binary str1 cnt1) (Binary str2 cnt2)));
|
||||
effect(TEMP tmp1, USE_KILL str1, USE_KILL str2, USE_KILL cnt1, USE_KILL cnt2, KILL cr);
|
||||
|
||||
format %{ "String Compare byte[] $str1,$cnt1,$str2,$cnt2 -> $result // KILL $tmp1" %}
|
||||
ins_encode %{
|
||||
__ string_compare($str1$$Register, $str2$$Register,
|
||||
$cnt1$$Register, $cnt2$$Register, $result$$Register,
|
||||
$tmp1$$XMMRegister, StrIntrinsicNode::LU);
|
||||
%}
|
||||
ins_pipe( pipe_slow );
|
||||
%}
|
||||
|
||||
instruct string_compareUL(rsi_RegP str1, rdx_RegI cnt1, rdi_RegP str2, rcx_RegI cnt2,
|
||||
rax_RegI result, regD tmp1, rFlagsReg cr)
|
||||
%{
|
||||
predicate(((StrCompNode*)n)->encoding() == StrIntrinsicNode::UL);
|
||||
match(Set result (StrComp (Binary str1 cnt1) (Binary str2 cnt2)));
|
||||
effect(TEMP tmp1, USE_KILL str1, USE_KILL str2, USE_KILL cnt1, USE_KILL cnt2, KILL cr);
|
||||
|
||||
format %{ "String Compare byte[] $str1,$cnt1,$str2,$cnt2 -> $result // KILL $tmp1" %}
|
||||
ins_encode %{
|
||||
__ string_compare($str2$$Register, $str1$$Register,
|
||||
$cnt2$$Register, $cnt1$$Register, $result$$Register,
|
||||
$tmp1$$XMMRegister, StrIntrinsicNode::UL);
|
||||
%}
|
||||
ins_pipe( pipe_slow );
|
||||
%}
|
||||
|
||||
// fast search of substring with known size.
|
||||
instruct string_indexof_con(rdi_RegP str1, rdx_RegI cnt1, rsi_RegP str2, immI int_cnt2,
|
||||
rbx_RegI result, regD vec, rax_RegI cnt2, rcx_RegI tmp, rFlagsReg cr)
|
||||
instruct string_indexof_conL(rdi_RegP str1, rdx_RegI cnt1, rsi_RegP str2, immI int_cnt2,
|
||||
rbx_RegI result, regD vec, rax_RegI cnt2, rcx_RegI tmp, rFlagsReg cr)
|
||||
%{
|
||||
predicate(UseSSE42Intrinsics);
|
||||
predicate(UseSSE42Intrinsics && (((StrIndexOfNode*)n)->encoding() == StrIntrinsicNode::LL));
|
||||
match(Set result (StrIndexOf (Binary str1 cnt1) (Binary str2 int_cnt2)));
|
||||
effect(TEMP vec, USE_KILL str1, USE_KILL str2, USE_KILL cnt1, KILL cnt2, KILL tmp, KILL cr);
|
||||
|
||||
format %{ "String IndexOf $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 @@ instruct string_indexof_con(rdi_RegP str1, rdx_RegI cnt1, rsi_RegP str2, immI in
|
||||
__ string_indexofC8($str1$$Register, $str2$$Register,
|
||||
$cnt1$$Register, $cnt2$$Register,
|
||||
icnt2, $result$$Register,
|
||||
$vec$$XMMRegister, $tmp$$Register);
|
||||
$vec$$XMMRegister, $tmp$$Register, StrIntrinsicNode::UU);
|
||||
} else {
|
||||
// Small strings are loaded through stack if they cross page boundary.
|
||||
__ string_indexof($str1$$Register, $str2$$Register,
|
||||
$cnt1$$Register, $cnt2$$Register,
|
||||
icnt2, $result$$Register,
|
||||
$vec$$XMMRegister, $tmp$$Register);
|
||||
$vec$$XMMRegister, $tmp$$Register, StrIntrinsicNode::UU);
|
||||
}
|
||||
%}
|
||||
ins_pipe( pipe_slow );
|
||||
%}
|
||||
|
||||
instruct string_indexof(rdi_RegP str1, rdx_RegI cnt1, rsi_RegP str2, rax_RegI cnt2,
|
||||
rbx_RegI result, regD vec, rcx_RegI tmp, rFlagsReg cr)
|
||||
// fast search of substring with known size.
|
||||
instruct string_indexof_conUL(rdi_RegP str1, rdx_RegI cnt1, rsi_RegP str2, immI int_cnt2,
|
||||
rbx_RegI result, regD vec, rax_RegI cnt2, rcx_RegI tmp, rFlagsReg cr)
|
||||
%{
|
||||
predicate(UseSSE42Intrinsics);
|
||||
predicate(UseSSE42Intrinsics && (((StrIndexOfNode*)n)->encoding() == StrIntrinsicNode::UL));
|
||||
match(Set result (StrIndexOf (Binary str1 cnt1) (Binary str2 int_cnt2)));
|
||||
effect(TEMP vec, USE_KILL str1, USE_KILL str2, USE_KILL cnt1, KILL cnt2, KILL tmp, KILL cr);
|
||||
|
||||
format %{ "String IndexOf char[] $str1,$cnt1,$str2,$int_cnt2 -> $result // KILL $vec, $cnt1, $cnt2, $tmp" %}
|
||||
ins_encode %{
|
||||
int icnt2 = (int)$int_cnt2$$constant;
|
||||
if (icnt2 >= 8) {
|
||||
// IndexOf for constant substrings with size >= 8 elements
|
||||
// which don't need to be loaded through stack.
|
||||
__ string_indexofC8($str1$$Register, $str2$$Register,
|
||||
$cnt1$$Register, $cnt2$$Register,
|
||||
icnt2, $result$$Register,
|
||||
$vec$$XMMRegister, $tmp$$Register, StrIntrinsicNode::UL);
|
||||
} else {
|
||||
// Small strings are loaded through stack if they cross page boundary.
|
||||
__ string_indexof($str1$$Register, $str2$$Register,
|
||||
$cnt1$$Register, $cnt2$$Register,
|
||||
icnt2, $result$$Register,
|
||||
$vec$$XMMRegister, $tmp$$Register, StrIntrinsicNode::UL);
|
||||
}
|
||||
%}
|
||||
ins_pipe( pipe_slow );
|
||||
%}
|
||||
|
||||
instruct string_indexofL(rdi_RegP str1, rdx_RegI cnt1, rsi_RegP str2, rax_RegI cnt2,
|
||||
rbx_RegI result, regD vec, rcx_RegI tmp, rFlagsReg cr)
|
||||
%{
|
||||
predicate(UseSSE42Intrinsics && (((StrIndexOfNode*)n)->encoding() == StrIntrinsicNode::LL));
|
||||
match(Set result (StrIndexOf (Binary str1 cnt1) (Binary str2 cnt2)));
|
||||
effect(TEMP vec, USE_KILL str1, USE_KILL str2, USE_KILL cnt1, USE_KILL cnt2, KILL tmp, KILL cr);
|
||||
|
||||
format %{ "String IndexOf $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 @@ instruct string_equals(rdi_RegP str1, rsi_RegP str2, rcx_RegI cnt, rax_RegI resu
|
||||
|
||||
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,
|
||||
regD tmp1, regD tmp2, rcx_RegI tmp3, rbx_RegI tmp4, rFlagsReg cr)
|
||||
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);
|
||||
//ins_cost(300);
|
||||
|
||||
format %{ "Array Equals $ary1,$ary2 -> $result // KILL $tmp1, $tmp2, $tmp3, $tmp4" %}
|
||||
format %{ "Array Equals byte[] $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);
|
||||
__ 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);
|
||||
|
||||
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 );
|
||||
%}
|
||||
|
@ -69,6 +69,9 @@ define_pd_global(uintx, TypeProfileLevel, 0);
|
||||
|
||||
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, \
|
||||
|
@ -607,6 +607,8 @@ bool InstructForm::needs_anti_dependence_check(FormDict &globals) const {
|
||||
( 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 @@ uint InstructForm::oper_input_base(FormDict &globals) {
|
||||
( 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;
|
||||
}
|
||||
|
||||
|
@ -221,6 +221,7 @@ int main(int argc, char *argv[])
|
||||
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 @@ int main(int argc, char *argv[])
|
||||
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");
|
||||
|
@ -225,6 +225,8 @@ bool Compiler::is_intrinsic_supported(methodHandle method) {
|
||||
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:
|
||||
|
@ -3445,6 +3445,8 @@ void GraphBuilder::build_graph_for_intrinsic(ciMethod* callee) {
|
||||
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 @@ void GraphBuilder::append_unsafe_CAS(ciMethod* callee) {
|
||||
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();
|
||||
|
@ -372,6 +372,7 @@ class GraphBuilder VALUE_OBJ_CLASS_SPEC {
|
||||
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);
|
||||
|
||||
|
@ -161,8 +161,6 @@ class LIR_Assembler: public CompilationResourceObj {
|
||||
// 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
|
||||
|
@ -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 @@ jchar ciTypeArray::char_at(int index) {
|
||||
#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);
|
||||
}
|
||||
|
@ -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 @@ public:
|
||||
// array will never change.
|
||||
jchar char_at(int index);
|
||||
|
||||
// Return byte at index.
|
||||
jbyte byte_at(int index);
|
||||
|
||||
};
|
||||
|
||||
#endif // SHARE_VM_CI_CITYPEARRAY_HPP
|
||||
|
@ -147,9 +147,8 @@ compute_optional_offset(int& dest_offset,
|
||||
|
||||
|
||||
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 @@ void java_lang_String::compute_offsets() {
|
||||
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 @@ Handle java_lang_String::basic_create(int length, TRAPS) {
|
||||
// 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 @@ Handle java_lang_String::create_from_str(const char* utf8_str, TRAPS) {
|
||||
if (utf8_str == NULL) {
|
||||
return Handle();
|
||||
}
|
||||
int length = UTF8::unicode_length(utf8_str);
|
||||
Handle h_obj = basic_create(length, CHECK_NH);
|
||||
if (length > 0) {
|
||||
UTF8::convert_to_unicode(utf8_str, value(h_obj())->char_at_addr(0), length);
|
||||
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) {
|
||||
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 @@ oop java_lang_String::create_oop_from_str(const char* utf8_str, TRAPS) {
|
||||
}
|
||||
|
||||
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);
|
||||
if (length > 0) {
|
||||
UTF8::convert_to_unicode((char*)symbol->bytes(), value(h_obj())->char_at_addr(0), length);
|
||||
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) {
|
||||
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 @@ Handle java_lang_String::create_from_platform_dependent_str(const char* str, TRA
|
||||
// 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 @@ Handle java_lang_String::char_converter(Handle java_string, jchar from_char, jch
|
||||
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 @@ Handle java_lang_String::char_converter(Handle java_string, jchar from_char, jch
|
||||
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 @@ unsigned int java_lang_String::hash_code(oop java_string) {
|
||||
if (length == 0) return 0;
|
||||
|
||||
typeArrayOop value = java_lang_String::value(java_string);
|
||||
int offset = java_lang_String::offset(java_string);
|
||||
return java_lang_String::hash_code(value->char_at_addr(offset), length);
|
||||
bool is_latin1 = java_lang_String::is_latin1(java_string);
|
||||
|
||||
if (is_latin1) {
|
||||
return java_lang_String::hash_code(value->byte_at_addr(0), length);
|
||||
} else {
|
||||
return java_lang_String::hash_code(value->char_at_addr(0), length);
|
||||
}
|
||||
}
|
||||
|
||||
char* java_lang_String::as_quoted_ascii(oop java_string) {
|
||||
typeArrayOop value = java_lang_String::value(java_string);
|
||||
int offset = java_lang_String::offset(java_string);
|
||||
int length = java_lang_String::length(java_string);
|
||||
bool is_latin1 = java_lang_String::is_latin1(java_string);
|
||||
|
||||
jchar* base = (length == 0) ? NULL : value->char_at_addr(offset);
|
||||
if (base == NULL) return NULL;
|
||||
if (length == 0) return NULL;
|
||||
|
||||
int result_length = UNICODE::quoted_ascii_length(base, length) + 1;
|
||||
char* result = NEW_RESOURCE_ARRAY(char, result_length);
|
||||
UNICODE::as_quoted_ascii(base, length, result, result_length);
|
||||
char* result;
|
||||
int result_length;
|
||||
if (!is_latin1) {
|
||||
jchar* base = value->char_at_addr(0);
|
||||
result_length = UNICODE::quoted_ascii_length(base, length) + 1;
|
||||
result = NEW_RESOURCE_ARRAY(char, result_length);
|
||||
UNICODE::as_quoted_ascii(base, length, result, result_length);
|
||||
} else {
|
||||
jbyte* base = value->byte_at_addr(0);
|
||||
result_length = UNICODE::quoted_ascii_length(base, length) + 1;
|
||||
result = NEW_RESOURCE_ARRAY(char, result_length);
|
||||
UNICODE::as_quoted_ascii(base, length, result, result_length);
|
||||
}
|
||||
assert(result_length >= length + 1, "must not be shorter");
|
||||
assert(result_length == (int)strlen(result) + 1, "must match");
|
||||
return result;
|
||||
@ -370,89 +514,141 @@ char* java_lang_String::as_quoted_ascii(oop java_string) {
|
||||
|
||||
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 @@ bool java_lang_String::equals(oop str1, oop str2) {
|
||||
assert(str2->klass() == SystemDictionary::String_klass(),
|
||||
"must be java String");
|
||||
typeArrayOop value1 = java_lang_String::value(str1);
|
||||
int offset1 = java_lang_String::offset(str1);
|
||||
int length1 = java_lang_String::length(str1);
|
||||
bool is_latin1 = java_lang_String::is_latin1(str1);
|
||||
typeArrayOop value2 = java_lang_String::value(str2);
|
||||
int offset2 = java_lang_String::offset(str2);
|
||||
int length2 = java_lang_String::length(str2);
|
||||
bool is_latin2 = java_lang_String::is_latin1(str2);
|
||||
|
||||
if (length1 != length2) {
|
||||
if ((length1 != length2) || (is_latin1 != is_latin2)) {
|
||||
// Strings of different size or with different
|
||||
// coders are never equal.
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < length1; i++) {
|
||||
if (value1->char_at(i + offset1) != value2->char_at(i + offset2)) {
|
||||
int blength1 = value1->length();
|
||||
for (int i = 0; i < value1->length(); i++) {
|
||||
if (value1->byte_at(i) != value2->byte_at(i)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -492,12 +691,13 @@ void java_lang_String::print(oop java_string, outputStream* st) {
|
||||
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 @@ oop java_lang_ThreadGroup::parent(oop java_thread_group) {
|
||||
|
||||
// ("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 @@ void JavaClasses::check_offsets() {
|
||||
|
||||
// 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
|
||||
|
||||
|
@ -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 @@ class java_lang_String : AllStatic {
|
||||
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 @@ class java_lang_String : AllStatic {
|
||||
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 @@ class java_lang_String : AllStatic {
|
||||
// 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 @@ class java_lang_String : AllStatic {
|
||||
}
|
||||
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 @@ class java_lang_ThreadGroup : AllStatic {
|
||||
// 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);
|
||||
|
@ -94,11 +94,16 @@ volatile int StringTable::_parallel_claimed_idx = 0;
|
||||
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 @@ void StringTable::dump(outputStream* st, bool verbose) {
|
||||
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);
|
||||
|
@ -111,7 +111,7 @@ public:
|
||||
// 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;
|
||||
|
@ -244,7 +244,7 @@ Symbol* SymbolTable::lookup(int index, const char* name,
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -165,7 +165,7 @@ public:
|
||||
// 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);
|
||||
|
||||
|
@ -425,10 +425,26 @@ bool vmIntrinsics::is_disabled_by_flags(methodHandle method) {
|
||||
// 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 @@ bool vmIntrinsics::is_disabled_by_flags(methodHandle method) {
|
||||
case vmIntrinsics::_loadFence:
|
||||
case vmIntrinsics::_storeFence:
|
||||
case vmIntrinsics::_fullFence:
|
||||
case vmIntrinsics::_hasNegatives:
|
||||
case vmIntrinsics::_Reference_get:
|
||||
break;
|
||||
default:
|
||||
@ -619,19 +636,31 @@ bool vmIntrinsics::is_disabled_by_flags(methodHandle method) {
|
||||
// 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 @@ bool vmIntrinsics::is_disabled_by_flags(methodHandle method) {
|
||||
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;
|
||||
|
@ -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") \
|
||||
|
@ -198,10 +198,11 @@ void G1StringDedupTable::create() {
|
||||
_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 @@ void G1StringDedupTable::transfer(G1StringDedupEntry** pentry, G1StringDedupTabl
|
||||
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 @@ typeArrayOop G1StringDedupTable::lookup(typeArrayOop value, unsigned int hash,
|
||||
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 @@ typeArrayOop G1StringDedupTable::lookup_or_add_inner(typeArrayOop value, unsigne
|
||||
|
||||
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 @@ typeArrayOop G1StringDedupTable::lookup_or_add_inner(typeArrayOop value, unsigne
|
||||
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 @@ void G1StringDedupTable::deduplicate(oop java_string, G1StringDedupStat& stat) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool latin1 = java_lang_String::is_latin1(java_string);
|
||||
unsigned int hash = 0;
|
||||
|
||||
if (use_java_hash()) {
|
||||
@ -308,7 +319,7 @@ void G1StringDedupTable::deduplicate(oop java_string, G1StringDedupStat& stat) {
|
||||
|
||||
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 @@ void G1StringDedupTable::deduplicate(oop java_string, G1StringDedupStat& stat) {
|
||||
}
|
||||
}
|
||||
|
||||
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 @@ uintx G1StringDedupTable::unlink_or_oops_do(G1StringDedupUnlinkOrOopsDoClosure*
|
||||
// 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 @@ void G1StringDedupTable::verify() {
|
||||
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 @@ void G1StringDedupTable::verify() {
|
||||
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();
|
||||
|
@ -40,12 +40,14 @@ class G1StringDedupEntry : public CHeapObj<mtGC> {
|
||||
private:
|
||||
G1StringDedupEntry* _next;
|
||||
unsigned int _hash;
|
||||
bool _latin1;
|
||||
typeArrayOop _obj;
|
||||
|
||||
public:
|
||||
G1StringDedupEntry() :
|
||||
_next(NULL),
|
||||
_hash(0),
|
||||
_latin1(false),
|
||||
_obj(NULL) {
|
||||
}
|
||||
|
||||
@ -69,6 +71,14 @@ public:
|
||||
_hash = hash;
|
||||
}
|
||||
|
||||
bool latin1() {
|
||||
return _latin1;
|
||||
}
|
||||
|
||||
void set_latin1(bool latin1) {
|
||||
_latin1 = latin1;
|
||||
}
|
||||
|
||||
typeArrayOop obj() {
|
||||
return _obj;
|
||||
}
|
||||
@ -152,7 +162,7 @@ private:
|
||||
}
|
||||
|
||||
// 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 @@ private:
|
||||
|
||||
// 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 @@ private:
|
||||
|
||||
// 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,
|
||||
|
@ -168,6 +168,7 @@ void FileMapInfo::FileMapHeader::populate(FileMapInfo* mapinfo, size_t alignment
|
||||
_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 @@ bool FileMapInfo::FileMapHeader::validate() {
|
||||
_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;
|
||||
}
|
||||
|
@ -95,6 +95,7 @@ public:
|
||||
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
|
||||
|
@ -540,7 +540,7 @@ void VM_PopulateDumpSharedSpace::doit() {
|
||||
|
||||
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].
|
||||
|
@ -2921,12 +2921,10 @@ void InstanceKlass::oop_print_on(oop obj, outputStream* st) {
|
||||
|
||||
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();
|
||||
|
@ -48,47 +48,47 @@ class typeArrayOopDesc : public arrayOopDesc {
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
|
@ -181,12 +181,25 @@ bool C2Compiler::is_intrinsic_supported(methodHandle method, bool is_virtual) {
|
||||
}
|
||||
|
||||
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 @@ bool C2Compiler::is_intrinsic_supported(methodHandle method, bool is_virtual) {
|
||||
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 @@ bool C2Compiler::is_intrinsic_supported(methodHandle method, bool is_virtual) {
|
||||
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:
|
||||
|
@ -136,6 +136,7 @@ macro(FastLock)
|
||||
macro(FastUnlock)
|
||||
macro(Goto)
|
||||
macro(Halt)
|
||||
macro(HasNegatives)
|
||||
macro(If)
|
||||
macro(IfFalse)
|
||||
macro(IfTrue)
|
||||
@ -246,8 +247,11 @@ macro(StoreP)
|
||||
macro(StoreN)
|
||||
macro(StoreNKlass)
|
||||
macro(StrComp)
|
||||
macro(StrCompressedCopy)
|
||||
macro(StrEquals)
|
||||
macro(StrIndexOf)
|
||||
macro(StrIndexOfChar)
|
||||
macro(StrInflatedCopy)
|
||||
macro(SubD)
|
||||
macro(SubF)
|
||||
macro(SubI)
|
||||
|
@ -560,9 +560,13 @@ void ConnectionGraph::add_node_to_connection_graph(Node *n, Unique_Node_List *de
|
||||
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 @@ void ConnectionGraph::add_final_edges(Node *n) {
|
||||
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 @@ Node* ConnectionGraph::find_inst_mem(Node *orig_mem, int alias_idx, GrowableArra
|
||||
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 @@ void ConnectionGraph::split_unique_types(GrowableArray<Node *> &alloc_worklist,
|
||||
}
|
||||
} 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 @@ void ConnectionGraph::split_unique_types(GrowableArray<Node *> &alloc_worklist,
|
||||
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 @@ void ConnectionGraph::split_unique_types(GrowableArray<Node *> &alloc_worklist,
|
||||
}
|
||||
} 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");
|
||||
|
@ -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 @@ Block* PhaseCFG::insert_anti_dependences(Block* LCA, Node* load, bool verify) {
|
||||
"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,
|
||||
|
@ -4266,35 +4266,11 @@ void GraphKit::g1_write_barrier_post(Node* oop_store,
|
||||
#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 @@ Node* GraphKit::load_String_value(Node* ctrl, Node* str) {
|
||||
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 @@ Node* GraphKit::load_String_value(Node* ctrl, Node* str) {
|
||||
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 @@ void GraphKit::store_String_value(Node* ctrl, Node* str, Node* value) {
|
||||
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) {
|
||||
|
@ -864,12 +864,14 @@ class GraphKit : public Phase {
|
||||
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) {
|
||||
|
@ -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 @@ void IdealKit::make_leaf_call(const TypeFunc *slow_call_type,
|
||||
"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,
|
||||
|
@ -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 @@ class IdealKit: public StackObj {
|
||||
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 @@ class IdealKit: public StackObj {
|
||||
Node* parm1,
|
||||
Node* parm2,
|
||||
Node* parm3);
|
||||
|
||||
};
|
||||
|
||||
#endif // SHARE_VM_OPTO_IDEALKIT_HPP
|
||||
|
@ -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 @@ uint StrIntrinsicNode::match_edge(uint idx) const {
|
||||
//------------------------------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 @@ Node *StrIntrinsicNode::Ideal(PhaseGVN *phase, bool can_reshape) {
|
||||
}
|
||||
|
||||
//------------------------------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 @@ uint EncodeISOArrayNode::match_edge(uint idx) const {
|
||||
//------------------------------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();
|
||||
}
|
||||
|
@ -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 @@ class AryEqNode: public StrIntrinsicNode {
|
||||
//------------------------------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
|
||||
|
@ -196,8 +196,12 @@ void PhaseCFG::implicit_null_check(Block* block, Node *proj, Node *val, int allo
|
||||
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;
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -622,8 +622,10 @@ bool IdealLoopTree::policy_maximally_unroll( PhaseIdealLoop *phase ) const {
|
||||
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 @@ bool IdealLoopTree::policy_unroll(PhaseIdealLoop *phase) {
|
||||
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;
|
||||
|
@ -3494,7 +3494,9 @@ void PhaseIdealLoop::build_loop_late_post( Node *n ) {
|
||||
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 ) {
|
||||
|
@ -379,13 +379,25 @@ static Node *scan_mem_chain(Node *mem, int alias_idx, int offset, Node *start_me
|
||||
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 @@ Node *PhaseMacroExpand::value_from_mem_phi(Node *mem, BasicType ft, const Type *
|
||||
}
|
||||
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);
|
||||
|
@ -936,9 +936,13 @@ static void match_alias_type(Compile* C, Node* n, Node* m) {
|
||||
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 @@ void Matcher::find_shared( Node *n ) {
|
||||
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 @@ void Matcher::find_shared( Node *n ) {
|
||||
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 @@ void Matcher::find_shared( Node *n ) {
|
||||
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));
|
||||
|
@ -598,7 +598,7 @@ PhaseStringOpts::PhaseStringOpts(PhaseGVN* gvn, Unique_Node_List*):
|
||||
}
|
||||
|
||||
// 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::fetch_static_field(GraphKit& kit, ciField* field) {
|
||||
}
|
||||
|
||||
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 @@ Node* PhaseStringOpts::int_stringSize(GraphKit& kit, Node* arg) {
|
||||
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 @@ void PhaseStringOpts::int_getChars(GraphKit& kit, Node* arg, Node* char_array, N
|
||||
// if (i == 0) break;
|
||||
// }
|
||||
|
||||
{
|
||||
// Add loop predicate first.
|
||||
kit.add_predicate();
|
||||
// Add loop predicate first.
|
||||
kit.add_predicate();
|
||||
|
||||
RegionNode *head = new RegionNode(3);
|
||||
head->init_req(1, kit.control());
|
||||
kit.gvn().set_type(head, Type::CONTROL);
|
||||
Node *i_phi = new PhiNode(head, TypeInt::INT);
|
||||
i_phi->init_req(1, i);
|
||||
kit.gvn().set_type(i_phi, TypeInt::INT);
|
||||
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);
|
||||
RegionNode* head = new RegionNode(3);
|
||||
head->init_req(1, kit.control());
|
||||
|
||||
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'));
|
||||
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* 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);
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
iff = kit.create_and_map_if(head, __ Bool(__ CmpI(q, __ intcon(0)), BoolTest::ne),
|
||||
PROB_FAIR, COUNT_UNKNOWN);
|
||||
Node* ne = __ IfTrue(iff);
|
||||
Node* eq = __ IfFalse(iff);
|
||||
|
||||
head->init_req(2, ne);
|
||||
mem->init_req(2, st);
|
||||
i_phi->init_req(2, q);
|
||||
charPos->init_req(2, m1);
|
||||
head->init_req(2, ne);
|
||||
mem->init_req(2, st);
|
||||
|
||||
charPos = m1;
|
||||
i_phi->init_req(2, q);
|
||||
charPos->init_req(2, index);
|
||||
charPos = index;
|
||||
|
||||
kit.set_control(eq);
|
||||
kit.set_memory(st, char_adr_idx);
|
||||
kit.set_control(eq);
|
||||
kit.set_memory(st, byte_adr_idx);
|
||||
|
||||
C->record_for_igvn(head);
|
||||
C->record_for_igvn(mem);
|
||||
C->record_for_igvn(i_phi);
|
||||
C->record_for_igvn(charPos);
|
||||
}
|
||||
C->record_for_igvn(head);
|
||||
C->record_for_igvn(mem);
|
||||
C->record_for_igvn(i_phi);
|
||||
C->record_for_igvn(charPos);
|
||||
|
||||
{
|
||||
// if (sign != 0) {
|
||||
// buf [--charPos] = sign;
|
||||
// }
|
||||
IfNode* iff = kit.create_and_map_if(kit.control(),
|
||||
__ Bool(__ CmpI(sign, __ intcon(0)), BoolTest::ne),
|
||||
PROB_FAIR, COUNT_UNKNOWN);
|
||||
// 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(2, __ IfFalse(iff));
|
||||
final_mem->init_req(2, kit.memory(char_adr_idx));
|
||||
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;
|
||||
|
||||
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);
|
||||
|
||||
// copy the contents
|
||||
if (offset->is_Con() && count->is_Con() && value->is_Con() && count->get_int() < unroll_string_copy_length) {
|
||||
// For small constant strings just emit individual stores.
|
||||
// A length of 6 seems like a good space/speed tradeof.
|
||||
int c = count->get_int();
|
||||
int o = offset->get_int();
|
||||
const TypeOopPtr* t = kit.gvn().type(value)->isa_oopptr();
|
||||
ciTypeArray* value_array = t->const_oop()->as_type_array();
|
||||
for (int e = 0; e < c; e++) {
|
||||
__ store_to_memory(kit.control(), kit.array_element_address(char_array, start, T_CHAR),
|
||||
__ intcon(value_array->char_at(o + e)), T_CHAR, char_adr_idx,
|
||||
MemNode::unordered);
|
||||
start = __ AddI(start, __ intcon(1));
|
||||
__ set(count, __ ConI(src_array_type->length()));
|
||||
int src_len = src_array_type->length() / (src_is_byte ? 1 : 2);
|
||||
if (src_len < unroll_string_copy_length) {
|
||||
// Small constant string
|
||||
copy_constant_string(kit, ideal, src_array_type, count, src_is_byte, dst_array, dst_coder, start);
|
||||
} else if (src_is_byte) {
|
||||
// Source is Latin1
|
||||
copy_latin1_string(kit, ideal, src_array, count, dst_array, dst_coder, start);
|
||||
} else {
|
||||
// Source is UTF16 (destination too). Simply emit a char arraycopy.
|
||||
arraycopy(kit, ideal, src_array, dst_array, T_CHAR, start, __ value(count));
|
||||
}
|
||||
} else {
|
||||
Node* src_ptr = kit.array_element_address(value, offset, T_CHAR);
|
||||
Node* dst_ptr = kit.array_element_address(char_array, start, T_CHAR);
|
||||
Node* c = count;
|
||||
Node* extra = NULL;
|
||||
#ifdef _LP64
|
||||
c = __ ConvI2L(c);
|
||||
extra = C->top();
|
||||
#endif
|
||||
Node* call = kit.make_runtime_call(GraphKit::RC_LEAF|GraphKit::RC_NO_FP,
|
||||
OptoRuntime::fast_arraycopy_Type(),
|
||||
CAST_FROM_FN_PTR(address, StubRoutines::jshort_disjoint_arraycopy()),
|
||||
"jshort_disjoint_arraycopy", TypeAryPtr::CHARS,
|
||||
src_ptr, dst_ptr, c, extra);
|
||||
start = __ AddI(start, count);
|
||||
Node* size = kit.load_array_length(src_array);
|
||||
__ set(count, size);
|
||||
// Non-constant source string
|
||||
if (CompactStrings) {
|
||||
// Emit runtime check for coder
|
||||
Node* coder = kit.load_String_coder(__ ctrl(), str);
|
||||
__ if_then(coder, BoolTest::eq, __ ConI(java_lang_String::CODER_LATIN1)); {
|
||||
// Source is Latin1
|
||||
copy_latin1_string(kit, ideal, src_array, count, dst_array, dst_coder, start);
|
||||
} __ else_();
|
||||
}
|
||||
// Source is UTF16 (destination too). Simply emit a char arraycopy.
|
||||
arraycopy(kit, ideal, src_array, dst_array, T_CHAR, start, __ value(count));
|
||||
|
||||
if (CompactStrings) {
|
||||
__ end_if();
|
||||
}
|
||||
}
|
||||
return start;
|
||||
|
||||
// Finally sync IdealKit and GraphKit.
|
||||
kit.sync_kit(ideal);
|
||||
return __ AddI(start, __ value(count));
|
||||
}
|
||||
|
||||
// Compress copy the char into dst_array at index start.
|
||||
Node* PhaseStringOpts::copy_char(GraphKit& kit, Node* val, Node* dst_array, Node* dst_coder, Node* start) {
|
||||
bool dcon = (dst_coder != NULL) && dst_coder->is_Con();
|
||||
bool dbyte = dcon ? (dst_coder->get_int() == java_lang_String::CODER_LATIN1) : false;
|
||||
|
||||
IdealKit ideal(&kit, true, true);
|
||||
IdealVariable end(ideal); __ declarations_done();
|
||||
Node* adr = kit.array_element_address(dst_array, start, T_BYTE);
|
||||
if (!dcon){
|
||||
__ if_then(dst_coder, BoolTest::eq, __ ConI(java_lang_String::CODER_LATIN1));
|
||||
}
|
||||
if (!dcon || dbyte) {
|
||||
// Destination is Latin1. Store a byte.
|
||||
__ store(__ ctrl(), adr, val, T_BYTE, byte_adr_idx, MemNode::unordered);
|
||||
__ set(end, __ AddI(start, __ ConI(1)));
|
||||
}
|
||||
if (!dcon) {
|
||||
__ else_();
|
||||
}
|
||||
if (!dcon || !dbyte) {
|
||||
// Destination is UTF16. Store a char.
|
||||
__ store(__ ctrl(), adr, val, T_CHAR, byte_adr_idx, MemNode::unordered);
|
||||
__ set(end, __ AddI(start, __ ConI(2)));
|
||||
}
|
||||
if (!dcon) {
|
||||
__ end_if();
|
||||
}
|
||||
// Finally sync IdealKit and GraphKit.
|
||||
kit.sync_kit(ideal);
|
||||
return __ value(end);
|
||||
}
|
||||
|
||||
#undef __
|
||||
#define __ kit.
|
||||
|
||||
// Allocate a byte array of specified length.
|
||||
Node* PhaseStringOpts::allocate_byte_array(GraphKit& kit, IdealKit* ideal, Node* length) {
|
||||
if (ideal != NULL) {
|
||||
// Sync IdealKit and graphKit.
|
||||
kit.sync_kit(*ideal);
|
||||
}
|
||||
Node* byte_array = NULL;
|
||||
{
|
||||
PreserveReexecuteState preexecs(&kit);
|
||||
// The original jvms is for an allocation of either a String or
|
||||
// StringBuffer so no stack adjustment is necessary for proper
|
||||
// reexecution. If we deoptimize in the slow path the bytecode
|
||||
// will be reexecuted and the char[] allocation will be thrown away.
|
||||
kit.jvms()->set_should_reexecute(true);
|
||||
byte_array = kit.new_array(__ makecon(TypeKlassPtr::make(ciTypeArrayKlass::make(T_BYTE))),
|
||||
length, 1);
|
||||
}
|
||||
|
||||
// Mark the allocation so that zeroing is skipped since the code
|
||||
// below will overwrite the entire array
|
||||
AllocateArrayNode* byte_alloc = AllocateArrayNode::Ideal_array_allocation(byte_array, _gvn);
|
||||
byte_alloc->maybe_set_complete(_gvn);
|
||||
|
||||
if (ideal != NULL) {
|
||||
// Sync IdealKit and graphKit.
|
||||
ideal->sync_kit(&kit);
|
||||
}
|
||||
return byte_array;
|
||||
}
|
||||
|
||||
jbyte PhaseStringOpts::get_constant_coder(GraphKit& kit, Node* str) {
|
||||
assert(str->is_Con(), "String must be constant");
|
||||
const TypeOopPtr* str_type = kit.gvn().type(str)->isa_oopptr();
|
||||
ciInstance* str_instance = str_type->const_oop()->as_instance();
|
||||
jbyte coder = str_instance->field_value_by_offset(java_lang_String::coder_offset_in_bytes()).as_byte();
|
||||
assert(CompactStrings || (coder == java_lang_String::CODER_UTF16), "Strings must be UTF16 encoded");
|
||||
return coder;
|
||||
}
|
||||
|
||||
int PhaseStringOpts::get_constant_length(GraphKit& kit, Node* str) {
|
||||
assert(str->is_Con(), "String must be constant");
|
||||
Node* src_array = kit.load_String_value(kit.control(), str);
|
||||
const TypeOopPtr* t = kit.gvn().type(src_array)->isa_oopptr();
|
||||
return t->const_oop()->as_type_array()->length();
|
||||
}
|
||||
|
||||
void PhaseStringOpts::replace_string_concat(StringConcat* sc) {
|
||||
// Log a little info about the transformation
|
||||
@ -1445,7 +1704,6 @@ void PhaseStringOpts::replace_string_concat(StringConcat* sc) {
|
||||
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 @@ void PhaseStringOpts::replace_string_concat(StringConcat* sc) {
|
||||
// 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 @@ void PhaseStringOpts::replace_string_concat(StringConcat* sc) {
|
||||
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 @@ void PhaseStringOpts::replace_string_concat(StringConcat* sc) {
|
||||
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 @@ void PhaseStringOpts::replace_string_concat(StringConcat* sc) {
|
||||
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 @@ void PhaseStringOpts::replace_string_concat(StringConcat* sc) {
|
||||
}
|
||||
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 @@ void PhaseStringOpts::replace_string_concat(StringConcat* sc) {
|
||||
|
||||
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 @@ void PhaseStringOpts::replace_string_concat(StringConcat* sc) {
|
||||
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();
|
||||
}
|
||||
|
@ -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 @@ class PhaseStringOpts : public Phase {
|
||||
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 @@ class PhaseStringOpts : public Phase {
|
||||
// Compute the number of characters required to represent the int value
|
||||
Node* int_stringSize(GraphKit& kit, Node* value);
|
||||
|
||||
// Copy the characters representing value into char_array starting at start
|
||||
void int_getChars(GraphKit& kit, Node* value, Node* char_array, Node* start, Node* end);
|
||||
// Simplified version of Integer.getChars
|
||||
void getChars(GraphKit& kit, Node* arg, Node* dst_array, BasicType bt, Node* end, Node* final_merge, Node* final_mem, int merge_index = 0);
|
||||
|
||||
// Copy of the contents of the String str into char_array starting at index start.
|
||||
Node* copy_string(GraphKit& kit, Node* str, Node* char_array, Node* start);
|
||||
// Copy the characters representing arg into dst_array starting at start
|
||||
Node* int_getChars(GraphKit& kit, Node* arg, Node* dst_array, Node* dst_coder, Node* start, Node* size);
|
||||
|
||||
// Copy contents of the String str into dst_array starting at index start.
|
||||
Node* copy_string(GraphKit& kit, Node* str, Node* dst_array, Node* dst_coder, Node* start);
|
||||
|
||||
// Copy 'count' bytes/chars from src_array to dst_array starting at index start
|
||||
void arraycopy(GraphKit& kit, IdealKit& ideal, Node* src_array, Node* dst_array, BasicType elembt, Node* start, Node* count);
|
||||
|
||||
// Copy contents of constant src_array to dst_array by emitting individual stores
|
||||
void copy_constant_string(GraphKit& kit, IdealKit& ideal, ciTypeArray* src_array, IdealVariable& count,
|
||||
bool src_is_byte, Node* dst_array, Node* dst_coder, Node* start);
|
||||
|
||||
// Copy contents of a Latin1 encoded string from src_array to dst_array
|
||||
void copy_latin1_string(GraphKit& kit, IdealKit& ideal, Node* src_array, IdealVariable& count,
|
||||
Node* dst_array, Node* dst_coder, Node* start);
|
||||
|
||||
// Copy the char into dst_array at index start.
|
||||
Node* copy_char(GraphKit& kit, Node* val, Node* dst_array, Node* dst_coder, Node* start);
|
||||
|
||||
// Allocate a byte array of specified length.
|
||||
Node* allocate_byte_array(GraphKit& kit, IdealKit* ideal, Node* length);
|
||||
|
||||
// Returns the coder of a constant string
|
||||
jbyte get_constant_coder(GraphKit& kit, Node* str);
|
||||
|
||||
// Returns the length of a constant string
|
||||
int get_constant_length(GraphKit& kit, Node* str);
|
||||
|
||||
// Clean up any leftover nodes
|
||||
void record_dead_node(Node* node);
|
||||
|
@ -2474,12 +2474,18 @@ JNI_QUICK_ENTRY(const jchar*, jni_GetStringChars(
|
||||
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 @@ JNI_ENTRY(void, jni_GetStringRegion(JNIEnv *env, jstring string, jsize start, js
|
||||
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 @@ JNI_ENTRY(const jchar*, jni_GetStringCritical(JNIEnv *env, jstring string, jbool
|
||||
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_END
|
||||
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
|
||||
|
@ -966,7 +966,7 @@ JvmtiEnv::GetThreadInfo(jthread thread, jvmtiThreadInfo* info_ptr) {
|
||||
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 @@ JvmtiEnv::GetThreadGroupInfo(jthreadGroup group, jvmtiThreadGroupInfo* info_ptr)
|
||||
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 @@ JvmtiEnv::GetThreadGroupInfo(jthreadGroup group, jvmtiThreadGroupInfo* info_ptr)
|
||||
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;
|
||||
}
|
||||
|
@ -1057,21 +1057,36 @@ static jint invoke_string_value_callback(jvmtiStringPrimitiveValueCallback cb,
|
||||
// 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
|
||||
|
@ -2889,6 +2889,9 @@ public:
|
||||
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 @@ public:
|
||||
"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 "\
|
||||
|
@ -2933,12 +2933,8 @@ const char* JavaThread::get_threadgroup_name() const {
|
||||
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 @@ const char* JavaThread::get_parent_name() const {
|
||||
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 @@ void Threads::initialize_java_lang_classes(JavaThread* main_thread, TRAPS) {
|
||||
|
||||
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.
|
||||
|
@ -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 @@ char* UTF8::next(const char* str, jchar* value) {
|
||||
} /* 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 @@ char* UTF8::next_character(const char* str, jint* value) {
|
||||
// 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 @@ int UTF8::unicode_length(const char* str, int len) {
|
||||
// 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 @@ static u_char* utf8_write(u_char* base, jchar ch) {
|
||||
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 @@ void UTF8::convert_to_unicode(const char* utf8_str, jchar* unicode_str, int unic
|
||||
/* 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 @@ void UTF8::convert_to_unicode(const char* utf8_str, jchar* unicode_str, int unic
|
||||
}
|
||||
}
|
||||
|
||||
// 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 @@ jint UTF8::get_supplementary_character(const unsigned char* str) {
|
||||
+ ((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 @@ int UNICODE::utf8_size(jchar c) {
|
||||
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 @@ int UNICODE::utf8_length(jchar* base, int length) {
|
||||
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 @@ char* UNICODE::as_utf8(jchar* base, int length) {
|
||||
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 @@ char* UNICODE::as_utf8(jchar* base, int length, char* buf, int buflen) {
|
||||
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 @@ void UNICODE::convert_to_utf8(const jchar* base, int length, char* utf8_buffer)
|
||||
}
|
||||
|
||||
// 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 @@ int UNICODE::quoted_ascii_length(jchar* base, int length) {
|
||||
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 @@ void UNICODE::as_quoted_ascii(const jchar* base, int length, char* buf, int bufl
|
||||
*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];
|
||||
|
@ -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 @@ class UTF8 : AllStatic {
|
||||
|
||||
// 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 UTF8 : AllStatic {
|
||||
|
||||
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 @@ class UNICODE : AllStatic {
|
||||
// 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
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
@ -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() { }
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user