Skip to content

Commit

Permalink
8141132: JEP 254: Compact Strings
Browse files Browse the repository at this point in the history
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
  • Loading branch information
9 people committed Nov 3, 2015
1 parent 4e24e2c commit 7af927f
Show file tree
Hide file tree
Showing 74 changed files with 4,817 additions and 1,662 deletions.
Expand Up @@ -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;
Expand Down Expand Up @@ -96,20 +95,30 @@ public static String charArrayToString(TypeArray charArray) {
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();
}
Expand Down Expand Up @@ -141,21 +150,14 @@ public static String escapeString(String s) {
}

public static String stringOopToString(Oop stringOop) {
if (offsetField == null) {
InstanceKlass k = (InstanceKlass) stringOop.getKlass();
offsetField = (IntField) k.findField("offset", "I"); // optional
countField = (IntField) k.findField("count", "I"); // optional
valueField = (OopField) k.findField("value", "[C");
if (Assert.ASSERTS_ENABLED) {
Assert.that(valueField != null, "Field \'value\' of java.lang.String not found");
}
}
if (offsetField != null && countField != null) {
return charArrayToString((TypeArray) valueField.getValue(stringOop),
offsetField.getValue(stringOop),
countField.getValue(stringOop));
InstanceKlass k = (InstanceKlass) stringOop.getKlass();
coderField = (ByteField) k.findField("coder", "B");
valueField = (OopField) k.findField("value", "[B");
if (Assert.ASSERTS_ENABLED) {
Assert.that(coderField != null, "Field \'coder\' of java.lang.String not found");
Assert.that(valueField != null, "Field \'value\' of java.lang.String not found");
}
return charArrayToString((TypeArray) valueField.getValue(stringOop));
return byteArrayToString((TypeArray) valueField.getValue(stringOop), coderField.getValue(stringOop));
}

public static String stringOopToEscapedString(Oop stringOop) {
Expand Down
Expand Up @@ -268,8 +268,8 @@ class StringStat implements StringTable.StringVisitor {
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) {
Expand Down
Expand Up @@ -61,9 +61,8 @@ protected static long hashSymbol(byte[] buf) {
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;
Expand Down
5 changes: 5 additions & 0 deletions hotspot/src/cpu/aarch64/vm/aarch64.ad
Expand Up @@ -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);

Expand All @@ -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);
Expand All @@ -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);
Expand All @@ -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);

Expand All @@ -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);

Expand Down
9 changes: 0 additions & 9 deletions hotspot/src/cpu/aarch64/vm/c1_LIRAssembler_aarch64.cpp
Expand Up @@ -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();
Expand Down
3 changes: 3 additions & 0 deletions hotspot/src/cpu/aarch64/vm/globals_aarch64.hpp
Expand Up @@ -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);

Expand Down
3 changes: 3 additions & 0 deletions hotspot/src/cpu/ppc/vm/globals_ppc.hpp
Expand Up @@ -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) \
\
Expand Down
19 changes: 10 additions & 9 deletions hotspot/src/cpu/ppc/vm/ppc.ad
Expand Up @@ -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.
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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.
Expand All @@ -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.
Expand All @@ -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.
Expand All @@ -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);
Expand Down
7 changes: 7 additions & 0 deletions hotspot/src/cpu/sparc/vm/assembler_sparc.hpp
Expand Up @@ -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,
Expand Down Expand Up @@ -194,6 +196,7 @@ class Assembler : public AbstractAssembler {
fnegd_opf = 0x06,

alignaddr_opf = 0x18,
bmask_opf = 0x19,

fadds_opf = 0x41,
faddd_opf = 0x42,
Expand All @@ -204,6 +207,7 @@ class Assembler : public AbstractAssembler {

fmuls_opf = 0x49,
fmuld_opf = 0x4a,
bshuffle_opf = 0x4c,
fdivs_opf = 0x4d,
fdivd_opf = 0x4e,

Expand Down Expand Up @@ -1226,6 +1230,9 @@ class Assembler : public AbstractAssembler {

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)); }
Expand Down

0 comments on commit 7af927f

Please sign in to comment.