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:
Tobias Hartmann 2015-11-03 09:41:03 +01:00
parent 4e24e2cc6c
commit 7af927f9c1
74 changed files with 4838 additions and 1683 deletions

View File

@ -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) {

View File

@ -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) {

View File

@ -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;

View File

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

View File

@ -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();

View File

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

View File

@ -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) \
\

View File

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

View File

@ -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)); }

View File

@ -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) {

View File

@ -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, \

View File

@ -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");

View File

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

View File

@ -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);
%}

View File

@ -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;

View File

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

View File

@ -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()) {

View File

@ -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

View File

@ -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
};

View File

@ -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 );
%}

View File

@ -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 );
%}

View File

@ -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, \

View File

@ -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;
}

View File

@ -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");

View File

@ -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:

View File

@ -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();

View File

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

View File

@ -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

View File

@ -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);
}

View File

@ -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

View File

@ -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

View File

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

View File

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

View File

@ -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;

View File

@ -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);
}

View File

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

View File

@ -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;

View File

@ -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") \

View File

@ -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();

View File

@ -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,

View File

@ -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;
}

View File

@ -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

View File

@ -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].

View File

@ -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();

View File

@ -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];
}

View File

@ -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:

View File

@ -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)

View File

@ -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");

View File

@ -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,

View File

@ -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) {

View File

@ -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) {

View File

@ -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,

View File

@ -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

View File

@ -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();
}

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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 ) {

View File

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

View File

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

View File

@ -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();
}

View File

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

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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 "\

View File

@ -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.

View File

@ -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];

View File

@ -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

View File

@ -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();
}
}

View File

@ -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() { }
}

View File

@ -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);
}
}