2 * Copyright (C) 2009 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 #include "assembler/assembler/LinkBuffer.h"
31 #include "assembler/assembler/MacroAssembler.h"
32 #include "RegexCompiler.h"
34 #include "yarr/pcre/pcre.h" // temporary, remove when fallback is removed.
38 namespace JSC { namespace Yarr {
42 class RegexGenerator : private MacroAssembler {
43 friend void jitCompileRegex(JSGlobalData* globalData, RegexCodeBlock& jitObject, const UString& pattern, unsigned& numSubpatterns, const char*& error, bool ignoreCase, bool multiline);
46 static const RegisterID input = ARMRegisters::r0;
47 static const RegisterID index = ARMRegisters::r1;
48 static const RegisterID length = ARMRegisters::r2;
49 static const RegisterID output = ARMRegisters::r4;
51 static const RegisterID regT0 = ARMRegisters::r5;
52 static const RegisterID regT1 = ARMRegisters::r6;
54 static const RegisterID returnRegister = ARMRegisters::r0;
56 static const RegisterID input = MIPSRegisters::a0;
57 static const RegisterID index = MIPSRegisters::a1;
58 static const RegisterID length = MIPSRegisters::a2;
59 static const RegisterID output = MIPSRegisters::a3;
61 static const RegisterID regT0 = MIPSRegisters::t4;
62 static const RegisterID regT1 = MIPSRegisters::t5;
64 static const RegisterID returnRegister = MIPSRegisters::v0;
66 static const RegisterID input = X86Registers::eax;
67 static const RegisterID index = X86Registers::edx;
68 static const RegisterID length = X86Registers::ecx;
69 static const RegisterID output = X86Registers::edi;
71 static const RegisterID regT0 = X86Registers::ebx;
72 static const RegisterID regT1 = X86Registers::esi;
74 static const RegisterID returnRegister = X86Registers::eax;
77 static const RegisterID input = X86Registers::ecx;
78 static const RegisterID index = X86Registers::edx;
79 static const RegisterID length = X86Registers::r8;
80 static const RegisterID output = X86Registers::r9;
82 static const RegisterID input = X86Registers::edi;
83 static const RegisterID index = X86Registers::esi;
84 static const RegisterID length = X86Registers::edx;
85 static const RegisterID output = X86Registers::ecx;
88 static const RegisterID regT0 = X86Registers::eax;
89 static const RegisterID regT1 = X86Registers::ebx;
91 static const RegisterID returnRegister = X86Registers::eax;
94 void optimizeAlternative(PatternAlternative* alternative)
96 if (!alternative->m_terms.length())
99 for (unsigned i = 0; i < alternative->m_terms.length() - 1; ++i) {
100 PatternTerm& term = alternative->m_terms[i];
101 PatternTerm& nextTerm = alternative->m_terms[i + 1];
103 if ((term.type == PatternTerm::TypeCharacterClass)
104 && (term.quantityType == QuantifierFixedCount)
105 && (nextTerm.type == PatternTerm::TypePatternCharacter)
106 && (nextTerm.quantityType == QuantifierFixedCount)) {
107 PatternTerm termCopy = term;
108 alternative->m_terms[i] = nextTerm;
109 alternative->m_terms[i + 1] = termCopy;
114 void matchCharacterClassRange(RegisterID character, JumpList& failures, JumpList& matchDest, const CharacterRange* ranges, unsigned count, unsigned* matchIndex, const UChar* matches, unsigned matchCount)
117 // pick which range we're going to generate
118 int which = count >> 1;
119 char lo = ranges[which].begin;
120 char hi = ranges[which].end;
122 // check if there are any ranges or matches below lo. If not, just jl to failure -
123 // if there is anything else to check, check that first, if it falls through jmp to failure.
124 if ((*matchIndex < matchCount) && (matches[*matchIndex] < lo)) {
125 Jump loOrAbove = branch32(GreaterThanOrEqual, character, Imm32((unsigned short)lo));
127 // generate code for all ranges before this one
129 matchCharacterClassRange(character, failures, matchDest, ranges, which, matchIndex, matches, matchCount);
131 while ((*matchIndex < matchCount) && (matches[*matchIndex] < lo)) {
132 matchDest.append(branch32(Equal, character, Imm32((unsigned short)matches[*matchIndex])));
135 failures.append(jump());
137 loOrAbove.link(this);
139 Jump loOrAbove = branch32(GreaterThanOrEqual, character, Imm32((unsigned short)lo));
141 matchCharacterClassRange(character, failures, matchDest, ranges, which, matchIndex, matches, matchCount);
142 failures.append(jump());
144 loOrAbove.link(this);
146 failures.append(branch32(LessThan, character, Imm32((unsigned short)lo)));
148 while ((*matchIndex < matchCount) && (matches[*matchIndex] <= hi))
151 matchDest.append(branch32(LessThanOrEqual, character, Imm32((unsigned short)hi)));
152 // fall through to here, the value is above hi.
154 // shuffle along & loop around if there are any more matches to handle.
155 unsigned next = which + 1;
161 void matchCharacterClass(RegisterID character, JumpList& matchDest, const CharacterClass* charClass)
163 if (charClass->m_table) {
164 ExtendedAddress tableEntry(character, reinterpret_cast<intptr_t>(charClass->m_table->m_table));
165 matchDest.append(branchTest8(charClass->m_table->m_inverted ? Zero : NonZero, tableEntry));
169 if (charClass->m_matchesUnicode.length() || charClass->m_rangesUnicode.length()) {
170 Jump isAscii = branch32(LessThanOrEqual, character, Imm32(0x7f));
172 if (charClass->m_matchesUnicode.length()) {
173 for (unsigned i = 0; i < charClass->m_matchesUnicode.length(); ++i) {
174 UChar ch = charClass->m_matchesUnicode[i];
175 matchDest.append(branch32(Equal, character, Imm32(ch)));
179 if (charClass->m_rangesUnicode.length()) {
180 for (unsigned i = 0; i < charClass->m_rangesUnicode.length(); ++i) {
181 UChar lo = charClass->m_rangesUnicode[i].begin;
182 UChar hi = charClass->m_rangesUnicode[i].end;
184 Jump below = branch32(LessThan, character, Imm32(lo));
185 matchDest.append(branch32(LessThanOrEqual, character, Imm32(hi)));
190 unicodeFail = jump();
194 if (charClass->m_ranges.length()) {
195 unsigned matchIndex = 0;
197 matchCharacterClassRange(character, failures, matchDest, charClass->m_ranges.begin(), charClass->m_ranges.length(), &matchIndex, charClass->m_matches.begin(), charClass->m_matches.length());
198 while (matchIndex < charClass->m_matches.length())
199 matchDest.append(branch32(Equal, character, Imm32((unsigned short)charClass->m_matches[matchIndex++])));
202 } else if (charClass->m_matches.length()) {
203 // optimization: gather 'a','A' etc back together, can mask & test once.
204 js::Vector<char, 16, js::SystemAllocPolicy> matchesAZaz;
206 for (unsigned i = 0; i < charClass->m_matches.length(); ++i) {
207 char ch = charClass->m_matches[i];
208 if (m_pattern.m_ignoreCase) {
209 if (isASCIILower(ch)) {
210 matchesAZaz.append(ch);
213 if (isASCIIUpper(ch))
216 matchDest.append(branch32(Equal, character, Imm32((unsigned short)ch)));
219 if (unsigned countAZaz = matchesAZaz.length()) {
220 or32(Imm32(32), character);
221 for (unsigned i = 0; i < countAZaz; ++i)
222 matchDest.append(branch32(Equal, character, Imm32(matchesAZaz[i])));
226 if (charClass->m_matchesUnicode.length() || charClass->m_rangesUnicode.length())
227 unicodeFail.link(this);
230 // Jumps if input not available; will have (incorrectly) incremented already!
231 Jump jumpIfNoAvailableInput(unsigned countToCheck)
233 add32(Imm32(countToCheck), index);
234 return branch32(Above, index, length);
237 Jump jumpIfAvailableInput(unsigned countToCheck)
239 add32(Imm32(countToCheck), index);
240 return branch32(BelowOrEqual, index, length);
245 return branch32(BelowOrEqual, index, length);
250 return branch32(Equal, index, length);
253 Jump notAtEndOfInput()
255 return branch32(NotEqual, index, length);
258 Jump jumpIfCharEquals(UChar ch, int inputPosition)
260 return branch16(Equal, BaseIndex(input, index, TimesTwo, inputPosition * sizeof(UChar)), Imm32(ch));
263 Jump jumpIfCharNotEquals(UChar ch, int inputPosition)
265 return branch16(NotEqual, BaseIndex(input, index, TimesTwo, inputPosition * sizeof(UChar)), Imm32(ch));
268 void readCharacter(int inputPosition, RegisterID reg)
270 load16(BaseIndex(input, index, TimesTwo, inputPosition * sizeof(UChar)), reg);
273 void storeToFrame(RegisterID reg, unsigned frameLocation)
275 poke(reg, frameLocation);
278 void storeToFrame(Imm32 imm, unsigned frameLocation)
280 poke(imm, frameLocation);
283 DataLabelPtr storeToFrameWithPatch(unsigned frameLocation)
285 return storePtrWithPatch(ImmPtr(0), Address(stackPointerRegister, frameLocation * sizeof(void*)));
288 void loadFromFrame(unsigned frameLocation, RegisterID reg)
290 peek(reg, frameLocation);
293 void loadFromFrameAndJump(unsigned frameLocation)
295 jump(Address(stackPointerRegister, frameLocation * sizeof(void*)));
298 struct AlternativeBacktrackRecord {
299 DataLabelPtr dataLabel;
300 Label backtrackLocation;
302 AlternativeBacktrackRecord(DataLabelPtr dataLabel, Label backtrackLocation)
303 : dataLabel(dataLabel)
304 , backtrackLocation(backtrackLocation)
309 struct TermGenerationState {
310 TermGenerationState(PatternDisjunction* disjunction, unsigned checkedTotal)
311 : disjunction(disjunction)
312 , checkedTotal(checkedTotal)
316 void resetAlternative()
318 isBackTrackGenerated = false;
321 bool alternativeValid()
323 return alt < disjunction->m_alternatives.length();
325 void nextAlternative()
329 PatternAlternative* alternative()
331 return disjunction->m_alternatives[alt];
336 ASSERT(alternativeValid());
341 ASSERT(alternativeValid());
342 return t < alternative()->m_terms.length();
346 ASSERT(alternativeValid());
351 ASSERT(alternativeValid());
352 return alternative()->m_terms[t];
356 ASSERT(alternativeValid());
357 return (t + 1) == alternative()->m_terms.length();
359 bool isMainDisjunction()
361 return !disjunction->m_parent;
364 PatternTerm& lookaheadTerm()
366 ASSERT(alternativeValid());
367 ASSERT((t + 1) < alternative()->m_terms.length());
368 return alternative()->m_terms[t + 1];
370 bool isSinglePatternCharacterLookaheadTerm()
372 ASSERT(alternativeValid());
373 return ((t + 1) < alternative()->m_terms.length())
374 && (lookaheadTerm().type == PatternTerm::TypePatternCharacter)
375 && (lookaheadTerm().quantityType == QuantifierFixedCount)
376 && (lookaheadTerm().quantityCount == 1);
381 return term().inputPosition - checkedTotal;
384 void jumpToBacktrack(Jump jump, MacroAssembler* masm)
386 if (isBackTrackGenerated)
387 jump.linkTo(backtrackLabel, masm);
389 backTrackJumps.append(jump);
391 void jumpToBacktrack(JumpList& jumps, MacroAssembler* masm)
393 if (isBackTrackGenerated)
394 jumps.linkTo(backtrackLabel, masm);
396 backTrackJumps.append(jumps);
398 bool plantJumpToBacktrackIfExists(MacroAssembler* masm)
400 if (isBackTrackGenerated) {
401 masm->jump(backtrackLabel);
406 void addBacktrackJump(Jump jump)
408 backTrackJumps.append(jump);
410 void setBacktrackGenerated(Label label)
412 isBackTrackGenerated = true;
413 backtrackLabel = label;
415 void linkAlternativeBacktracks(MacroAssembler* masm)
417 isBackTrackGenerated = false;
418 backTrackJumps.link(masm);
420 void linkAlternativeBacktracksTo(Label label, MacroAssembler* masm)
422 isBackTrackGenerated = false;
423 backTrackJumps.linkTo(label, masm);
425 void propagateBacktrackingFrom(TermGenerationState& nestedParenthesesState, MacroAssembler* masm)
427 jumpToBacktrack(nestedParenthesesState.backTrackJumps, masm);
428 if (nestedParenthesesState.isBackTrackGenerated)
429 setBacktrackGenerated(nestedParenthesesState.backtrackLabel);
432 PatternDisjunction* disjunction;
437 JumpList backTrackJumps;
438 Label backtrackLabel;
439 bool isBackTrackGenerated;
442 void generateAssertionBOL(TermGenerationState& state)
444 PatternTerm& term = state.term();
446 if (m_pattern.m_multiline) {
447 const RegisterID character = regT0;
450 if (!term.inputPosition)
451 matchDest.append(branch32(Equal, index, Imm32(state.checkedTotal)));
453 readCharacter(state.inputOffset() - 1, character);
454 matchCharacterClass(character, matchDest, m_pattern.newlineCharacterClass());
455 state.jumpToBacktrack(jump(), this);
457 matchDest.link(this);
459 // Erk, really should poison out these alternatives early. :-/
460 if (term.inputPosition)
461 state.jumpToBacktrack(jump(), this);
463 state.jumpToBacktrack(branch32(NotEqual, index, Imm32(state.checkedTotal)), this);
467 void generateAssertionEOL(TermGenerationState& state)
469 PatternTerm& term = state.term();
471 if (m_pattern.m_multiline) {
472 const RegisterID character = regT0;
475 if (term.inputPosition == state.checkedTotal)
476 matchDest.append(atEndOfInput());
478 readCharacter(state.inputOffset(), character);
479 matchCharacterClass(character, matchDest, m_pattern.newlineCharacterClass());
480 state.jumpToBacktrack(jump(), this);
482 matchDest.link(this);
484 if (term.inputPosition == state.checkedTotal)
485 state.jumpToBacktrack(notAtEndOfInput(), this);
486 // Erk, really should poison out these alternatives early. :-/
488 state.jumpToBacktrack(jump(), this);
492 // Also falls though on nextIsNotWordChar.
493 void matchAssertionWordchar(TermGenerationState& state, JumpList& nextIsWordChar, JumpList& nextIsNotWordChar)
495 const RegisterID character = regT0;
496 PatternTerm& term = state.term();
498 if (term.inputPosition == state.checkedTotal)
499 nextIsNotWordChar.append(atEndOfInput());
501 readCharacter(state.inputOffset(), character);
502 matchCharacterClass(character, nextIsWordChar, m_pattern.wordcharCharacterClass());
505 void generateAssertionWordBoundary(TermGenerationState& state)
507 const RegisterID character = regT0;
508 PatternTerm& term = state.term();
512 if (!term.inputPosition)
513 atBegin = branch32(Equal, index, Imm32(state.checkedTotal));
514 readCharacter(state.inputOffset() - 1, character);
515 matchCharacterClass(character, matchDest, m_pattern.wordcharCharacterClass());
516 if (!term.inputPosition)
519 // We fall through to here if the last character was not a wordchar.
520 JumpList nonWordCharThenWordChar;
521 JumpList nonWordCharThenNonWordChar;
522 if (term.invertOrCapture) {
523 matchAssertionWordchar(state, nonWordCharThenNonWordChar, nonWordCharThenWordChar);
524 nonWordCharThenWordChar.append(jump());
526 matchAssertionWordchar(state, nonWordCharThenWordChar, nonWordCharThenNonWordChar);
527 nonWordCharThenNonWordChar.append(jump());
529 state.jumpToBacktrack(nonWordCharThenNonWordChar, this);
531 // We jump here if the last character was a wordchar.
532 matchDest.link(this);
533 JumpList wordCharThenWordChar;
534 JumpList wordCharThenNonWordChar;
535 if (term.invertOrCapture) {
536 matchAssertionWordchar(state, wordCharThenNonWordChar, wordCharThenWordChar);
537 wordCharThenWordChar.append(jump());
539 matchAssertionWordchar(state, wordCharThenWordChar, wordCharThenNonWordChar);
540 // This can fall-though!
543 state.jumpToBacktrack(wordCharThenWordChar, this);
545 nonWordCharThenWordChar.link(this);
546 wordCharThenNonWordChar.link(this);
549 void generatePatternCharacterSingle(TermGenerationState& state)
551 const RegisterID character = regT0;
552 UChar ch = state.term().patternCharacter;
554 if (m_pattern.m_ignoreCase && isASCIIAlpha(ch)) {
555 readCharacter(state.inputOffset(), character);
556 or32(Imm32(32), character);
557 state.jumpToBacktrack(branch32(NotEqual, character, Imm32(Unicode::toLower(ch))), this);
559 ASSERT(!m_pattern.m_ignoreCase || (Unicode::toLower(ch) == Unicode::toUpper(ch)));
560 state.jumpToBacktrack(jumpIfCharNotEquals(ch, state.inputOffset()), this);
564 void generatePatternCharacterPair(TermGenerationState& state)
566 const RegisterID character = regT0;
567 UChar ch1 = state.term().patternCharacter;
568 UChar ch2 = state.lookaheadTerm().patternCharacter;
571 int chPair = ch1 | (ch2 << 16);
573 if (m_pattern.m_ignoreCase) {
574 if (isASCIIAlpha(ch1))
576 if (isASCIIAlpha(ch2))
581 load32WithUnalignedHalfWords(BaseIndex(input, index, TimesTwo, state.inputOffset() * sizeof(UChar)), character);
582 or32(Imm32(mask), character);
583 state.jumpToBacktrack(branch32(NotEqual, character, Imm32(chPair | mask)), this);
585 state.jumpToBacktrack(branch32WithUnalignedHalfWords(NotEqual, BaseIndex(input, index, TimesTwo, state.inputOffset() * sizeof(UChar)), Imm32(chPair)), this);
588 void generatePatternCharacterFixed(TermGenerationState& state)
590 const RegisterID character = regT0;
591 const RegisterID countRegister = regT1;
592 PatternTerm& term = state.term();
593 UChar ch = term.patternCharacter;
595 move(index, countRegister);
596 sub32(Imm32(term.quantityCount), countRegister);
599 if (m_pattern.m_ignoreCase && isASCIIAlpha(ch)) {
600 load16(BaseIndex(input, countRegister, TimesTwo, (state.inputOffset() + term.quantityCount) * sizeof(UChar)), character);
601 or32(Imm32(32), character);
602 state.jumpToBacktrack(branch32(NotEqual, character, Imm32(Unicode::toLower(ch))), this);
604 ASSERT(!m_pattern.m_ignoreCase || (Unicode::toLower(ch) == Unicode::toUpper(ch)));
605 state.jumpToBacktrack(branch16(NotEqual, BaseIndex(input, countRegister, TimesTwo, (state.inputOffset() + term.quantityCount) * sizeof(UChar)), Imm32(ch)), this);
607 add32(Imm32(1), countRegister);
608 branch32(NotEqual, countRegister, index).linkTo(loop, this);
611 void generatePatternCharacterGreedy(TermGenerationState& state)
613 const RegisterID character = regT0;
614 const RegisterID countRegister = regT1;
615 PatternTerm& term = state.term();
616 UChar ch = term.patternCharacter;
618 move(Imm32(0), countRegister);
622 failures.append(atEndOfInput());
623 if (m_pattern.m_ignoreCase && isASCIIAlpha(ch)) {
624 readCharacter(state.inputOffset(), character);
625 or32(Imm32(32), character);
626 failures.append(branch32(NotEqual, character, Imm32(Unicode::toLower(ch))));
628 ASSERT(!m_pattern.m_ignoreCase || (Unicode::toLower(ch) == Unicode::toUpper(ch)));
629 failures.append(jumpIfCharNotEquals(ch, state.inputOffset()));
632 add32(Imm32(1), countRegister);
633 add32(Imm32(1), index);
634 if (term.quantityCount != 0xffffffff) {
635 branch32(NotEqual, countRegister, Imm32(term.quantityCount)).linkTo(loop, this);
636 failures.append(jump());
640 Label backtrackBegin(this);
641 loadFromFrame(term.frameLocation, countRegister);
642 state.jumpToBacktrack(branchTest32(Zero, countRegister), this);
643 sub32(Imm32(1), countRegister);
644 sub32(Imm32(1), index);
648 storeToFrame(countRegister, term.frameLocation);
650 state.setBacktrackGenerated(backtrackBegin);
653 void generatePatternCharacterNonGreedy(TermGenerationState& state)
655 const RegisterID character = regT0;
656 const RegisterID countRegister = regT1;
657 PatternTerm& term = state.term();
658 UChar ch = term.patternCharacter;
660 move(Imm32(0), countRegister);
662 Jump firstTimeDoNothing = jump();
664 Label hardFail(this);
665 sub32(countRegister, index);
666 state.jumpToBacktrack(jump(), this);
668 Label backtrackBegin(this);
669 loadFromFrame(term.frameLocation, countRegister);
671 atEndOfInput().linkTo(hardFail, this);
672 if (term.quantityCount != 0xffffffff)
673 branch32(Equal, countRegister, Imm32(term.quantityCount), hardFail);
674 if (m_pattern.m_ignoreCase && isASCIIAlpha(ch)) {
675 readCharacter(state.inputOffset(), character);
676 or32(Imm32(32), character);
677 branch32(NotEqual, character, Imm32(Unicode::toLower(ch))).linkTo(hardFail, this);
679 ASSERT(!m_pattern.m_ignoreCase || (Unicode::toLower(ch) == Unicode::toUpper(ch)));
680 jumpIfCharNotEquals(ch, state.inputOffset()).linkTo(hardFail, this);
683 add32(Imm32(1), countRegister);
684 add32(Imm32(1), index);
686 firstTimeDoNothing.link(this);
687 storeToFrame(countRegister, term.frameLocation);
689 state.setBacktrackGenerated(backtrackBegin);
692 void generateCharacterClassSingle(TermGenerationState& state)
694 const RegisterID character = regT0;
695 PatternTerm& term = state.term();
698 readCharacter(state.inputOffset(), character);
699 matchCharacterClass(character, matchDest, term.characterClass);
701 if (term.invertOrCapture)
702 state.jumpToBacktrack(matchDest, this);
704 state.jumpToBacktrack(jump(), this);
705 matchDest.link(this);
709 void generateCharacterClassFixed(TermGenerationState& state)
711 const RegisterID character = regT0;
712 const RegisterID countRegister = regT1;
713 PatternTerm& term = state.term();
715 move(index, countRegister);
716 sub32(Imm32(term.quantityCount), countRegister);
720 load16(BaseIndex(input, countRegister, TimesTwo, (state.inputOffset() + term.quantityCount) * sizeof(UChar)), character);
721 matchCharacterClass(character, matchDest, term.characterClass);
723 if (term.invertOrCapture)
724 state.jumpToBacktrack(matchDest, this);
726 state.jumpToBacktrack(jump(), this);
727 matchDest.link(this);
730 add32(Imm32(1), countRegister);
731 branch32(NotEqual, countRegister, index).linkTo(loop, this);
734 void generateCharacterClassGreedy(TermGenerationState& state)
736 const RegisterID character = regT0;
737 const RegisterID countRegister = regT1;
738 PatternTerm& term = state.term();
740 move(Imm32(0), countRegister);
744 failures.append(atEndOfInput());
746 if (term.invertOrCapture) {
747 readCharacter(state.inputOffset(), character);
748 matchCharacterClass(character, failures, term.characterClass);
751 readCharacter(state.inputOffset(), character);
752 matchCharacterClass(character, matchDest, term.characterClass);
753 failures.append(jump());
754 matchDest.link(this);
757 add32(Imm32(1), countRegister);
758 add32(Imm32(1), index);
759 if (term.quantityCount != 0xffffffff) {
760 branch32(NotEqual, countRegister, Imm32(term.quantityCount)).linkTo(loop, this);
761 failures.append(jump());
765 Label backtrackBegin(this);
766 loadFromFrame(term.frameLocation, countRegister);
767 state.jumpToBacktrack(branchTest32(Zero, countRegister), this);
768 sub32(Imm32(1), countRegister);
769 sub32(Imm32(1), index);
773 storeToFrame(countRegister, term.frameLocation);
775 state.setBacktrackGenerated(backtrackBegin);
778 void generateCharacterClassNonGreedy(TermGenerationState& state)
780 const RegisterID character = regT0;
781 const RegisterID countRegister = regT1;
782 PatternTerm& term = state.term();
784 move(Imm32(0), countRegister);
786 Jump firstTimeDoNothing = jump();
788 Label hardFail(this);
789 sub32(countRegister, index);
790 state.jumpToBacktrack(jump(), this);
792 Label backtrackBegin(this);
793 loadFromFrame(term.frameLocation, countRegister);
795 atEndOfInput().linkTo(hardFail, this);
796 branch32(Equal, countRegister, Imm32(term.quantityCount), hardFail);
799 readCharacter(state.inputOffset(), character);
800 matchCharacterClass(character, matchDest, term.characterClass);
802 if (term.invertOrCapture)
803 matchDest.linkTo(hardFail, this);
806 matchDest.link(this);
809 add32(Imm32(1), countRegister);
810 add32(Imm32(1), index);
812 firstTimeDoNothing.link(this);
813 storeToFrame(countRegister, term.frameLocation);
815 state.setBacktrackGenerated(backtrackBegin);
818 void generateParenthesesDisjunction(PatternTerm& parenthesesTerm, TermGenerationState& state, unsigned alternativeFrameLocation)
820 ASSERT((parenthesesTerm.type == PatternTerm::TypeParenthesesSubpattern) || (parenthesesTerm.type == PatternTerm::TypeParentheticalAssertion));
821 ASSERT(parenthesesTerm.quantityCount == 1);
823 PatternDisjunction* disjunction = parenthesesTerm.parentheses.disjunction;
824 unsigned preCheckedCount = ((parenthesesTerm.quantityType == QuantifierFixedCount) && (parenthesesTerm.type != PatternTerm::TypeParentheticalAssertion)) ? disjunction->m_minimumSize : 0;
826 if (disjunction->m_alternatives.length() == 1) {
827 state.resetAlternative();
828 ASSERT(state.alternativeValid());
829 PatternAlternative* alternative = state.alternative();
830 optimizeAlternative(alternative);
832 int countToCheck = alternative->m_minimumSize - preCheckedCount;
834 ASSERT((parenthesesTerm.type == PatternTerm::TypeParentheticalAssertion) || (parenthesesTerm.quantityType != QuantifierFixedCount));
836 // FIXME: This is quite horrible. The call to 'plantJumpToBacktrackIfExists'
837 // will be forced to always trampoline into here, just to decrement the index.
841 Label backtrackBegin(this);
842 sub32(Imm32(countToCheck), index);
843 state.addBacktrackJump(jump());
847 state.setBacktrackGenerated(backtrackBegin);
849 state.jumpToBacktrack(jumpIfNoAvailableInput(countToCheck), this);
850 state.checkedTotal += countToCheck;
853 for (state.resetTerm(); state.termValid(); state.nextTerm())
856 state.checkedTotal -= countToCheck;
860 for (state.resetAlternative(); state.alternativeValid(); state.nextAlternative()) {
862 PatternAlternative* alternative = state.alternative();
863 optimizeAlternative(alternative);
865 ASSERT(alternative->m_minimumSize >= preCheckedCount);
866 int countToCheck = alternative->m_minimumSize - preCheckedCount;
868 state.addBacktrackJump(jumpIfNoAvailableInput(countToCheck));
869 state.checkedTotal += countToCheck;
872 for (state.resetTerm(); state.termValid(); state.nextTerm())
875 // Matched an alternative.
876 DataLabelPtr dataLabel = storeToFrameWithPatch(alternativeFrameLocation);
877 successes.append(jump());
879 // Alternative did not match.
880 Label backtrackLocation(this);
882 // Can we backtrack the alternative? - if so, do so. If not, just fall through to the next one.
883 state.plantJumpToBacktrackIfExists(this);
885 state.linkAlternativeBacktracks(this);
888 sub32(Imm32(countToCheck), index);
889 state.checkedTotal -= countToCheck;
892 m_backtrackRecords.append(AlternativeBacktrackRecord(dataLabel, backtrackLocation));
894 // We fall through to here when the last alternative fails.
895 // Add a backtrack out of here for the parenthese handling code to link up.
896 state.addBacktrackJump(jump());
898 // Generate a trampoline for the parens code to backtrack to, to retry the
900 state.setBacktrackGenerated(label());
901 loadFromFrameAndJump(alternativeFrameLocation);
903 // FIXME: both of the above hooks are a little inefficient, in that you
904 // may end up trampolining here, just to trampoline back out to the
905 // parentheses code, or vice versa. We can probably eliminate a jump
906 // by restructuring, but coding this way for now for simplicity during
909 successes.link(this);
913 void generateParenthesesSingle(TermGenerationState& state)
915 const RegisterID indexTemporary = regT0;
916 PatternTerm& term = state.term();
917 PatternDisjunction* disjunction = term.parentheses.disjunction;
918 ASSERT(term.quantityCount == 1);
920 unsigned preCheckedCount = (term.quantityType == QuantifierFixedCount) ? disjunction->m_minimumSize : 0;
922 unsigned parenthesesFrameLocation = term.frameLocation;
923 unsigned alternativeFrameLocation = parenthesesFrameLocation;
924 if (term.quantityType != QuantifierFixedCount)
925 alternativeFrameLocation += RegexStackSpaceForBackTrackInfoParenthesesOnce;
927 // optimized case - no capture & no quantifier can be handled in a light-weight manner.
928 if (!term.invertOrCapture && (term.quantityType == QuantifierFixedCount)) {
929 TermGenerationState parenthesesState(disjunction, state.checkedTotal);
930 generateParenthesesDisjunction(state.term(), parenthesesState, alternativeFrameLocation);
931 // this expects that any backtracks back out of the parentheses will be in the
932 // parenthesesState's backTrackJumps vector, and that if they need backtracking
933 // they will have set an entry point on the parenthesesState's backtrackLabel.
934 state.propagateBacktrackingFrom(parenthesesState, this);
936 Jump nonGreedySkipParentheses;
937 Label nonGreedyTryParentheses;
938 if (term.quantityType == QuantifierGreedy)
939 storeToFrame(index, parenthesesFrameLocation);
940 else if (term.quantityType == QuantifierNonGreedy) {
941 storeToFrame(Imm32(-1), parenthesesFrameLocation);
942 nonGreedySkipParentheses = jump();
943 nonGreedyTryParentheses = label();
944 storeToFrame(index, parenthesesFrameLocation);
947 // store the match start index
948 if (term.invertOrCapture) {
949 int inputOffset = state.inputOffset() - preCheckedCount;
951 move(index, indexTemporary);
952 add32(Imm32(inputOffset), indexTemporary);
953 store32(indexTemporary, Address(output, (term.parentheses.subpatternId << 1) * sizeof(int)));
955 store32(index, Address(output, (term.parentheses.subpatternId << 1) * sizeof(int)));
958 // generate the body of the parentheses
959 TermGenerationState parenthesesState(disjunction, state.checkedTotal);
960 generateParenthesesDisjunction(state.term(), parenthesesState, alternativeFrameLocation);
962 Jump success = (term.quantityType == QuantifierFixedCount) ?
964 branch32(NotEqual, index, Address(stackPointerRegister, (parenthesesFrameLocation * sizeof(void*))));
966 // A failure AFTER the parens jumps here
967 Label backtrackFromAfterParens(this);
969 if (term.quantityType == QuantifierGreedy) {
970 // If this is -1 we have now tested with both with and without the parens.
971 loadFromFrame(parenthesesFrameLocation, indexTemporary);
972 state.jumpToBacktrack(branch32(Equal, indexTemporary, Imm32(-1)), this);
973 } else if (term.quantityType == QuantifierNonGreedy) {
974 // If this is -1 we have now tested without the parens, now test with.
975 loadFromFrame(parenthesesFrameLocation, indexTemporary);
976 branch32(Equal, indexTemporary, Imm32(-1)).linkTo(nonGreedyTryParentheses, this);
979 parenthesesState.plantJumpToBacktrackIfExists(this);
980 // A failure WITHIN the parens jumps here
981 parenthesesState.linkAlternativeBacktracks(this);
982 if (term.invertOrCapture) {
983 store32(Imm32(-1), Address(output, (term.parentheses.subpatternId << 1) * sizeof(int)));
985 store32(Imm32(-1), Address(output, ((term.parentheses.subpatternId << 1) + 1) * sizeof(int)));
989 if (term.quantityType == QuantifierGreedy)
990 storeToFrame(Imm32(-1), parenthesesFrameLocation);
992 state.jumpToBacktrack(jump(), this);
994 state.setBacktrackGenerated(backtrackFromAfterParens);
995 if (term.quantityType == QuantifierNonGreedy)
996 nonGreedySkipParentheses.link(this);
999 // store the match end index
1000 if (term.invertOrCapture) {
1001 int inputOffset = state.inputOffset();
1003 move(index, indexTemporary);
1004 add32(Imm32(state.inputOffset()), indexTemporary);
1005 store32(indexTemporary, Address(output, ((term.parentheses.subpatternId << 1) + 1) * sizeof(int)));
1007 store32(index, Address(output, ((term.parentheses.subpatternId << 1) + 1) * sizeof(int)));
1012 void generateParenthesesGreedyNoBacktrack(TermGenerationState& state)
1014 PatternTerm& parenthesesTerm = state.term();
1015 PatternDisjunction* disjunction = parenthesesTerm.parentheses.disjunction;
1016 ASSERT(parenthesesTerm.type == PatternTerm::TypeParenthesesSubpattern);
1017 ASSERT(parenthesesTerm.quantityCount != 1); // Handled by generateParenthesesSingle.
1019 TermGenerationState parenthesesState(disjunction, state.checkedTotal);
1021 Label matchAgain(this);
1023 storeToFrame(index, parenthesesTerm.frameLocation); // Save the current index to check for zero len matches later.
1025 for (parenthesesState.resetAlternative(); parenthesesState.alternativeValid(); parenthesesState.nextAlternative()) {
1027 PatternAlternative* alternative = parenthesesState.alternative();
1028 optimizeAlternative(alternative);
1030 int countToCheck = alternative->m_minimumSize;
1032 parenthesesState.addBacktrackJump(jumpIfNoAvailableInput(countToCheck));
1033 parenthesesState.checkedTotal += countToCheck;
1036 for (parenthesesState.resetTerm(); parenthesesState.termValid(); parenthesesState.nextTerm())
1037 generateTerm(parenthesesState);
1039 // If we get here, we matched! If the index advanced then try to match more since limit isn't supported yet.
1040 branch32(NotEqual, index, Address(stackPointerRegister, (parenthesesTerm.frameLocation * sizeof(void*))), matchAgain);
1042 // If we get here we matched, but we matched "" - cannot accept this alternative as is, so either backtrack,
1043 // or fall through to try the next alternative if no backtrack is available.
1044 parenthesesState.plantJumpToBacktrackIfExists(this);
1046 parenthesesState.linkAlternativeBacktracks(this);
1047 // We get here if the alternative fails to match - fall through to the next iteration, or out of the loop.
1050 sub32(Imm32(countToCheck), index);
1051 parenthesesState.checkedTotal -= countToCheck;
1055 // If the last alternative falls through to here, we have a failed match...
1056 // Which means that we match whatever we have matched up to this point (even if nothing).
1059 void generateParentheticalAssertion(TermGenerationState& state)
1061 PatternTerm& term = state.term();
1062 PatternDisjunction* disjunction = term.parentheses.disjunction;
1063 ASSERT(term.quantityCount == 1);
1064 ASSERT(term.quantityType == QuantifierFixedCount);
1066 unsigned parenthesesFrameLocation = term.frameLocation;
1067 unsigned alternativeFrameLocation = parenthesesFrameLocation + RegexStackSpaceForBackTrackInfoParentheticalAssertion;
1069 int countCheckedAfterAssertion = state.checkedTotal - term.inputPosition;
1071 if (term.invertOrCapture) {
1073 storeToFrame(index, parenthesesFrameLocation);
1075 state.checkedTotal -= countCheckedAfterAssertion;
1076 if (countCheckedAfterAssertion)
1077 sub32(Imm32(countCheckedAfterAssertion), index);
1079 TermGenerationState parenthesesState(disjunction, state.checkedTotal);
1080 generateParenthesesDisjunction(state.term(), parenthesesState, alternativeFrameLocation);
1081 // Success! - which means - Fail!
1082 loadFromFrame(parenthesesFrameLocation, index);
1083 state.jumpToBacktrack(jump(), this);
1085 // And fail means success.
1086 parenthesesState.linkAlternativeBacktracks(this);
1087 loadFromFrame(parenthesesFrameLocation, index);
1089 state.checkedTotal += countCheckedAfterAssertion;
1092 storeToFrame(index, parenthesesFrameLocation);
1094 state.checkedTotal -= countCheckedAfterAssertion;
1095 if (countCheckedAfterAssertion)
1096 sub32(Imm32(countCheckedAfterAssertion), index);
1098 TermGenerationState parenthesesState(disjunction, state.checkedTotal);
1099 generateParenthesesDisjunction(state.term(), parenthesesState, alternativeFrameLocation);
1100 // Success! - which means - Success!
1101 loadFromFrame(parenthesesFrameLocation, index);
1102 Jump success = jump();
1104 parenthesesState.linkAlternativeBacktracks(this);
1105 loadFromFrame(parenthesesFrameLocation, index);
1106 state.jumpToBacktrack(jump(), this);
1110 state.checkedTotal += countCheckedAfterAssertion;
1114 void generateTerm(TermGenerationState& state)
1116 PatternTerm& term = state.term();
1118 switch (term.type) {
1119 case PatternTerm::TypeAssertionBOL:
1120 generateAssertionBOL(state);
1123 case PatternTerm::TypeAssertionEOL:
1124 generateAssertionEOL(state);
1127 case PatternTerm::TypeAssertionWordBoundary:
1128 generateAssertionWordBoundary(state);
1131 case PatternTerm::TypePatternCharacter:
1132 switch (term.quantityType) {
1133 case QuantifierFixedCount:
1134 if (term.quantityCount == 1) {
1135 if (state.isSinglePatternCharacterLookaheadTerm() && (state.lookaheadTerm().inputPosition == (term.inputPosition + 1))) {
1136 generatePatternCharacterPair(state);
1139 generatePatternCharacterSingle(state);
1141 generatePatternCharacterFixed(state);
1143 case QuantifierGreedy:
1144 generatePatternCharacterGreedy(state);
1146 case QuantifierNonGreedy:
1147 generatePatternCharacterNonGreedy(state);
1152 case PatternTerm::TypeCharacterClass:
1153 switch (term.quantityType) {
1154 case QuantifierFixedCount:
1155 if (term.quantityCount == 1)
1156 generateCharacterClassSingle(state);
1158 generateCharacterClassFixed(state);
1160 case QuantifierGreedy:
1161 generateCharacterClassGreedy(state);
1163 case QuantifierNonGreedy:
1164 generateCharacterClassNonGreedy(state);
1169 case PatternTerm::TypeBackReference:
1170 m_shouldFallBack = true;
1173 case PatternTerm::TypeForwardReference:
1176 case PatternTerm::TypeParenthesesSubpattern:
1177 if (term.quantityCount == 1 && !term.parentheses.isCopy)
1178 generateParenthesesSingle(state);
1179 else if (term.parentheses.isTerminal)
1180 generateParenthesesGreedyNoBacktrack(state);
1182 m_shouldFallBack = true;
1185 case PatternTerm::TypeParentheticalAssertion:
1186 generateParentheticalAssertion(state);
1191 void generateDisjunction(PatternDisjunction* disjunction)
1193 TermGenerationState state(disjunction, 0);
1194 state.resetAlternative();
1196 // check availability for the next alternative
1197 int countCheckedForCurrentAlternative = 0;
1198 int countToCheckForFirstAlternative = 0;
1199 bool hasShorterAlternatives = false;
1200 bool setRepeatAlternativeLabels = false;
1201 JumpList notEnoughInputForPreviousAlternative;
1202 Label firstAlternative;
1203 Label firstAlternativeInputChecked;
1205 // The label 'firstAlternative' is used to plant a check to see if there is
1206 // sufficient input available to run the first repeating alternative.
1207 // The label 'firstAlternativeInputChecked' will jump directly to matching
1208 // the first repeating alternative having skipped this check.
1210 if (state.alternativeValid()) {
1211 PatternAlternative* alternative = state.alternative();
1212 if (!alternative->onceThrough()) {
1213 firstAlternative = Label(this);
1214 setRepeatAlternativeLabels = true;
1216 countToCheckForFirstAlternative = alternative->m_minimumSize;
1217 state.checkedTotal += countToCheckForFirstAlternative;
1218 if (countToCheckForFirstAlternative)
1219 notEnoughInputForPreviousAlternative.append(jumpIfNoAvailableInput(countToCheckForFirstAlternative));
1220 countCheckedForCurrentAlternative = countToCheckForFirstAlternative;
1223 if (setRepeatAlternativeLabels)
1224 firstAlternativeInputChecked = Label(this);
1226 while (state.alternativeValid()) {
1227 PatternAlternative* alternative = state.alternative();
1228 optimizeAlternative(alternative);
1230 // Track whether any alternatives are shorter than the first one.
1231 if (!alternative->onceThrough())
1232 hasShorterAlternatives = hasShorterAlternatives || (countCheckedForCurrentAlternative < countToCheckForFirstAlternative);
1234 for (state.resetTerm(); state.termValid(); state.nextTerm())
1235 generateTerm(state);
1237 // If we get here, the alternative matched.
1238 if (m_pattern.m_body->m_callFrameSize)
1239 addPtr(Imm32(m_pattern.m_body->m_callFrameSize * sizeof(void*)), stackPointerRegister);
1241 ASSERT(index != returnRegister);
1242 if (m_pattern.m_body->m_hasFixedSize) {
1243 move(index, returnRegister);
1244 if (alternative->m_minimumSize)
1245 sub32(Imm32(alternative->m_minimumSize), returnRegister);
1247 store32(returnRegister, output);
1249 load32(Address(output), returnRegister);
1251 store32(index, Address(output, 4));
1255 state.nextAlternative();
1257 // if there are any more alternatives, plant the check for input before looping.
1258 if (state.alternativeValid()) {
1259 PatternAlternative* nextAlternative = state.alternative();
1260 if (!setRepeatAlternativeLabels && !nextAlternative->onceThrough()) {
1261 // We have handled non-repeating alternatives, jump to next iteration
1262 // and loop over repeating alternatives.
1263 state.jumpToBacktrack(jump(), this);
1265 countToCheckForFirstAlternative = nextAlternative->m_minimumSize;
1267 // If we get here, there the last input checked failed.
1268 notEnoughInputForPreviousAlternative.link(this);
1270 state.linkAlternativeBacktracks(this);
1272 // Back up to start the looping alternatives.
1273 if (countCheckedForCurrentAlternative)
1274 sub32(Imm32(countCheckedForCurrentAlternative), index);
1276 firstAlternative = Label(this);
1278 state.checkedTotal = countToCheckForFirstAlternative;
1279 if (countToCheckForFirstAlternative)
1280 notEnoughInputForPreviousAlternative.append(jumpIfNoAvailableInput(countToCheckForFirstAlternative));
1282 countCheckedForCurrentAlternative = countToCheckForFirstAlternative;
1284 firstAlternativeInputChecked = Label(this);
1286 setRepeatAlternativeLabels = true;
1288 int countToCheckForNextAlternative = nextAlternative->m_minimumSize;
1290 if (countCheckedForCurrentAlternative > countToCheckForNextAlternative) { // CASE 1: current alternative was longer than the next one.
1291 // If we get here, then the last input checked failed.
1292 notEnoughInputForPreviousAlternative.link(this);
1294 // Check if sufficent input available to run the next alternative
1295 notEnoughInputForPreviousAlternative.append(jumpIfNoAvailableInput(countToCheckForNextAlternative - countCheckedForCurrentAlternative));
1296 // We are now in the correct state to enter the next alternative; this add is only required
1297 // to mirror and revert operation of the sub32, just below.
1298 add32(Imm32(countCheckedForCurrentAlternative - countToCheckForNextAlternative), index);
1300 // If we get here, then the last input checked passed.
1301 state.linkAlternativeBacktracks(this);
1302 // No need to check if we can run the next alternative, since it is shorter -
1303 // just update index.
1304 sub32(Imm32(countCheckedForCurrentAlternative - countToCheckForNextAlternative), index);
1305 } else if (countCheckedForCurrentAlternative < countToCheckForNextAlternative) { // CASE 2: next alternative is longer than the current one.
1306 // If we get here, then the last input checked failed.
1307 // If there is insufficient input to run the current alternative, and the next alternative is longer,
1308 // then there is definitely not enough input to run it - don't even check. Just adjust index, as if
1310 notEnoughInputForPreviousAlternative.link(this);
1311 add32(Imm32(countToCheckForNextAlternative - countCheckedForCurrentAlternative), index);
1312 notEnoughInputForPreviousAlternative.append(jump());
1314 // The next alternative is longer than the current one; check the difference.
1315 state.linkAlternativeBacktracks(this);
1316 notEnoughInputForPreviousAlternative.append(jumpIfNoAvailableInput(countToCheckForNextAlternative - countCheckedForCurrentAlternative));
1317 } else { // CASE 3: Both alternatives are the same length.
1318 ASSERT(countCheckedForCurrentAlternative == countToCheckForNextAlternative);
1320 // If the next alterative is the same length as this one, then no need to check the input -
1321 // if there was sufficent input to run the current alternative then there is sufficient
1322 // input to run the next one; if not, there isn't.
1323 state.linkAlternativeBacktracks(this);
1325 state.checkedTotal -= countCheckedForCurrentAlternative;
1326 countCheckedForCurrentAlternative = countToCheckForNextAlternative;
1327 state.checkedTotal += countCheckedForCurrentAlternative;
1332 // If we get here, all Alternatives failed...
1334 state.checkedTotal -= countCheckedForCurrentAlternative;
1336 if (!setRepeatAlternativeLabels) {
1337 // If there are no alternatives that need repeating (all are marked 'onceThrough') then just link
1338 // the match failures to this point, and fall through to the return below.
1339 state.linkAlternativeBacktracks(this);
1340 notEnoughInputForPreviousAlternative.link(this);
1342 // How much more input need there be to be able to retry from the first alternative?
1344 // /yarr_jit/ or /wrec|pcre/
1345 // In these examples we need check for one more input before looping.
1347 // In this case we need check for 5 more input to loop (+4 to allow for the first alterative
1348 // being four longer than the last alternative checked, and another +1 to effectively move
1349 // the start position along by one).
1350 // /yarr|rules/ or /wrec|notsomuch/
1351 // In these examples, provided that there was sufficient input to have just been matching for
1352 // the second alternative we can loop without checking for available input (since the second
1353 // alternative is longer than the first). In the latter example we need to decrement index
1354 // (by 4) so the start position is only progressed by 1 from the last iteration.
1355 int incrementForNextIter = (countToCheckForFirstAlternative - countCheckedForCurrentAlternative) + 1;
1357 // First, deal with the cases where there was sufficient input to try the last alternative.
1358 if (incrementForNextIter > 0) // We need to check for more input anyway, fall through to the checking below.
1359 state.linkAlternativeBacktracks(this);
1360 else if (m_pattern.m_body->m_hasFixedSize && !incrementForNextIter) // No need to update anything, link these backtracks straight to the to pof the loop!
1361 state.linkAlternativeBacktracksTo(firstAlternativeInputChecked, this);
1362 else { // no need to check the input, but we do have some bookkeeping to do first.
1363 state.linkAlternativeBacktracks(this);
1365 // Where necessary update our preserved start position.
1366 if (!m_pattern.m_body->m_hasFixedSize) {
1368 sub32(Imm32(countCheckedForCurrentAlternative - 1), regT0);
1369 store32(regT0, Address(output));
1372 // Update index if necessary, and loop (without checking).
1373 if (incrementForNextIter)
1374 add32(Imm32(incrementForNextIter), index);
1375 jump().linkTo(firstAlternativeInputChecked, this);
1378 notEnoughInputForPreviousAlternative.link(this);
1379 // Update our idea of the start position, if we're tracking this.
1380 if (!m_pattern.m_body->m_hasFixedSize) {
1381 if (countCheckedForCurrentAlternative - 1) {
1383 sub32(Imm32(countCheckedForCurrentAlternative - 1), regT0);
1384 store32(regT0, Address(output));
1386 store32(index, Address(output));
1389 // Check if there is sufficent input to run the first alternative again.
1390 jumpIfAvailableInput(incrementForNextIter).linkTo(firstAlternativeInputChecked, this);
1391 // No - insufficent input to run the first alteranative, are there any other alternatives we
1392 // might need to check? If so, the last check will have left the index incremented by
1393 // (countToCheckForFirstAlternative + 1), so we need test whether countToCheckForFirstAlternative
1394 // LESS input is available, to have the effect of just progressing the start position by 1
1395 // from the last iteration. If this check passes we can just jump up to the check associated
1396 // with the first alternative in the loop. This is a bit sad, since we'll end up trying the
1397 // first alternative again, and this check will fail (otherwise the check planted just above
1398 // here would have passed). This is a bit sad, however it saves trying to do something more
1399 // complex here in compilation, and in the common case we should end up coallescing the checks.
1401 // FIXME: a nice improvement here may be to stop trying to match sooner, based on the least
1402 // of the minimum-alternative-lengths. E.g. if I have two alternatives of length 200 and 150,
1403 // and a string of length 100, we'll end up looping index from 0 to 100, checking whether there
1404 // is sufficient input to run either alternative (constantly failing). If there had been only
1405 // one alternative, or if the shorter alternative had come first, we would have terminated
1407 if (hasShorterAlternatives)
1408 jumpIfAvailableInput(-countToCheckForFirstAlternative).linkTo(firstAlternative, this);
1409 // index will now be a bit garbled (depending on whether 'hasShorterAlternatives' is true,
1410 // it has either been incremented by 1 or by (countToCheckForFirstAlternative + 1) ...
1411 // but since we're about to return a failure this doesn't really matter!)
1414 if (m_pattern.m_body->m_callFrameSize)
1415 addPtr(Imm32(m_pattern.m_body->m_callFrameSize * sizeof(void*)), stackPointerRegister);
1417 move(Imm32(-1), returnRegister);
1422 void generateEnter()
1425 push(X86Registers::ebp);
1426 move(stackPointerRegister, X86Registers::ebp);
1427 push(X86Registers::ebx);
1429 push(X86Registers::ebp);
1430 move(stackPointerRegister, X86Registers::ebp);
1431 // TODO: do we need spill registers to fill the output pointer if there are no sub captures?
1432 push(X86Registers::ebx);
1433 push(X86Registers::edi);
1434 push(X86Registers::esi);
1435 // load output into edi (2 = saved ebp + return address).
1436 #if WTF_COMPILER_MSVC || WTF_COMPILER_SUNPRO
1437 loadPtr(Address(X86Registers::ebp, 2 * sizeof(void*)), input);
1438 loadPtr(Address(X86Registers::ebp, 3 * sizeof(void*)), index);
1439 loadPtr(Address(X86Registers::ebp, 4 * sizeof(void*)), length);
1440 loadPtr(Address(X86Registers::ebp, 5 * sizeof(void*)), output);
1442 loadPtr(Address(X86Registers::ebp, 2 * sizeof(void*)), output);
1445 push(ARMRegisters::r4);
1446 push(ARMRegisters::r5);
1447 push(ARMRegisters::r6);
1448 #if WTF_CPU_ARM_TRADITIONAL
1449 push(ARMRegisters::r8); // scratch register
1451 move(ARMRegisters::r3, output);
1457 void generateReturn()
1460 pop(X86Registers::ebx);
1461 pop(X86Registers::ebp);
1463 pop(X86Registers::esi);
1464 pop(X86Registers::edi);
1465 pop(X86Registers::ebx);
1466 pop(X86Registers::ebp);
1468 #if WTF_CPU_ARM_TRADITIONAL
1469 pop(ARMRegisters::r8); // scratch register
1471 pop(ARMRegisters::r6);
1472 pop(ARMRegisters::r5);
1473 pop(ARMRegisters::r4);
1481 RegexGenerator(RegexPattern& pattern)
1482 : m_pattern(pattern)
1483 , m_shouldFallBack(false)
1491 if (!m_pattern.m_body->m_hasFixedSize)
1492 store32(index, Address(output));
1494 if (m_pattern.m_body->m_callFrameSize)
1495 subPtr(Imm32(m_pattern.m_body->m_callFrameSize * sizeof(void*)), stackPointerRegister);
1497 generateDisjunction(m_pattern.m_body);
1500 void compile(ExecutableAllocator& allocator, RegexCodeBlock& jitObject)
1505 m_shouldFallBack = true;
1509 ExecutablePool *executablePool = allocator.poolForSize(size());
1510 if (!executablePool) {
1511 m_shouldFallBack = true;
1515 LinkBuffer patchBuffer(this, executablePool);
1517 for (unsigned i = 0; i < m_backtrackRecords.length(); ++i)
1518 patchBuffer.patch(m_backtrackRecords[i].dataLabel, patchBuffer.locationOf(m_backtrackRecords[i].backtrackLocation));
1520 jitObject.set(patchBuffer.finalizeCode());
1523 bool shouldFallBack()
1525 return m_shouldFallBack;
1529 RegexPattern& m_pattern;
1530 bool m_shouldFallBack;
1531 js::Vector<AlternativeBacktrackRecord, 0, js::SystemAllocPolicy> m_backtrackRecords;
1534 void jitCompileRegex(ExecutableAllocator& allocator, RegexCodeBlock& jitObject, const UString&patternString, unsigned& numSubpatterns, int &error, bool &fellBack, bool ignoreCase, bool multiline
1536 , bool forceFallback
1541 if (!forceFallback) {
1544 RegexPattern pattern(ignoreCase, multiline);
1545 if ((error = compileRegex(patternString, pattern)))
1547 numSubpatterns = pattern.m_numSubpatterns;
1549 if (!pattern.m_containsBackreferences) {
1550 RegexGenerator generator(pattern);
1551 generator.compile(allocator, jitObject);
1552 if (!generator.shouldFallBack())
1560 JSRegExpIgnoreCaseOption ignoreCaseOption = ignoreCase ? JSRegExpIgnoreCase : JSRegExpDoNotIgnoreCase;
1561 JSRegExpMultilineOption multilineOption = multiline ? JSRegExpMultiline : JSRegExpSingleLine;
1562 jitObject.setFallback(jsRegExpCompile(reinterpret_cast<const UChar*>(const_cast<UString &>(patternString).chars()), patternString.length(), ignoreCaseOption, multilineOption, &numSubpatterns, &error));