mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-27 14:54:52 +02:00
6942326: x86 code in string_indexof() could read beyond reserved heap space
Copy small (<8) strings on stack if str+16 crosses a page boundary and load from stack into XMM. Back up pointer when loading string's tail. Reviewed-by: never
This commit is contained in:
parent
836fd81744
commit
a74bc73598
7 changed files with 953 additions and 112 deletions
|
@ -1601,6 +1601,17 @@ void Assembler::movdl(Register dst, XMMRegister src) {
|
||||||
emit_byte(0xC0 | encode);
|
emit_byte(0xC0 | encode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Assembler::movdl(XMMRegister dst, Address src) {
|
||||||
|
NOT_LP64(assert(VM_Version::supports_sse2(), ""));
|
||||||
|
InstructionMark im(this);
|
||||||
|
emit_byte(0x66);
|
||||||
|
prefix(src, dst);
|
||||||
|
emit_byte(0x0F);
|
||||||
|
emit_byte(0x6E);
|
||||||
|
emit_operand(dst, src);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void Assembler::movdqa(XMMRegister dst, Address src) {
|
void Assembler::movdqa(XMMRegister dst, Address src) {
|
||||||
NOT_LP64(assert(VM_Version::supports_sse2(), ""));
|
NOT_LP64(assert(VM_Version::supports_sse2(), ""));
|
||||||
InstructionMark im(this);
|
InstructionMark im(this);
|
||||||
|
@ -2412,7 +2423,10 @@ void Assembler::pshuflw(XMMRegister dst, Address src, int mode) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Assembler::psrlq(XMMRegister dst, int shift) {
|
void Assembler::psrlq(XMMRegister dst, int shift) {
|
||||||
// HMM Table D-1 says sse2 or mmx
|
// Shift 64 bit value logically right by specified number of bits.
|
||||||
|
// HMM Table D-1 says sse2 or mmx.
|
||||||
|
// Do not confuse it with psrldq SSE2 instruction which
|
||||||
|
// shifts 128 bit value in xmm register by number of bytes.
|
||||||
NOT_LP64(assert(VM_Version::supports_sse(), ""));
|
NOT_LP64(assert(VM_Version::supports_sse(), ""));
|
||||||
|
|
||||||
int encode = prefixq_and_encode(xmm2->encoding(), dst->encoding());
|
int encode = prefixq_and_encode(xmm2->encoding(), dst->encoding());
|
||||||
|
@ -2423,6 +2437,18 @@ void Assembler::psrlq(XMMRegister dst, int shift) {
|
||||||
emit_byte(shift);
|
emit_byte(shift);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Assembler::psrldq(XMMRegister dst, int shift) {
|
||||||
|
// Shift 128 bit value in xmm register by number of bytes.
|
||||||
|
NOT_LP64(assert(VM_Version::supports_sse2(), ""));
|
||||||
|
|
||||||
|
int encode = prefixq_and_encode(xmm3->encoding(), dst->encoding());
|
||||||
|
emit_byte(0x66);
|
||||||
|
emit_byte(0x0F);
|
||||||
|
emit_byte(0x73);
|
||||||
|
emit_byte(0xC0 | encode);
|
||||||
|
emit_byte(shift);
|
||||||
|
}
|
||||||
|
|
||||||
void Assembler::ptest(XMMRegister dst, Address src) {
|
void Assembler::ptest(XMMRegister dst, Address src) {
|
||||||
assert(VM_Version::supports_sse4_1(), "");
|
assert(VM_Version::supports_sse4_1(), "");
|
||||||
|
|
||||||
|
@ -8567,101 +8593,418 @@ void MacroAssembler::reinit_heapbase() {
|
||||||
}
|
}
|
||||||
#endif // _LP64
|
#endif // _LP64
|
||||||
|
|
||||||
// IndexOf substring.
|
// IndexOf for constant substrings with size >= 8 chars
|
||||||
void MacroAssembler::string_indexof(Register str1, Register str2,
|
// which don't need to be loaded through stack.
|
||||||
Register cnt1, Register cnt2, Register result,
|
void MacroAssembler::string_indexofC8(Register str1, Register str2,
|
||||||
XMMRegister vec, Register tmp) {
|
Register cnt1, Register cnt2,
|
||||||
|
int int_cnt2, Register result,
|
||||||
|
XMMRegister vec, Register tmp) {
|
||||||
assert(UseSSE42Intrinsics, "SSE4.2 is required");
|
assert(UseSSE42Intrinsics, "SSE4.2 is required");
|
||||||
|
|
||||||
Label RELOAD_SUBSTR, PREP_FOR_SCAN, SCAN_TO_SUBSTR,
|
// This method uses pcmpestri inxtruction with bound registers
|
||||||
SCAN_SUBSTR, RET_NOT_FOUND, CLEANUP;
|
|
||||||
|
|
||||||
push(str1); // string addr
|
|
||||||
push(str2); // substr addr
|
|
||||||
push(cnt2); // substr count
|
|
||||||
jmpb(PREP_FOR_SCAN);
|
|
||||||
|
|
||||||
// Substr count saved at sp
|
|
||||||
// Substr saved at sp+1*wordSize
|
|
||||||
// String saved at sp+2*wordSize
|
|
||||||
|
|
||||||
// Reload substr for rescan
|
|
||||||
bind(RELOAD_SUBSTR);
|
|
||||||
movl(cnt2, Address(rsp, 0));
|
|
||||||
movptr(str2, Address(rsp, wordSize));
|
|
||||||
// We came here after the beginninig of the substring was
|
|
||||||
// matched but the rest of it was not so we need to search
|
|
||||||
// again. Start from the next element after the previous match.
|
|
||||||
subptr(str1, result); // Restore counter
|
|
||||||
shrl(str1, 1);
|
|
||||||
addl(cnt1, str1);
|
|
||||||
decrementl(cnt1);
|
|
||||||
lea(str1, Address(result, 2)); // Reload string
|
|
||||||
|
|
||||||
// Load substr
|
|
||||||
bind(PREP_FOR_SCAN);
|
|
||||||
movdqu(vec, Address(str2, 0));
|
|
||||||
addl(cnt1, 8); // prime the loop
|
|
||||||
subptr(str1, 16);
|
|
||||||
|
|
||||||
// Scan string for substr in 16-byte vectors
|
|
||||||
bind(SCAN_TO_SUBSTR);
|
|
||||||
subl(cnt1, 8);
|
|
||||||
addptr(str1, 16);
|
|
||||||
|
|
||||||
// pcmpestri
|
|
||||||
// inputs:
|
// inputs:
|
||||||
// xmm - substring
|
// xmm - substring
|
||||||
// rax - substring length (elements count)
|
// rax - substring length (elements count)
|
||||||
// mem - scaned string
|
// mem - scanned string
|
||||||
// rdx - string length (elements count)
|
// rdx - string length (elements count)
|
||||||
// 0xd - mode: 1100 (substring search) + 01 (unsigned shorts)
|
// 0xd - mode: 1100 (substring search) + 01 (unsigned shorts)
|
||||||
// outputs:
|
// outputs:
|
||||||
// rcx - matched index in string
|
// rcx - matched index in string
|
||||||
assert(cnt1 == rdx && cnt2 == rax && tmp == rcx, "pcmpestri");
|
assert(cnt1 == rdx && cnt2 == rax && tmp == rcx, "pcmpestri");
|
||||||
|
|
||||||
pcmpestri(vec, Address(str1, 0), 0x0d);
|
Label RELOAD_SUBSTR, SCAN_TO_SUBSTR, SCAN_SUBSTR,
|
||||||
jcc(Assembler::above, SCAN_TO_SUBSTR); // CF == 0 && ZF == 0
|
RET_FOUND, RET_NOT_FOUND, EXIT, FOUND_SUBSTR,
|
||||||
jccb(Assembler::aboveEqual, RET_NOT_FOUND); // CF == 0
|
MATCH_SUBSTR_HEAD, RELOAD_STR, FOUND_CANDIDATE;
|
||||||
|
|
||||||
// Fallthrough: found a potential substr
|
// Note, inline_string_indexOf() generates checks:
|
||||||
|
// if (substr.count > string.count) return -1;
|
||||||
|
// if (substr.count == 0) return 0;
|
||||||
|
assert(int_cnt2 >= 8, "this code isused only for cnt2 >= 8 chars");
|
||||||
|
|
||||||
|
// Load substring.
|
||||||
|
movdqu(vec, Address(str2, 0));
|
||||||
|
movl(cnt2, int_cnt2);
|
||||||
|
movptr(result, str1); // string addr
|
||||||
|
|
||||||
|
if (int_cnt2 > 8) {
|
||||||
|
jmpb(SCAN_TO_SUBSTR);
|
||||||
|
|
||||||
|
// Reload substr for rescan, this code
|
||||||
|
// is executed only for large substrings (> 8 chars)
|
||||||
|
bind(RELOAD_SUBSTR);
|
||||||
|
movdqu(vec, Address(str2, 0));
|
||||||
|
negptr(cnt2); // Jumped here with negative cnt2, convert to positive
|
||||||
|
|
||||||
|
bind(RELOAD_STR);
|
||||||
|
// We came here after the beginning of the substring was
|
||||||
|
// matched but the rest of it was not so we need to search
|
||||||
|
// again. Start from the next element after the previous match.
|
||||||
|
|
||||||
|
// cnt2 is number of substring reminding elements and
|
||||||
|
// cnt1 is number of string reminding elements when cmp failed.
|
||||||
|
// Restored cnt1 = cnt1 - cnt2 + int_cnt2
|
||||||
|
subl(cnt1, cnt2);
|
||||||
|
addl(cnt1, int_cnt2);
|
||||||
|
movl(cnt2, int_cnt2); // Now restore cnt2
|
||||||
|
|
||||||
|
decrementl(cnt1); // Shift to next element
|
||||||
|
cmpl(cnt1, cnt2);
|
||||||
|
jccb(Assembler::negative, RET_NOT_FOUND); // Left less then substring
|
||||||
|
|
||||||
|
addptr(result, 2);
|
||||||
|
|
||||||
|
} // (int_cnt2 > 8)
|
||||||
|
|
||||||
|
// Scan string for start of substr in 16-byte vectors
|
||||||
|
bind(SCAN_TO_SUBSTR);
|
||||||
|
pcmpestri(vec, Address(result, 0), 0x0d);
|
||||||
|
jccb(Assembler::below, FOUND_CANDIDATE); // CF == 1
|
||||||
|
subl(cnt1, 8);
|
||||||
|
jccb(Assembler::lessEqual, RET_NOT_FOUND); // Scanned full string
|
||||||
|
cmpl(cnt1, cnt2);
|
||||||
|
jccb(Assembler::negative, RET_NOT_FOUND); // Left less then substring
|
||||||
|
addptr(result, 16);
|
||||||
|
jmpb(SCAN_TO_SUBSTR);
|
||||||
|
|
||||||
|
// Found a potential substr
|
||||||
|
bind(FOUND_CANDIDATE);
|
||||||
|
// Matched whole vector if first element matched (tmp(rcx) == 0).
|
||||||
|
if (int_cnt2 == 8) {
|
||||||
|
jccb(Assembler::overflow, RET_FOUND); // OF == 1
|
||||||
|
} else { // int_cnt2 > 8
|
||||||
|
jccb(Assembler::overflow, FOUND_SUBSTR);
|
||||||
|
}
|
||||||
|
// After pcmpestri tmp(rcx) contains matched element index
|
||||||
|
// Compute start addr of substr
|
||||||
|
lea(result, Address(result, tmp, Address::times_2));
|
||||||
|
|
||||||
// Make sure string is still long enough
|
// Make sure string is still long enough
|
||||||
subl(cnt1, tmp);
|
subl(cnt1, tmp);
|
||||||
cmpl(cnt1, cnt2);
|
cmpl(cnt1, cnt2);
|
||||||
jccb(Assembler::negative, RET_NOT_FOUND);
|
if (int_cnt2 == 8) {
|
||||||
// Compute start addr of substr
|
jccb(Assembler::greaterEqual, SCAN_TO_SUBSTR);
|
||||||
lea(str1, Address(str1, tmp, Address::times_2));
|
} else { // int_cnt2 > 8
|
||||||
movptr(result, str1); // save
|
jccb(Assembler::greaterEqual, MATCH_SUBSTR_HEAD);
|
||||||
|
}
|
||||||
// Compare potential substr
|
// Left less then substring.
|
||||||
addl(cnt1, 8); // prime the loop
|
|
||||||
addl(cnt2, 8);
|
|
||||||
subptr(str1, 16);
|
|
||||||
subptr(str2, 16);
|
|
||||||
|
|
||||||
// Scan 16-byte vectors of string and substr
|
|
||||||
bind(SCAN_SUBSTR);
|
|
||||||
subl(cnt1, 8);
|
|
||||||
subl(cnt2, 8);
|
|
||||||
addptr(str1, 16);
|
|
||||||
addptr(str2, 16);
|
|
||||||
movdqu(vec, Address(str2, 0));
|
|
||||||
pcmpestri(vec, Address(str1, 0), 0x0d);
|
|
||||||
jcc(Assembler::noOverflow, RELOAD_SUBSTR); // OF == 0
|
|
||||||
jcc(Assembler::positive, SCAN_SUBSTR); // SF == 0
|
|
||||||
|
|
||||||
// Compute substr offset
|
|
||||||
subptr(result, Address(rsp, 2*wordSize));
|
|
||||||
shrl(result, 1); // index
|
|
||||||
jmpb(CLEANUP);
|
|
||||||
|
|
||||||
bind(RET_NOT_FOUND);
|
bind(RET_NOT_FOUND);
|
||||||
movl(result, -1);
|
movl(result, -1);
|
||||||
|
jmpb(EXIT);
|
||||||
|
|
||||||
|
if (int_cnt2 > 8) {
|
||||||
|
// This code is optimized for the case when whole substring
|
||||||
|
// is matched if its head is matched.
|
||||||
|
bind(MATCH_SUBSTR_HEAD);
|
||||||
|
pcmpestri(vec, Address(result, 0), 0x0d);
|
||||||
|
// Reload only string if does not match
|
||||||
|
jccb(Assembler::noOverflow, RELOAD_STR); // OF == 0
|
||||||
|
|
||||||
|
Label CONT_SCAN_SUBSTR;
|
||||||
|
// Compare the rest of substring (> 8 chars).
|
||||||
|
bind(FOUND_SUBSTR);
|
||||||
|
// First 8 chars are already matched.
|
||||||
|
negptr(cnt2);
|
||||||
|
addptr(cnt2, 8);
|
||||||
|
|
||||||
|
bind(SCAN_SUBSTR);
|
||||||
|
subl(cnt1, 8);
|
||||||
|
cmpl(cnt2, -8); // Do not read beyond substring
|
||||||
|
jccb(Assembler::lessEqual, CONT_SCAN_SUBSTR);
|
||||||
|
// Back-up strings to avoid reading beyond substring:
|
||||||
|
// cnt1 = cnt1 - cnt2 + 8
|
||||||
|
addl(cnt1, cnt2); // cnt2 is negative
|
||||||
|
addl(cnt1, 8);
|
||||||
|
movl(cnt2, 8); negptr(cnt2);
|
||||||
|
bind(CONT_SCAN_SUBSTR);
|
||||||
|
if (int_cnt2 < (int)G) {
|
||||||
|
movdqu(vec, Address(str2, cnt2, Address::times_2, int_cnt2*2));
|
||||||
|
pcmpestri(vec, Address(result, cnt2, Address::times_2, int_cnt2*2), 0x0d);
|
||||||
|
} else {
|
||||||
|
// calculate index in register to avoid integer overflow (int_cnt2*2)
|
||||||
|
movl(tmp, int_cnt2);
|
||||||
|
addptr(tmp, cnt2);
|
||||||
|
movdqu(vec, Address(str2, tmp, Address::times_2, 0));
|
||||||
|
pcmpestri(vec, Address(result, tmp, Address::times_2, 0), 0x0d);
|
||||||
|
}
|
||||||
|
// Need to reload strings pointers if not matched whole vector
|
||||||
|
jccb(Assembler::noOverflow, RELOAD_SUBSTR); // OF == 0
|
||||||
|
addptr(cnt2, 8);
|
||||||
|
jccb(Assembler::negative, SCAN_SUBSTR);
|
||||||
|
// Fall through if found full substring
|
||||||
|
|
||||||
|
} // (int_cnt2 > 8)
|
||||||
|
|
||||||
|
bind(RET_FOUND);
|
||||||
|
// Found result if we matched full small substring.
|
||||||
|
// Compute substr offset
|
||||||
|
subptr(result, str1);
|
||||||
|
shrl(result, 1); // index
|
||||||
|
bind(EXIT);
|
||||||
|
|
||||||
|
} // string_indexofC8
|
||||||
|
|
||||||
|
// Small strings are loaded through stack if they cross page boundary.
|
||||||
|
void MacroAssembler::string_indexof(Register str1, Register str2,
|
||||||
|
Register cnt1, Register cnt2,
|
||||||
|
int int_cnt2, Register result,
|
||||||
|
XMMRegister vec, Register tmp) {
|
||||||
|
assert(UseSSE42Intrinsics, "SSE4.2 is required");
|
||||||
|
//
|
||||||
|
// int_cnt2 is length of small (< 8 chars) constant substring
|
||||||
|
// or (-1) for non constant substring in which case its length
|
||||||
|
// is in cnt2 register.
|
||||||
|
//
|
||||||
|
// Note, inline_string_indexOf() generates checks:
|
||||||
|
// if (substr.count > string.count) return -1;
|
||||||
|
// if (substr.count == 0) return 0;
|
||||||
|
//
|
||||||
|
assert(int_cnt2 == -1 || (0 < int_cnt2 && int_cnt2 < 8), "should be != 0");
|
||||||
|
|
||||||
|
// This method uses pcmpestri inxtruction with bound registers
|
||||||
|
// inputs:
|
||||||
|
// xmm - substring
|
||||||
|
// rax - substring length (elements count)
|
||||||
|
// mem - scanned string
|
||||||
|
// rdx - string length (elements count)
|
||||||
|
// 0xd - mode: 1100 (substring search) + 01 (unsigned shorts)
|
||||||
|
// outputs:
|
||||||
|
// rcx - matched index in string
|
||||||
|
assert(cnt1 == rdx && cnt2 == rax && tmp == rcx, "pcmpestri");
|
||||||
|
|
||||||
|
Label RELOAD_SUBSTR, SCAN_TO_SUBSTR, SCAN_SUBSTR, ADJUST_STR,
|
||||||
|
RET_FOUND, RET_NOT_FOUND, CLEANUP, FOUND_SUBSTR,
|
||||||
|
FOUND_CANDIDATE;
|
||||||
|
|
||||||
|
{ //========================================================
|
||||||
|
// We don't know where these strings are located
|
||||||
|
// and we can't read beyond them. Load them through stack.
|
||||||
|
Label BIG_STRINGS, CHECK_STR, COPY_SUBSTR, COPY_STR;
|
||||||
|
|
||||||
|
movptr(tmp, rsp); // save old SP
|
||||||
|
|
||||||
|
if (int_cnt2 > 0) { // small (< 8 chars) constant substring
|
||||||
|
if (int_cnt2 == 1) { // One char
|
||||||
|
load_unsigned_short(result, Address(str2, 0));
|
||||||
|
movdl(vec, result); // move 32 bits
|
||||||
|
} else if (int_cnt2 == 2) { // Two chars
|
||||||
|
movdl(vec, Address(str2, 0)); // move 32 bits
|
||||||
|
} else if (int_cnt2 == 4) { // Four chars
|
||||||
|
movq(vec, Address(str2, 0)); // move 64 bits
|
||||||
|
} else { // cnt2 = { 3, 5, 6, 7 }
|
||||||
|
// Array header size is 12 bytes in 32-bit VM
|
||||||
|
// + 6 bytes for 3 chars == 18 bytes,
|
||||||
|
// enough space to load vec and shift.
|
||||||
|
assert(HeapWordSize*typeArrayKlass::header_size() >= 12,"sanity");
|
||||||
|
movdqu(vec, Address(str2, (int_cnt2*2)-16));
|
||||||
|
psrldq(vec, 16-(int_cnt2*2));
|
||||||
|
}
|
||||||
|
} else { // not constant substring
|
||||||
|
cmpl(cnt2, 8);
|
||||||
|
jccb(Assembler::aboveEqual, BIG_STRINGS); // Both strings are big enough
|
||||||
|
|
||||||
|
// We can read beyond string if srt+16 does not cross page boundary
|
||||||
|
// since heaps are aligned and mapped by pages.
|
||||||
|
assert(os::vm_page_size() < (int)G, "default page should be small");
|
||||||
|
movl(result, str2); // We need only low 32 bits
|
||||||
|
andl(result, (os::vm_page_size()-1));
|
||||||
|
cmpl(result, (os::vm_page_size()-16));
|
||||||
|
jccb(Assembler::belowEqual, CHECK_STR);
|
||||||
|
|
||||||
|
// Move small strings to stack to allow load 16 bytes into vec.
|
||||||
|
subptr(rsp, 16);
|
||||||
|
int stk_offset = wordSize-2;
|
||||||
|
push(cnt2);
|
||||||
|
|
||||||
|
bind(COPY_SUBSTR);
|
||||||
|
load_unsigned_short(result, Address(str2, cnt2, Address::times_2, -2));
|
||||||
|
movw(Address(rsp, cnt2, Address::times_2, stk_offset), result);
|
||||||
|
decrement(cnt2);
|
||||||
|
jccb(Assembler::notZero, COPY_SUBSTR);
|
||||||
|
|
||||||
|
pop(cnt2);
|
||||||
|
movptr(str2, rsp); // New substring address
|
||||||
|
} // non constant
|
||||||
|
|
||||||
|
bind(CHECK_STR);
|
||||||
|
cmpl(cnt1, 8);
|
||||||
|
jccb(Assembler::aboveEqual, BIG_STRINGS);
|
||||||
|
|
||||||
|
// Check cross page boundary.
|
||||||
|
movl(result, str1); // We need only low 32 bits
|
||||||
|
andl(result, (os::vm_page_size()-1));
|
||||||
|
cmpl(result, (os::vm_page_size()-16));
|
||||||
|
jccb(Assembler::belowEqual, BIG_STRINGS);
|
||||||
|
|
||||||
|
subptr(rsp, 16);
|
||||||
|
int stk_offset = -2;
|
||||||
|
if (int_cnt2 < 0) { // not constant
|
||||||
|
push(cnt2);
|
||||||
|
stk_offset += wordSize;
|
||||||
|
}
|
||||||
|
movl(cnt2, cnt1);
|
||||||
|
|
||||||
|
bind(COPY_STR);
|
||||||
|
load_unsigned_short(result, Address(str1, cnt2, Address::times_2, -2));
|
||||||
|
movw(Address(rsp, cnt2, Address::times_2, stk_offset), result);
|
||||||
|
decrement(cnt2);
|
||||||
|
jccb(Assembler::notZero, COPY_STR);
|
||||||
|
|
||||||
|
if (int_cnt2 < 0) { // not constant
|
||||||
|
pop(cnt2);
|
||||||
|
}
|
||||||
|
movptr(str1, rsp); // New string address
|
||||||
|
|
||||||
|
bind(BIG_STRINGS);
|
||||||
|
// Load substring.
|
||||||
|
if (int_cnt2 < 0) { // -1
|
||||||
|
movdqu(vec, Address(str2, 0));
|
||||||
|
push(cnt2); // substr count
|
||||||
|
push(str2); // substr addr
|
||||||
|
push(str1); // string addr
|
||||||
|
} else {
|
||||||
|
// Small (< 8 chars) constant substrings are loaded already.
|
||||||
|
movl(cnt2, int_cnt2);
|
||||||
|
}
|
||||||
|
push(tmp); // original SP
|
||||||
|
|
||||||
|
} // Finished loading
|
||||||
|
|
||||||
|
//========================================================
|
||||||
|
// Start search
|
||||||
|
//
|
||||||
|
|
||||||
|
movptr(result, str1); // string addr
|
||||||
|
|
||||||
|
if (int_cnt2 < 0) { // Only for non constant substring
|
||||||
|
jmpb(SCAN_TO_SUBSTR);
|
||||||
|
|
||||||
|
// SP saved at sp+0
|
||||||
|
// String saved at sp+1*wordSize
|
||||||
|
// Substr saved at sp+2*wordSize
|
||||||
|
// Substr count saved at sp+3*wordSize
|
||||||
|
|
||||||
|
// Reload substr for rescan, this code
|
||||||
|
// is executed only for large substrings (> 8 chars)
|
||||||
|
bind(RELOAD_SUBSTR);
|
||||||
|
movptr(str2, Address(rsp, 2*wordSize));
|
||||||
|
movl(cnt2, Address(rsp, 3*wordSize));
|
||||||
|
movdqu(vec, Address(str2, 0));
|
||||||
|
// We came here after the beginning of the substring was
|
||||||
|
// matched but the rest of it was not so we need to search
|
||||||
|
// again. Start from the next element after the previous match.
|
||||||
|
subptr(str1, result); // Restore counter
|
||||||
|
shrl(str1, 1);
|
||||||
|
addl(cnt1, str1);
|
||||||
|
decrementl(cnt1); // Shift to next element
|
||||||
|
cmpl(cnt1, cnt2);
|
||||||
|
jccb(Assembler::negative, RET_NOT_FOUND); // Left less then substring
|
||||||
|
|
||||||
|
addptr(result, 2);
|
||||||
|
} // non constant
|
||||||
|
|
||||||
|
// Scan string for start of substr in 16-byte vectors
|
||||||
|
bind(SCAN_TO_SUBSTR);
|
||||||
|
assert(cnt1 == rdx && cnt2 == rax && tmp == rcx, "pcmpestri");
|
||||||
|
pcmpestri(vec, Address(result, 0), 0x0d);
|
||||||
|
jccb(Assembler::below, FOUND_CANDIDATE); // CF == 1
|
||||||
|
subl(cnt1, 8);
|
||||||
|
jccb(Assembler::lessEqual, RET_NOT_FOUND); // Scanned full string
|
||||||
|
cmpl(cnt1, cnt2);
|
||||||
|
jccb(Assembler::negative, RET_NOT_FOUND); // Left less then substring
|
||||||
|
addptr(result, 16);
|
||||||
|
|
||||||
|
bind(ADJUST_STR);
|
||||||
|
cmpl(cnt1, 8); // Do not read beyond string
|
||||||
|
jccb(Assembler::greaterEqual, SCAN_TO_SUBSTR);
|
||||||
|
// Back-up string to avoid reading beyond string.
|
||||||
|
lea(result, Address(result, cnt1, Address::times_2, -16));
|
||||||
|
movl(cnt1, 8);
|
||||||
|
jmpb(SCAN_TO_SUBSTR);
|
||||||
|
|
||||||
|
// Found a potential substr
|
||||||
|
bind(FOUND_CANDIDATE);
|
||||||
|
// After pcmpestri tmp(rcx) contains matched element index
|
||||||
|
|
||||||
|
// Make sure string is still long enough
|
||||||
|
subl(cnt1, tmp);
|
||||||
|
cmpl(cnt1, cnt2);
|
||||||
|
jccb(Assembler::greaterEqual, FOUND_SUBSTR);
|
||||||
|
// Left less then substring.
|
||||||
|
|
||||||
|
bind(RET_NOT_FOUND);
|
||||||
|
movl(result, -1);
|
||||||
|
jmpb(CLEANUP);
|
||||||
|
|
||||||
|
bind(FOUND_SUBSTR);
|
||||||
|
// Compute start addr of substr
|
||||||
|
lea(result, Address(result, tmp, Address::times_2));
|
||||||
|
|
||||||
|
if (int_cnt2 > 0) { // Constant substring
|
||||||
|
// Repeat search for small substring (< 8 chars)
|
||||||
|
// from new point without reloading substring.
|
||||||
|
// Have to check that we don't read beyond string.
|
||||||
|
cmpl(tmp, 8-int_cnt2);
|
||||||
|
jccb(Assembler::greater, ADJUST_STR);
|
||||||
|
// Fall through if matched whole substring.
|
||||||
|
} else { // non constant
|
||||||
|
assert(int_cnt2 == -1, "should be != 0");
|
||||||
|
|
||||||
|
addl(tmp, cnt2);
|
||||||
|
// Found result if we matched whole substring.
|
||||||
|
cmpl(tmp, 8);
|
||||||
|
jccb(Assembler::lessEqual, RET_FOUND);
|
||||||
|
|
||||||
|
// Repeat search for small substring (<= 8 chars)
|
||||||
|
// from new point 'str1' without reloading substring.
|
||||||
|
cmpl(cnt2, 8);
|
||||||
|
// Have to check that we don't read beyond string.
|
||||||
|
jccb(Assembler::lessEqual, ADJUST_STR);
|
||||||
|
|
||||||
|
Label CHECK_NEXT, CONT_SCAN_SUBSTR, RET_FOUND_LONG;
|
||||||
|
// Compare the rest of substring (> 8 chars).
|
||||||
|
movptr(str1, result);
|
||||||
|
|
||||||
|
cmpl(tmp, cnt2);
|
||||||
|
// First 8 chars are already matched.
|
||||||
|
jccb(Assembler::equal, CHECK_NEXT);
|
||||||
|
|
||||||
|
bind(SCAN_SUBSTR);
|
||||||
|
pcmpestri(vec, Address(str1, 0), 0x0d);
|
||||||
|
// Need to reload strings pointers if not matched whole vector
|
||||||
|
jcc(Assembler::noOverflow, RELOAD_SUBSTR); // OF == 0
|
||||||
|
|
||||||
|
bind(CHECK_NEXT);
|
||||||
|
subl(cnt2, 8);
|
||||||
|
jccb(Assembler::lessEqual, RET_FOUND_LONG); // Found full substring
|
||||||
|
addptr(str1, 16);
|
||||||
|
addptr(str2, 16);
|
||||||
|
subl(cnt1, 8);
|
||||||
|
cmpl(cnt2, 8); // Do not read beyond substring
|
||||||
|
jccb(Assembler::greaterEqual, CONT_SCAN_SUBSTR);
|
||||||
|
// Back-up strings to avoid reading beyond substring.
|
||||||
|
lea(str2, Address(str2, cnt2, Address::times_2, -16));
|
||||||
|
lea(str1, Address(str1, cnt2, Address::times_2, -16));
|
||||||
|
subl(cnt1, cnt2);
|
||||||
|
movl(cnt2, 8);
|
||||||
|
addl(cnt1, 8);
|
||||||
|
bind(CONT_SCAN_SUBSTR);
|
||||||
|
movdqu(vec, Address(str2, 0));
|
||||||
|
jmpb(SCAN_SUBSTR);
|
||||||
|
|
||||||
|
bind(RET_FOUND_LONG);
|
||||||
|
movptr(str1, Address(rsp, wordSize));
|
||||||
|
} // non constant
|
||||||
|
|
||||||
|
bind(RET_FOUND);
|
||||||
|
// Compute substr offset
|
||||||
|
subptr(result, str1);
|
||||||
|
shrl(result, 1); // index
|
||||||
|
|
||||||
bind(CLEANUP);
|
bind(CLEANUP);
|
||||||
addptr(rsp, 3*wordSize);
|
pop(rsp); // restore SP
|
||||||
}
|
|
||||||
|
} // string_indexof
|
||||||
|
|
||||||
// Compare strings.
|
// Compare strings.
|
||||||
void MacroAssembler::string_compare(Register str1, Register str2,
|
void MacroAssembler::string_compare(Register str1, Register str2,
|
||||||
|
|
|
@ -1121,6 +1121,7 @@ private:
|
||||||
|
|
||||||
void movdl(XMMRegister dst, Register src);
|
void movdl(XMMRegister dst, Register src);
|
||||||
void movdl(Register dst, XMMRegister src);
|
void movdl(Register dst, XMMRegister src);
|
||||||
|
void movdl(XMMRegister dst, Address src);
|
||||||
|
|
||||||
// Move Double Quadword
|
// Move Double Quadword
|
||||||
void movdq(XMMRegister dst, Register src);
|
void movdq(XMMRegister dst, Register src);
|
||||||
|
@ -1288,9 +1289,12 @@ private:
|
||||||
void pshuflw(XMMRegister dst, XMMRegister src, int mode);
|
void pshuflw(XMMRegister dst, XMMRegister src, int mode);
|
||||||
void pshuflw(XMMRegister dst, Address src, int mode);
|
void pshuflw(XMMRegister dst, Address src, int mode);
|
||||||
|
|
||||||
// Shift Right Logical Quadword Immediate
|
// Shift Right by bits Logical Quadword Immediate
|
||||||
void psrlq(XMMRegister dst, int shift);
|
void psrlq(XMMRegister dst, int shift);
|
||||||
|
|
||||||
|
// Shift Right by bytes Logical DoubleQuadword Immediate
|
||||||
|
void psrldq(XMMRegister dst, int shift);
|
||||||
|
|
||||||
// Logical Compare Double Quadword
|
// Logical Compare Double Quadword
|
||||||
void ptest(XMMRegister dst, XMMRegister src);
|
void ptest(XMMRegister dst, XMMRegister src);
|
||||||
void ptest(XMMRegister dst, Address src);
|
void ptest(XMMRegister dst, Address src);
|
||||||
|
@ -2290,10 +2294,22 @@ public:
|
||||||
void movl2ptr(Register dst, Register src) { LP64_ONLY(movslq(dst, src)) NOT_LP64(if (dst != src) movl(dst, src)); }
|
void movl2ptr(Register dst, Register src) { LP64_ONLY(movslq(dst, src)) NOT_LP64(if (dst != src) movl(dst, src)); }
|
||||||
|
|
||||||
// IndexOf strings.
|
// IndexOf strings.
|
||||||
|
// Small strings are loaded through stack if they cross page boundary.
|
||||||
void string_indexof(Register str1, Register str2,
|
void string_indexof(Register str1, Register str2,
|
||||||
Register cnt1, Register cnt2, Register result,
|
Register cnt1, Register cnt2,
|
||||||
|
int int_cnt2, Register result,
|
||||||
XMMRegister vec, Register tmp);
|
XMMRegister vec, Register tmp);
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
// Smallest code: we don't need to load through stack,
|
||||||
|
// check string tail.
|
||||||
|
|
||||||
// Compare strings.
|
// Compare strings.
|
||||||
void string_compare(Register str1, Register str2,
|
void string_compare(Register str1, Register str2,
|
||||||
Register cnt1, Register cnt2, Register result,
|
Register cnt1, Register cnt2, Register result,
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
//
|
//
|
||||||
// Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved.
|
// Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved.
|
||||||
// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
//
|
//
|
||||||
// This code is free software; you can redistribute it and/or modify it
|
// This code is free software; you can redistribute it and/or modify it
|
||||||
|
@ -12658,17 +12658,46 @@ instruct string_equals(eDIRegP str1, eSIRegP str2, eCXRegI cnt, eAXRegI result,
|
||||||
ins_pipe( pipe_slow );
|
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, regXD vec, eAXRegI cnt2, eCXRegI tmp, eFlagsReg cr) %{
|
||||||
|
predicate(UseSSE42Intrinsics);
|
||||||
|
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" %}
|
||||||
|
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);
|
||||||
|
} 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);
|
||||||
|
}
|
||||||
|
%}
|
||||||
|
ins_pipe( pipe_slow );
|
||||||
|
%}
|
||||||
|
|
||||||
instruct string_indexof(eDIRegP str1, eDXRegI cnt1, eSIRegP str2, eAXRegI cnt2,
|
instruct string_indexof(eDIRegP str1, eDXRegI cnt1, eSIRegP str2, eAXRegI cnt2,
|
||||||
eBXRegI result, regXD tmp1, eCXRegI tmp2, eFlagsReg cr) %{
|
eBXRegI result, regXD vec, eCXRegI tmp, eFlagsReg cr) %{
|
||||||
predicate(UseSSE42Intrinsics);
|
predicate(UseSSE42Intrinsics);
|
||||||
match(Set result (StrIndexOf (Binary str1 cnt1) (Binary str2 cnt2)));
|
match(Set result (StrIndexOf (Binary str1 cnt1) (Binary str2 cnt2)));
|
||||||
effect(TEMP tmp1, USE_KILL str1, USE_KILL str2, USE_KILL cnt1, USE_KILL cnt2, KILL tmp2, KILL cr);
|
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 $tmp2, $tmp1" %}
|
format %{ "String IndexOf $str1,$cnt1,$str2,$cnt2 -> $result // KILL all" %}
|
||||||
ins_encode %{
|
ins_encode %{
|
||||||
__ string_indexof($str1$$Register, $str2$$Register,
|
__ string_indexof($str1$$Register, $str2$$Register,
|
||||||
$cnt1$$Register, $cnt2$$Register, $result$$Register,
|
$cnt1$$Register, $cnt2$$Register,
|
||||||
$tmp1$$XMMRegister, $tmp2$$Register);
|
(-1), $result$$Register,
|
||||||
|
$vec$$XMMRegister, $tmp$$Register);
|
||||||
%}
|
%}
|
||||||
ins_pipe( pipe_slow );
|
ins_pipe( pipe_slow );
|
||||||
%}
|
%}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
//
|
//
|
||||||
// Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
|
// Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
|
||||||
// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
//
|
//
|
||||||
// This code is free software; you can redistribute it and/or modify it
|
// This code is free software; you can redistribute it and/or modify it
|
||||||
|
@ -11598,18 +11598,48 @@ instruct string_compare(rdi_RegP str1, rcx_RegI cnt1, rsi_RegP str2, rdx_RegI cn
|
||||||
ins_pipe( pipe_slow );
|
ins_pipe( pipe_slow );
|
||||||
%}
|
%}
|
||||||
|
|
||||||
|
// fast search of substring with known size.
|
||||||
|
instruct string_indexof_con(rdi_RegP str1, rdx_RegI cnt1, rsi_RegP str2, immI int_cnt2,
|
||||||
|
rbx_RegI result, regD vec, rax_RegI cnt2, rcx_RegI tmp, rFlagsReg cr)
|
||||||
|
%{
|
||||||
|
predicate(UseSSE42Intrinsics);
|
||||||
|
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" %}
|
||||||
|
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);
|
||||||
|
} 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);
|
||||||
|
}
|
||||||
|
%}
|
||||||
|
ins_pipe( pipe_slow );
|
||||||
|
%}
|
||||||
|
|
||||||
instruct string_indexof(rdi_RegP str1, rdx_RegI cnt1, rsi_RegP str2, rax_RegI cnt2,
|
instruct string_indexof(rdi_RegP str1, rdx_RegI cnt1, rsi_RegP str2, rax_RegI cnt2,
|
||||||
rbx_RegI result, regD tmp1, rcx_RegI tmp2, rFlagsReg cr)
|
rbx_RegI result, regD vec, rcx_RegI tmp, rFlagsReg cr)
|
||||||
%{
|
%{
|
||||||
predicate(UseSSE42Intrinsics);
|
predicate(UseSSE42Intrinsics);
|
||||||
match(Set result (StrIndexOf (Binary str1 cnt1) (Binary str2 cnt2)));
|
match(Set result (StrIndexOf (Binary str1 cnt1) (Binary str2 cnt2)));
|
||||||
effect(TEMP tmp1, USE_KILL str1, USE_KILL str2, USE_KILL cnt1, USE_KILL cnt2, KILL tmp2, KILL cr);
|
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 $tmp1, $tmp2" %}
|
format %{ "String IndexOf $str1,$cnt1,$str2,$cnt2 -> $result // KILL all" %}
|
||||||
ins_encode %{
|
ins_encode %{
|
||||||
__ string_indexof($str1$$Register, $str2$$Register,
|
__ string_indexof($str1$$Register, $str2$$Register,
|
||||||
$cnt1$$Register, $cnt2$$Register, $result$$Register,
|
$cnt1$$Register, $cnt2$$Register,
|
||||||
$tmp1$$XMMRegister, $tmp2$$Register);
|
(-1), $result$$Register,
|
||||||
|
$vec$$XMMRegister, $tmp$$Register);
|
||||||
%}
|
%}
|
||||||
ins_pipe( pipe_slow );
|
ins_pipe( pipe_slow );
|
||||||
%}
|
%}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
@ -1193,7 +1193,7 @@ bool LibraryCallKit::inline_string_indexOf() {
|
||||||
Node* result;
|
Node* result;
|
||||||
// Disable the use of pcmpestri until it can be guaranteed that
|
// Disable the use of pcmpestri until it can be guaranteed that
|
||||||
// the load doesn't cross into the uncommited space.
|
// the load doesn't cross into the uncommited space.
|
||||||
if (false && Matcher::has_match_rule(Op_StrIndexOf) &&
|
if (Matcher::has_match_rule(Op_StrIndexOf) &&
|
||||||
UseSSE42Intrinsics) {
|
UseSSE42Intrinsics) {
|
||||||
// Generate SSE4.2 version of indexOf
|
// Generate SSE4.2 version of indexOf
|
||||||
// We currently only have match rules that use SSE4.2
|
// We currently only have match rules that use SSE4.2
|
||||||
|
@ -1211,13 +1211,13 @@ bool LibraryCallKit::inline_string_indexOf() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make the merge point
|
ciInstanceKlass* str_klass = env()->String_klass();
|
||||||
RegionNode* result_rgn = new (C, 3) RegionNode(3);
|
const TypeOopPtr* string_type = TypeOopPtr::make_from_klass(str_klass);
|
||||||
Node* result_phi = new (C, 3) PhiNode(result_rgn, TypeInt::INT);
|
|
||||||
Node* no_ctrl = NULL;
|
|
||||||
|
|
||||||
ciInstanceKlass* klass = env()->String_klass();
|
// Make the merge point
|
||||||
const TypeOopPtr* string_type = TypeOopPtr::make_from_klass(klass);
|
RegionNode* result_rgn = new (C, 4) RegionNode(4);
|
||||||
|
Node* result_phi = new (C, 4) PhiNode(result_rgn, TypeInt::INT);
|
||||||
|
Node* no_ctrl = NULL;
|
||||||
|
|
||||||
// Get counts for string and substr
|
// Get counts for string and substr
|
||||||
Node* source_cnta = basic_plus_adr(receiver, receiver, count_offset);
|
Node* source_cnta = basic_plus_adr(receiver, receiver, count_offset);
|
||||||
|
@ -1235,6 +1235,17 @@ bool LibraryCallKit::inline_string_indexOf() {
|
||||||
result_rgn->init_req(2, if_gt);
|
result_rgn->init_req(2, if_gt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!stopped()) {
|
||||||
|
// Check for substr count == 0
|
||||||
|
cmp = _gvn.transform( new(C, 3) CmpINode(substr_cnt, intcon(0)) );
|
||||||
|
bol = _gvn.transform( new(C, 2) BoolNode(cmp, BoolTest::eq) );
|
||||||
|
Node* if_zero = generate_slow_guard(bol, NULL);
|
||||||
|
if (if_zero != NULL) {
|
||||||
|
result_phi->init_req(3, intcon(0));
|
||||||
|
result_rgn->init_req(3, if_zero);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!stopped()) {
|
if (!stopped()) {
|
||||||
result = make_string_method_node(Op_StrIndexOf, receiver, source_cnt, argument, substr_cnt);
|
result = make_string_method_node(Op_StrIndexOf, receiver, source_cnt, argument, substr_cnt);
|
||||||
result_phi->init_req(1, result);
|
result_phi->init_req(1, result);
|
||||||
|
@ -1244,8 +1255,8 @@ bool LibraryCallKit::inline_string_indexOf() {
|
||||||
record_for_igvn(result_rgn);
|
record_for_igvn(result_rgn);
|
||||||
result = _gvn.transform(result_phi);
|
result = _gvn.transform(result_phi);
|
||||||
|
|
||||||
} else { //Use LibraryCallKit::string_indexOf
|
} else { // Use LibraryCallKit::string_indexOf
|
||||||
// don't intrinsify is argument isn't a constant string.
|
// don't intrinsify if argument isn't a constant string.
|
||||||
if (!argument->is_Con()) {
|
if (!argument->is_Con()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -1281,7 +1292,7 @@ bool LibraryCallKit::inline_string_indexOf() {
|
||||||
// No null check on the argument is needed since it's a constant String oop.
|
// No null check on the argument is needed since it's a constant String oop.
|
||||||
_sp -= 2;
|
_sp -= 2;
|
||||||
if (stopped()) {
|
if (stopped()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The null string as a pattern always returns 0 (match at beginning of string)
|
// The null string as a pattern always returns 0 (match at beginning of string)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
@ -1559,21 +1559,24 @@ const Type *LoadNode::Value( PhaseTransform *phase ) const {
|
||||||
phase->C->has_unsafe_access(),
|
phase->C->has_unsafe_access(),
|
||||||
"Field accesses must be precise" );
|
"Field accesses must be precise" );
|
||||||
// For oop loads, we expect the _type to be precise
|
// For oop loads, we expect the _type to be precise
|
||||||
if (OptimizeStringConcat && klass == phase->C->env()->String_klass() &&
|
if (klass == phase->C->env()->String_klass() &&
|
||||||
adr->is_AddP() && off != Type::OffsetBot) {
|
adr->is_AddP() && off != Type::OffsetBot) {
|
||||||
// For constant Strings treat the fields as compile time constants.
|
// For constant Strings treat the final fields as compile time constants.
|
||||||
Node* base = adr->in(AddPNode::Base);
|
Node* base = adr->in(AddPNode::Base);
|
||||||
const TypeOopPtr* t = phase->type(base)->isa_oopptr();
|
const TypeOopPtr* t = phase->type(base)->isa_oopptr();
|
||||||
if (t != NULL && t->singleton()) {
|
if (t != NULL && t->singleton()) {
|
||||||
ciObject* string = t->const_oop();
|
ciField* field = phase->C->env()->String_klass()->get_field_by_offset(off, false);
|
||||||
ciConstant constant = string->as_instance()->field_value_by_offset(off);
|
if (field != NULL && field->is_final()) {
|
||||||
if (constant.basic_type() == T_INT) {
|
ciObject* string = t->const_oop();
|
||||||
return TypeInt::make(constant.as_int());
|
ciConstant constant = string->as_instance()->field_value(field);
|
||||||
} else if (constant.basic_type() == T_ARRAY) {
|
if (constant.basic_type() == T_INT) {
|
||||||
if (adr->bottom_type()->is_ptr_to_narrowoop()) {
|
return TypeInt::make(constant.as_int());
|
||||||
return TypeNarrowOop::make_from_constant(constant.as_object());
|
} else if (constant.basic_type() == T_ARRAY) {
|
||||||
} else {
|
if (adr->bottom_type()->is_ptr_to_narrowoop()) {
|
||||||
return TypeOopPtr::make_from_constant(constant.as_object());
|
return TypeNarrowOop::make_from_constant(constant.as_object());
|
||||||
|
} else {
|
||||||
|
return TypeOopPtr::make_from_constant(constant.as_object());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
409
hotspot/test/compiler/6942326/Test.java
Normal file
409
hotspot/test/compiler/6942326/Test.java
Normal file
|
@ -0,0 +1,409 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2011, 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
|
||||||
|
* @bug 6942326
|
||||||
|
* @summary x86 code in string_indexof() could read beyond reserved heap space
|
||||||
|
*
|
||||||
|
* @run main/othervm/timeout=300 -Xmx32m -Xbatch -XX:+IgnoreUnrecognizedVMOptions -XX:CompileCommand=exclude,Test,main -XX:CompileCommand=exclude,Test,test_varsub_indexof -XX:CompileCommand=exclude,Test,test_varstr_indexof -XX:CompileCommand=exclude,Test,test_missub_indexof -XX:CompileCommand=exclude,Test,test_consub_indexof -XX:CompileCommand=exclude,Test,test_conmis_indexof -XX:CompileCommand=exclude,Test,test_subcon Test
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class Test {
|
||||||
|
|
||||||
|
static String[] strings = new String[1024];
|
||||||
|
private static final int ITERATIONS = 100000;
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
|
||||||
|
long start_total = System.currentTimeMillis();
|
||||||
|
|
||||||
|
// search variable size substring in string (33 chars).
|
||||||
|
String a = " 1111111111111xx1111111111111xx11y"; // +1 to execute a.substring(1) first
|
||||||
|
String b = "1111111111111xx1111111111111xx11y";
|
||||||
|
test_varsub_indexof(a, b);
|
||||||
|
|
||||||
|
// search variable size substring in string (32 chars).
|
||||||
|
a = " 1111111111111xx1111111111111xx1y";
|
||||||
|
b = "1111111111111xx1111111111111xx1y";
|
||||||
|
test_varsub_indexof(a, b);
|
||||||
|
|
||||||
|
// search variable size substring in string (17 chars).
|
||||||
|
a = " 1111111111111xx1y";
|
||||||
|
b = "1111111111111xx1y";
|
||||||
|
test_varsub_indexof(a, b);
|
||||||
|
|
||||||
|
// search variable size substring in string (16 chars).
|
||||||
|
a = " 111111111111xx1y";
|
||||||
|
b = "111111111111xx1y";
|
||||||
|
test_varsub_indexof(a, b);
|
||||||
|
|
||||||
|
// search variable size substring in string (8 chars).
|
||||||
|
a = " 1111xx1y";
|
||||||
|
b = "1111xx1y";
|
||||||
|
test_varsub_indexof(a, b);
|
||||||
|
|
||||||
|
// search variable size substring in string (7 chars).
|
||||||
|
a = " 111xx1y";
|
||||||
|
b = "111xx1y";
|
||||||
|
test_varsub_indexof(a, b);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// search substring (17 chars) in variable size string.
|
||||||
|
a = "1111111111111xx1x";
|
||||||
|
b = " 1111111111111xx1111111111111xx1x"; // +1 to execute b.substring(1) first
|
||||||
|
test_varstr_indexof(a, b);
|
||||||
|
|
||||||
|
// search substring (16 chars) in variable size string.
|
||||||
|
a = "111111111111xx1x";
|
||||||
|
b = " 1111111111111xx1111111111111xx1x";
|
||||||
|
test_varstr_indexof(a, b);
|
||||||
|
|
||||||
|
// search substring (9 chars) in variable size string.
|
||||||
|
a = "11111xx1x";
|
||||||
|
b = " 1111111111111xx1111111111111xx1x";
|
||||||
|
test_varstr_indexof(a, b);
|
||||||
|
|
||||||
|
// search substring (8 chars) in variable size string.
|
||||||
|
a = "1111xx1x";
|
||||||
|
b = " 1111111111111xx1111111111111xx1x";
|
||||||
|
test_varstr_indexof(a, b);
|
||||||
|
|
||||||
|
// search substring (4 chars) in variable size string.
|
||||||
|
a = "xx1x";
|
||||||
|
b = " 1111111111111xx1111111111111xx1x";
|
||||||
|
test_varstr_indexof(a, b);
|
||||||
|
|
||||||
|
// search substring (3 chars) in variable size string.
|
||||||
|
a = "x1x";
|
||||||
|
b = " 1111111111111xx1111111111111xx1x";
|
||||||
|
test_varstr_indexof(a, b);
|
||||||
|
|
||||||
|
// search substring (2 chars) in variable size string.
|
||||||
|
a = "1y";
|
||||||
|
b = " 1111111111111xx1111111111111xx1y";
|
||||||
|
test_varstr_indexof(a, b);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// search non matching variable size substring in string (33 chars).
|
||||||
|
a = " 1111111111111xx1111111111111xx11z"; // +1 to execute a.substring(1) first
|
||||||
|
b = "1111111111111xx1111111111111xx11y";
|
||||||
|
test_missub_indexof(a, b);
|
||||||
|
|
||||||
|
// search non matching variable size substring in string (32 chars).
|
||||||
|
a = " 1111111111111xx1111111111111xx1z";
|
||||||
|
b = "1111111111111xx1111111111111xx1y";
|
||||||
|
test_missub_indexof(a, b);
|
||||||
|
|
||||||
|
// search non matching variable size substring in string (17 chars).
|
||||||
|
a = " 1111111111111xx1z";
|
||||||
|
b = "1111111111111xx1y";
|
||||||
|
test_missub_indexof(a, b);
|
||||||
|
|
||||||
|
// search non matching variable size substring in string (16 chars).
|
||||||
|
a = " 111111111111xx1z";
|
||||||
|
b = "111111111111xx1y";
|
||||||
|
test_missub_indexof(a, b);
|
||||||
|
|
||||||
|
// search non matching variable size substring in string (8 chars).
|
||||||
|
a = " 1111xx1z";
|
||||||
|
b = "1111xx1y";
|
||||||
|
test_missub_indexof(a, b);
|
||||||
|
|
||||||
|
// search non matching variable size substring in string (7 chars).
|
||||||
|
a = " 111xx1z";
|
||||||
|
b = "111xx1y";
|
||||||
|
test_missub_indexof(a, b);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Testing constant substring search in variable size string.
|
||||||
|
|
||||||
|
// search constant substring (17 chars).
|
||||||
|
b = " 1111111111111xx1111111111111xx1x"; // +1 to execute b.substring(1) first
|
||||||
|
TestCon tc = new TestCon17();
|
||||||
|
test_consub_indexof(tc, b);
|
||||||
|
|
||||||
|
// search constant substring (16 chars).
|
||||||
|
b = " 1111111111111xx1111111111111xx1x";
|
||||||
|
tc = new TestCon16();
|
||||||
|
test_consub_indexof(tc, b);
|
||||||
|
|
||||||
|
// search constant substring (9 chars).
|
||||||
|
b = " 1111111111111xx1111111111111xx1x";
|
||||||
|
tc = new TestCon9();
|
||||||
|
test_consub_indexof(tc, b);
|
||||||
|
|
||||||
|
// search constant substring (8 chars).
|
||||||
|
b = " 1111111111111xx1111111111111xx1x";
|
||||||
|
tc = new TestCon8();
|
||||||
|
test_consub_indexof(tc, b);
|
||||||
|
|
||||||
|
// search constant substring (4 chars).
|
||||||
|
b = " 1111111111111xx1111111111111xx1x";
|
||||||
|
tc = new TestCon4();
|
||||||
|
test_consub_indexof(tc, b);
|
||||||
|
|
||||||
|
// search constant substring (3 chars).
|
||||||
|
b = " 1111111111111xx1111111111111xx1x";
|
||||||
|
tc = new TestCon3();
|
||||||
|
test_consub_indexof(tc, b);
|
||||||
|
|
||||||
|
// search constant substring (2 chars).
|
||||||
|
b = " 1111111111111xx1111111111111xx1y";
|
||||||
|
tc = new TestCon2();
|
||||||
|
test_consub_indexof(tc, b);
|
||||||
|
|
||||||
|
// search constant substring (1 chars).
|
||||||
|
b = " 1111111111111xx1111111111111xx1y";
|
||||||
|
tc = new TestCon1();
|
||||||
|
test_consub_indexof(tc, b);
|
||||||
|
|
||||||
|
|
||||||
|
// search non matching constant substring (17 chars).
|
||||||
|
b = " 1111111111111xx1111111111111xx1z"; // +1 to execute b.substring(1) first
|
||||||
|
tc = new TestCon17();
|
||||||
|
test_conmis_indexof(tc, b);
|
||||||
|
|
||||||
|
// search non matching constant substring (16 chars).
|
||||||
|
b = " 1111111111111xx1111111111111xx1z";
|
||||||
|
tc = new TestCon16();
|
||||||
|
test_conmis_indexof(tc, b);
|
||||||
|
|
||||||
|
// search non matching constant substring (9 chars).
|
||||||
|
b = " 1111111111111xx1111111111111xx1z";
|
||||||
|
tc = new TestCon9();
|
||||||
|
test_conmis_indexof(tc, b);
|
||||||
|
|
||||||
|
// search non matching constant substring (8 chars).
|
||||||
|
b = " 1111111111111xx1111111111111xx1z";
|
||||||
|
tc = new TestCon8();
|
||||||
|
test_conmis_indexof(tc, b);
|
||||||
|
|
||||||
|
// search non matching constant substring (4 chars).
|
||||||
|
b = " 1111111111111xx1111111111111xx1z";
|
||||||
|
tc = new TestCon4();
|
||||||
|
test_conmis_indexof(tc, b);
|
||||||
|
|
||||||
|
// search non matching constant substring (3 chars).
|
||||||
|
b = " 1111111111111xx1111111111111xx1z";
|
||||||
|
tc = new TestCon3();
|
||||||
|
test_conmis_indexof(tc, b);
|
||||||
|
|
||||||
|
// search non matching constant substring (2 chars).
|
||||||
|
b = " 1111111111111xx1111111111111xx1z";
|
||||||
|
tc = new TestCon2();
|
||||||
|
test_conmis_indexof(tc, b);
|
||||||
|
|
||||||
|
// search non matching constant substring (1 chars).
|
||||||
|
b = " 1111111111111xx1111111111111xx1z";
|
||||||
|
tc = new TestCon1();
|
||||||
|
test_conmis_indexof(tc, b);
|
||||||
|
|
||||||
|
long end_total = System.currentTimeMillis();
|
||||||
|
System.out.println("End run time: " + (end_total - start_total));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long test_init(String a, String b) {
|
||||||
|
for (int i = 0; i < 512; i++) {
|
||||||
|
strings[i * 2] = new String(b.toCharArray());
|
||||||
|
strings[i * 2 + 1] = new String(a.toCharArray());
|
||||||
|
}
|
||||||
|
System.out.print(a.length() + " " + b.length() + " ");
|
||||||
|
return System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void test_end(String a, String b, int v, int expected, long start) {
|
||||||
|
long end = System.currentTimeMillis();
|
||||||
|
int res = (v/ITERATIONS);
|
||||||
|
System.out.print(" " + res);
|
||||||
|
System.out.println(" time:" + (end - start));
|
||||||
|
if (res != expected) {
|
||||||
|
System.out.println("wrong indexOf result: " + res + ", expected " + expected);
|
||||||
|
System.out.println("\"" + b + "\".indexOf(\"" + a + "\")");
|
||||||
|
System.exit(97);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int test_subvar() {
|
||||||
|
int s = 0;
|
||||||
|
int v = 0;
|
||||||
|
for (int i = 0; i < ITERATIONS; i++) {
|
||||||
|
v += strings[s].indexOf(strings[s + 1]);
|
||||||
|
s += 2;
|
||||||
|
if (s >= strings.length) s = 0;
|
||||||
|
}
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void test_varsub_indexof(String a, String b) {
|
||||||
|
System.out.println("Start search variable size substring in string (" + b.length() + " chars)");
|
||||||
|
long start_it = System.currentTimeMillis();
|
||||||
|
int limit = 1; // last a.length() == 1
|
||||||
|
while (a.length() > limit) {
|
||||||
|
a = a.substring(1);
|
||||||
|
long start = test_init(a, b);
|
||||||
|
int v = test_subvar();
|
||||||
|
test_end(a, b, v, (b.length() - a.length()), start);
|
||||||
|
}
|
||||||
|
long end_it = System.currentTimeMillis();
|
||||||
|
System.out.println("End search variable size substring in string (" + b.length() + " chars), time: " + (end_it - start_it));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void test_varstr_indexof(String a, String b) {
|
||||||
|
System.out.println("Start search substring (" + a.length() + " chars) in variable size string");
|
||||||
|
long start_it = System.currentTimeMillis();
|
||||||
|
int limit = a.length();
|
||||||
|
while (b.length() > limit) {
|
||||||
|
b = b.substring(1);
|
||||||
|
long start = test_init(a, b);
|
||||||
|
int v = test_subvar();
|
||||||
|
test_end(a, b, v, (b.length() - a.length()), start);
|
||||||
|
}
|
||||||
|
long end_it = System.currentTimeMillis();
|
||||||
|
System.out.println("End search substring (" + a.length() + " chars) in variable size string, time: " + (end_it - start_it));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void test_missub_indexof(String a, String b) {
|
||||||
|
System.out.println("Start search non matching variable size substring in string (" + b.length() + " chars)");
|
||||||
|
long start_it = System.currentTimeMillis();
|
||||||
|
int limit = 1; // last a.length() == 1
|
||||||
|
while (a.length() > limit) {
|
||||||
|
a = a.substring(1);
|
||||||
|
long start = test_init(a, b);
|
||||||
|
int v = test_subvar();
|
||||||
|
test_end(a, b, v, (-1), start);
|
||||||
|
}
|
||||||
|
long end_it = System.currentTimeMillis();
|
||||||
|
System.out.println("End search non matching variable size substring in string (" + b.length() + " chars), time: " + (end_it - start_it));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public static void test_consub_indexof(TestCon tc, String b) {
|
||||||
|
System.out.println("Start search constant substring (" + tc.constr().length() + " chars)");
|
||||||
|
long start_it = System.currentTimeMillis();
|
||||||
|
int limit = tc.constr().length();
|
||||||
|
while (b.length() > limit) {
|
||||||
|
b = b.substring(1);
|
||||||
|
long start = test_init(tc.constr(), b);
|
||||||
|
int v = test_subcon(tc);
|
||||||
|
test_end(tc.constr(), b, v, (b.length() - tc.constr().length()), start);
|
||||||
|
}
|
||||||
|
long end_it = System.currentTimeMillis();
|
||||||
|
System.out.println("End search constant substring (" + tc.constr().length() + " chars), time: " + (end_it - start_it));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void test_conmis_indexof(TestCon tc, String b) {
|
||||||
|
System.out.println("Start search non matching constant substring (" + tc.constr().length() + " chars)");
|
||||||
|
long start_it = System.currentTimeMillis();
|
||||||
|
int limit = tc.constr().length();
|
||||||
|
while (b.length() > limit) {
|
||||||
|
b = b.substring(1);
|
||||||
|
long start = test_init(tc.constr(), b);
|
||||||
|
int v = test_subcon(tc);
|
||||||
|
test_end(tc.constr(), b, v, (-1), start);
|
||||||
|
}
|
||||||
|
long end_it = System.currentTimeMillis();
|
||||||
|
System.out.println("End search non matching constant substring (" + tc.constr().length() + " chars), time: " + (end_it - start_it));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int test_subcon(TestCon tc) {
|
||||||
|
int s = 0;
|
||||||
|
int v = 0;
|
||||||
|
for (int i = 0; i < ITERATIONS; i++) {
|
||||||
|
v += tc.indexOf(strings[s]);
|
||||||
|
s += 2;
|
||||||
|
if (s >= strings.length) s = 0;
|
||||||
|
}
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
private interface TestCon {
|
||||||
|
public String constr();
|
||||||
|
public int indexOf(String str);
|
||||||
|
}
|
||||||
|
|
||||||
|
// search constant substring (17 chars).
|
||||||
|
private final static class TestCon17 implements TestCon {
|
||||||
|
private static final String constr = "1111111111111xx1x";
|
||||||
|
public String constr() { return constr; }
|
||||||
|
public int indexOf(String str) { return str.indexOf(constr); }
|
||||||
|
}
|
||||||
|
|
||||||
|
// search constant substring (16 chars).
|
||||||
|
private final static class TestCon16 implements TestCon {
|
||||||
|
private static final String constr = "111111111111xx1x";
|
||||||
|
public String constr() { return constr; }
|
||||||
|
public int indexOf(String str) { return str.indexOf(constr); }
|
||||||
|
}
|
||||||
|
|
||||||
|
// search constant substring (9 chars).
|
||||||
|
private final static class TestCon9 implements TestCon {
|
||||||
|
private static final String constr = "11111xx1x";
|
||||||
|
public String constr() { return constr; }
|
||||||
|
public int indexOf(String str) { return str.indexOf(constr); }
|
||||||
|
}
|
||||||
|
|
||||||
|
// search constant substring (8 chars).
|
||||||
|
private final static class TestCon8 implements TestCon {
|
||||||
|
private static final String constr = "1111xx1x";
|
||||||
|
public String constr() { return constr; }
|
||||||
|
public int indexOf(String str) { return str.indexOf(constr); }
|
||||||
|
}
|
||||||
|
|
||||||
|
// search constant substring (4 chars).
|
||||||
|
private final static class TestCon4 implements TestCon {
|
||||||
|
private static final String constr = "xx1x";
|
||||||
|
public String constr() { return constr; }
|
||||||
|
public int indexOf(String str) { return str.indexOf(constr); }
|
||||||
|
}
|
||||||
|
|
||||||
|
// search constant substring (3 chars).
|
||||||
|
private final static class TestCon3 implements TestCon {
|
||||||
|
private static final String constr = "x1x";
|
||||||
|
public String constr() { return constr; }
|
||||||
|
public int indexOf(String str) { return str.indexOf(constr); }
|
||||||
|
}
|
||||||
|
|
||||||
|
// search constant substring (2 chars).
|
||||||
|
private final static class TestCon2 implements TestCon {
|
||||||
|
private static final String constr = "1y";
|
||||||
|
public String constr() { return constr; }
|
||||||
|
public int indexOf(String str) { return str.indexOf(constr); }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// search constant substring (1 chars).
|
||||||
|
private final static class TestCon1 implements TestCon {
|
||||||
|
private static final String constr = "y";
|
||||||
|
public String constr() { return constr; }
|
||||||
|
public int indexOf(String str) { return str.indexOf(constr); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue