OLD | NEW |
1 // Copyright 2012 the V8 project authors. All rights reserved. | 1 // Copyright 2012 the V8 project authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "src/v8.h" | 5 #include "src/v8.h" |
6 | 6 |
7 #include "src/ast.h" | 7 #include "src/ast.h" |
8 #include "src/base/platform/platform.h" | 8 #include "src/base/platform/platform.h" |
9 #include "src/compilation-cache.h" | 9 #include "src/compilation-cache.h" |
10 #include "src/compiler.h" | 10 #include "src/compiler.h" |
(...skipping 3475 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3486 void LoopChoiceNode::AddContinueAlternative(GuardedAlternative alt) { | 3486 void LoopChoiceNode::AddContinueAlternative(GuardedAlternative alt) { |
3487 DCHECK_EQ(continue_node_, NULL); | 3487 DCHECK_EQ(continue_node_, NULL); |
3488 AddAlternative(alt); | 3488 AddAlternative(alt); |
3489 continue_node_ = alt.node(); | 3489 continue_node_ = alt.node(); |
3490 } | 3490 } |
3491 | 3491 |
3492 | 3492 |
3493 void LoopChoiceNode::Emit(RegExpCompiler* compiler, Trace* trace) { | 3493 void LoopChoiceNode::Emit(RegExpCompiler* compiler, Trace* trace) { |
3494 RegExpMacroAssembler* macro_assembler = compiler->macro_assembler(); | 3494 RegExpMacroAssembler* macro_assembler = compiler->macro_assembler(); |
3495 if (trace->stop_node() == this) { | 3495 if (trace->stop_node() == this) { |
| 3496 // Back edge of greedy optimized loop node graph. |
3496 int text_length = | 3497 int text_length = |
3497 GreedyLoopTextLengthForAlternative(&(alternatives_->at(0))); | 3498 GreedyLoopTextLengthForAlternative(&(alternatives_->at(0))); |
3498 DCHECK(text_length != kNodeIsTooComplexForGreedyLoops); | 3499 DCHECK(text_length != kNodeIsTooComplexForGreedyLoops); |
3499 // Update the counter-based backtracking info on the stack. This is an | 3500 // Update the counter-based backtracking info on the stack. This is an |
3500 // optimization for greedy loops (see below). | 3501 // optimization for greedy loops (see below). |
3501 DCHECK(trace->cp_offset() == text_length); | 3502 DCHECK(trace->cp_offset() == text_length); |
3502 macro_assembler->AdvanceCurrentPosition(text_length); | 3503 macro_assembler->AdvanceCurrentPosition(text_length); |
3503 macro_assembler->GoTo(trace->loop_label()); | 3504 macro_assembler->GoTo(trace->loop_label()); |
3504 return; | 3505 return; |
3505 } | 3506 } |
(...skipping 246 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3752 boolean_skip_table->set(j, kDontSkipArrayEntry); | 3753 boolean_skip_table->set(j, kDontSkipArrayEntry); |
3753 } | 3754 } |
3754 } | 3755 } |
3755 } | 3756 } |
3756 | 3757 |
3757 return skip; | 3758 return skip; |
3758 } | 3759 } |
3759 | 3760 |
3760 | 3761 |
3761 // See comment above on the implementation of GetSkipTable. | 3762 // See comment above on the implementation of GetSkipTable. |
3762 bool BoyerMooreLookahead::EmitSkipInstructions(RegExpMacroAssembler* masm) { | 3763 void BoyerMooreLookahead::EmitSkipInstructions(RegExpMacroAssembler* masm) { |
3763 const int kSize = RegExpMacroAssembler::kTableSize; | 3764 const int kSize = RegExpMacroAssembler::kTableSize; |
3764 | 3765 |
3765 int min_lookahead = 0; | 3766 int min_lookahead = 0; |
3766 int max_lookahead = 0; | 3767 int max_lookahead = 0; |
3767 | 3768 |
3768 if (!FindWorthwhileInterval(&min_lookahead, &max_lookahead)) return false; | 3769 if (!FindWorthwhileInterval(&min_lookahead, &max_lookahead)) return; |
3769 | 3770 |
3770 bool found_single_character = false; | 3771 bool found_single_character = false; |
3771 int single_character = 0; | 3772 int single_character = 0; |
3772 for (int i = max_lookahead; i >= min_lookahead; i--) { | 3773 for (int i = max_lookahead; i >= min_lookahead; i--) { |
3773 BoyerMoorePositionInfo* map = bitmaps_->at(i); | 3774 BoyerMoorePositionInfo* map = bitmaps_->at(i); |
3774 if (map->map_count() > 1 || | 3775 if (map->map_count() > 1 || |
3775 (found_single_character && map->map_count() != 0)) { | 3776 (found_single_character && map->map_count() != 0)) { |
3776 found_single_character = false; | 3777 found_single_character = false; |
3777 break; | 3778 break; |
3778 } | 3779 } |
3779 for (int j = 0; j < kSize; j++) { | 3780 for (int j = 0; j < kSize; j++) { |
3780 if (map->at(j)) { | 3781 if (map->at(j)) { |
3781 found_single_character = true; | 3782 found_single_character = true; |
3782 single_character = j; | 3783 single_character = j; |
3783 break; | 3784 break; |
3784 } | 3785 } |
3785 } | 3786 } |
3786 } | 3787 } |
3787 | 3788 |
3788 int lookahead_width = max_lookahead + 1 - min_lookahead; | 3789 int lookahead_width = max_lookahead + 1 - min_lookahead; |
3789 | 3790 |
3790 if (found_single_character && lookahead_width == 1 && max_lookahead < 3) { | 3791 if (found_single_character && lookahead_width == 1 && max_lookahead < 3) { |
3791 // The mask-compare can probably handle this better. | 3792 // The mask-compare can probably handle this better. |
3792 return false; | 3793 return; |
3793 } | 3794 } |
3794 | 3795 |
3795 if (found_single_character) { | 3796 if (found_single_character) { |
3796 Label cont, again; | 3797 Label cont, again; |
3797 masm->Bind(&again); | 3798 masm->Bind(&again); |
3798 masm->LoadCurrentCharacter(max_lookahead, &cont, true); | 3799 masm->LoadCurrentCharacter(max_lookahead, &cont, true); |
3799 if (max_char_ > kSize) { | 3800 if (max_char_ > kSize) { |
3800 masm->CheckCharacterAfterAnd(single_character, | 3801 masm->CheckCharacterAfterAnd(single_character, |
3801 RegExpMacroAssembler::kTableMask, | 3802 RegExpMacroAssembler::kTableMask, |
3802 &cont); | 3803 &cont); |
3803 } else { | 3804 } else { |
3804 masm->CheckCharacter(single_character, &cont); | 3805 masm->CheckCharacter(single_character, &cont); |
3805 } | 3806 } |
3806 masm->AdvanceCurrentPosition(lookahead_width); | 3807 masm->AdvanceCurrentPosition(lookahead_width); |
3807 masm->GoTo(&again); | 3808 masm->GoTo(&again); |
3808 masm->Bind(&cont); | 3809 masm->Bind(&cont); |
3809 return true; | 3810 return; |
3810 } | 3811 } |
3811 | 3812 |
3812 Factory* factory = masm->zone()->isolate()->factory(); | 3813 Factory* factory = masm->zone()->isolate()->factory(); |
3813 Handle<ByteArray> boolean_skip_table = factory->NewByteArray(kSize, TENURED); | 3814 Handle<ByteArray> boolean_skip_table = factory->NewByteArray(kSize, TENURED); |
3814 int skip_distance = GetSkipTable( | 3815 int skip_distance = GetSkipTable( |
3815 min_lookahead, max_lookahead, boolean_skip_table); | 3816 min_lookahead, max_lookahead, boolean_skip_table); |
3816 DCHECK(skip_distance != 0); | 3817 DCHECK(skip_distance != 0); |
3817 | 3818 |
3818 Label cont, again; | 3819 Label cont, again; |
3819 masm->Bind(&again); | 3820 masm->Bind(&again); |
3820 masm->LoadCurrentCharacter(max_lookahead, &cont, true); | 3821 masm->LoadCurrentCharacter(max_lookahead, &cont, true); |
3821 masm->CheckBitInTable(boolean_skip_table, &cont); | 3822 masm->CheckBitInTable(boolean_skip_table, &cont); |
3822 masm->AdvanceCurrentPosition(skip_distance); | 3823 masm->AdvanceCurrentPosition(skip_distance); |
3823 masm->GoTo(&again); | 3824 masm->GoTo(&again); |
3824 masm->Bind(&cont); | 3825 masm->Bind(&cont); |
3825 | |
3826 return true; | |
3827 } | 3826 } |
3828 | 3827 |
3829 | 3828 |
3830 /* Code generation for choice nodes. | 3829 /* Code generation for choice nodes. |
3831 * | 3830 * |
3832 * We generate quick checks that do a mask and compare to eliminate a | 3831 * We generate quick checks that do a mask and compare to eliminate a |
3833 * choice. If the quick check succeeds then it jumps to the continuation to | 3832 * choice. If the quick check succeeds then it jumps to the continuation to |
3834 * do slow checks and check subsequent nodes. If it fails (the common case) | 3833 * do slow checks and check subsequent nodes. If it fails (the common case) |
3835 * it falls through to the next choice. | 3834 * it falls through to the next choice. |
3836 * | 3835 * |
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3898 * | F/ | | 3897 * | F/ | |
3899 * | / | | 3898 * | / | |
3900 * | R | | 3899 * | R | |
3901 * | / | | 3900 * | / | |
3902 * F VL | | 3901 * F VL | |
3903 * <------U | | 3902 * <------U | |
3904 * back |S | | 3903 * back |S | |
3905 * \______________/ | 3904 * \______________/ |
3906 */ | 3905 */ |
3907 | 3906 |
3908 void ChoiceNode::Emit(RegExpCompiler* compiler, Trace* trace) { | 3907 GreedyLoopState::GreedyLoopState(bool not_at_start) { |
3909 RegExpMacroAssembler* macro_assembler = compiler->macro_assembler(); | 3908 counter_backtrack_trace_.set_backtrack(&label_); |
| 3909 if (not_at_start) counter_backtrack_trace_.set_at_start(false); |
| 3910 } |
| 3911 |
| 3912 |
| 3913 void ChoiceNode::AssertGuardsMentionRegisters(Trace* trace) { |
| 3914 #ifdef DEBUG |
3910 int choice_count = alternatives_->length(); | 3915 int choice_count = alternatives_->length(); |
3911 #ifdef DEBUG | |
3912 for (int i = 0; i < choice_count - 1; i++) { | 3916 for (int i = 0; i < choice_count - 1; i++) { |
3913 GuardedAlternative alternative = alternatives_->at(i); | 3917 GuardedAlternative alternative = alternatives_->at(i); |
3914 ZoneList<Guard*>* guards = alternative.guards(); | 3918 ZoneList<Guard*>* guards = alternative.guards(); |
3915 int guard_count = (guards == NULL) ? 0 : guards->length(); | 3919 int guard_count = (guards == NULL) ? 0 : guards->length(); |
3916 for (int j = 0; j < guard_count; j++) { | 3920 for (int j = 0; j < guard_count; j++) { |
3917 DCHECK(!trace->mentions_reg(guards->at(j)->reg())); | 3921 DCHECK(!trace->mentions_reg(guards->at(j)->reg())); |
3918 } | 3922 } |
3919 } | 3923 } |
3920 #endif | 3924 #endif |
| 3925 } |
| 3926 |
| 3927 |
| 3928 void ChoiceNode::SetUpPreLoad(RegExpCompiler* compiler, |
| 3929 Trace* current_trace, |
| 3930 PreloadState* state) { |
| 3931 if (state->eats_at_least_ == PreloadState::kEatsAtLeastNotYetInitialized) { |
| 3932 // Save some time by looking at most one machine word ahead. |
| 3933 state->eats_at_least_ = |
| 3934 EatsAtLeast(compiler->ascii() ? 4 : 2, |
| 3935 kRecursionBudget, |
| 3936 current_trace->at_start() == Trace::FALSE_VALUE); |
| 3937 } |
| 3938 state->preload_characters_ = |
| 3939 CalculatePreloadCharacters(compiler, state->eats_at_least_); |
| 3940 |
| 3941 state->preload_is_current_ = |
| 3942 (current_trace->characters_preloaded() == state->preload_characters_); |
| 3943 state->preload_has_checked_bounds_ = state->preload_is_current_; |
| 3944 } |
| 3945 |
| 3946 |
| 3947 void ChoiceNode::Emit(RegExpCompiler* compiler, Trace* trace) { |
| 3948 int choice_count = alternatives_->length(); |
| 3949 |
| 3950 AssertGuardsMentionRegisters(trace); |
3921 | 3951 |
3922 LimitResult limit_result = LimitVersions(compiler, trace); | 3952 LimitResult limit_result = LimitVersions(compiler, trace); |
3923 if (limit_result == DONE) return; | 3953 if (limit_result == DONE) return; |
3924 DCHECK(limit_result == CONTINUE); | 3954 DCHECK(limit_result == CONTINUE); |
3925 | 3955 |
3926 int new_flush_budget = trace->flush_budget() / choice_count; | 3956 // For loop nodes we already flushed (see LoopChoiceNode::Emit), but for |
| 3957 // other choice nodes we only flush if we are out of code size budget. |
3927 if (trace->flush_budget() == 0 && trace->actions() != NULL) { | 3958 if (trace->flush_budget() == 0 && trace->actions() != NULL) { |
3928 trace->Flush(compiler, this); | 3959 trace->Flush(compiler, this); |
3929 return; | 3960 return; |
3930 } | 3961 } |
3931 | 3962 |
3932 RecursionCheck rc(compiler); | 3963 RecursionCheck rc(compiler); |
3933 | 3964 |
3934 Trace* current_trace = trace; | 3965 PreloadState preload; |
3935 | 3966 preload.init(); |
3936 int text_length = GreedyLoopTextLengthForAlternative(&(alternatives_->at(0))); | 3967 GreedyLoopState greedy_loop_state(not_at_start()); |
3937 bool greedy_loop = false; | 3968 |
3938 Label greedy_loop_label; | 3969 int text_length = GreedyLoopTextLengthForAlternative(&alternatives_->at(0)); |
3939 Trace counter_backtrack_trace; | 3970 AlternativeGenerationList alt_gens(choice_count, zone()); |
3940 counter_backtrack_trace.set_backtrack(&greedy_loop_label); | |
3941 if (not_at_start()) counter_backtrack_trace.set_at_start(false); | |
3942 | 3971 |
3943 if (choice_count > 1 && text_length != kNodeIsTooComplexForGreedyLoops) { | 3972 if (choice_count > 1 && text_length != kNodeIsTooComplexForGreedyLoops) { |
3944 // Here we have special handling for greedy loops containing only text nodes | 3973 trace = EmitGreedyLoop(compiler, |
3945 // and other simple nodes. These are handled by pushing the current | 3974 trace, |
3946 // position on the stack and then incrementing the current position each | 3975 &alt_gens, |
3947 // time around the switch. On backtrack we decrement the current position | 3976 &preload, |
3948 // and check it against the pushed value. This avoids pushing backtrack | 3977 &greedy_loop_state, |
3949 // information for each iteration of the loop, which could take up a lot of | 3978 text_length); |
3950 // space. | 3979 } else { |
3951 greedy_loop = true; | 3980 // TODO(erikcorry): Delete this. We don't need this label, but it makes us |
3952 DCHECK(trace->stop_node() == NULL); | 3981 // match the traces produced pre-cleanup. |
3953 macro_assembler->PushCurrentPosition(); | 3982 Label second_choice; |
3954 current_trace = &counter_backtrack_trace; | 3983 compiler->macro_assembler()->Bind(&second_choice); |
3955 Label greedy_match_failed; | 3984 |
3956 Trace greedy_match_trace; | 3985 preload.eats_at_least_ = EmitOptimizedUnanchoredSearch(compiler, trace); |
3957 if (not_at_start()) greedy_match_trace.set_at_start(false); | 3986 |
3958 greedy_match_trace.set_backtrack(&greedy_match_failed); | 3987 EmitChoices(compiler, |
3959 Label loop_label; | 3988 &alt_gens, |
3960 macro_assembler->Bind(&loop_label); | 3989 0, |
3961 greedy_match_trace.set_stop_node(this); | 3990 trace, |
3962 greedy_match_trace.set_loop_label(&loop_label); | 3991 false, // Not greedy loop. |
3963 alternatives_->at(0).node()->Emit(compiler, &greedy_match_trace); | 3992 &preload); |
3964 macro_assembler->Bind(&greedy_match_failed); | 3993 } |
3965 } | 3994 |
| 3995 // At this point we need to generate slow checks for the alternatives where |
| 3996 // the quick check was inlined. We can recognize these because the associated |
| 3997 // label was bound. |
| 3998 int new_flush_budget = trace->flush_budget() / choice_count; |
| 3999 for (int i = 0; i < choice_count - 1; i++) { |
| 4000 AlternativeGeneration* alt_gen = alt_gens.at(i); |
| 4001 Trace new_trace(*trace); |
| 4002 // If there are actions to be flushed we have to limit how many times |
| 4003 // they are flushed. Take the budget of the parent trace and distribute |
| 4004 // it fairly amongst the children. |
| 4005 if (new_trace.actions() != NULL) { |
| 4006 new_trace.set_flush_budget(new_flush_budget); |
| 4007 } |
| 4008 EmitOutOfLineContinuation(compiler, |
| 4009 &new_trace, |
| 4010 alternatives_->at(i), |
| 4011 alt_gen, |
| 4012 preload.preload_characters_, |
| 4013 alt_gens.at(i + 1)->expects_preload); |
| 4014 } |
| 4015 } |
| 4016 |
| 4017 |
| 4018 Trace* ChoiceNode::EmitGreedyLoop(RegExpCompiler* compiler, |
| 4019 Trace* trace, |
| 4020 AlternativeGenerationList* alt_gens, |
| 4021 PreloadState* preload, |
| 4022 GreedyLoopState* greedy_loop_state, |
| 4023 int text_length) { |
| 4024 RegExpMacroAssembler* macro_assembler = compiler->macro_assembler(); |
| 4025 // Here we have special handling for greedy loops containing only text nodes |
| 4026 // and other simple nodes. These are handled by pushing the current |
| 4027 // position on the stack and then incrementing the current position each |
| 4028 // time around the switch. On backtrack we decrement the current position |
| 4029 // and check it against the pushed value. This avoids pushing backtrack |
| 4030 // information for each iteration of the loop, which could take up a lot of |
| 4031 // space. |
| 4032 DCHECK(trace->stop_node() == NULL); |
| 4033 macro_assembler->PushCurrentPosition(); |
| 4034 Label greedy_match_failed; |
| 4035 Trace greedy_match_trace; |
| 4036 if (not_at_start()) greedy_match_trace.set_at_start(false); |
| 4037 greedy_match_trace.set_backtrack(&greedy_match_failed); |
| 4038 Label loop_label; |
| 4039 macro_assembler->Bind(&loop_label); |
| 4040 greedy_match_trace.set_stop_node(this); |
| 4041 greedy_match_trace.set_loop_label(&loop_label); |
| 4042 alternatives_->at(0).node()->Emit(compiler, &greedy_match_trace); |
| 4043 macro_assembler->Bind(&greedy_match_failed); |
3966 | 4044 |
3967 Label second_choice; // For use in greedy matches. | 4045 Label second_choice; // For use in greedy matches. |
3968 macro_assembler->Bind(&second_choice); | 4046 macro_assembler->Bind(&second_choice); |
3969 | 4047 |
3970 int first_normal_choice = greedy_loop ? 1 : 0; | 4048 Trace* new_trace = greedy_loop_state->counter_backtrack_trace(); |
3971 | 4049 |
3972 bool not_at_start = current_trace->at_start() == Trace::FALSE_VALUE; | 4050 EmitChoices(compiler, |
3973 const int kEatsAtLeastNotYetInitialized = -1; | 4051 alt_gens, |
3974 int eats_at_least = kEatsAtLeastNotYetInitialized; | 4052 1, |
3975 | 4053 new_trace, |
3976 bool skip_was_emitted = false; | 4054 true, // Is greedy loop. |
3977 | 4055 preload); |
3978 if (!greedy_loop && choice_count == 2) { | 4056 |
3979 GuardedAlternative alt1 = alternatives_->at(1); | 4057 macro_assembler->Bind(greedy_loop_state->label()); |
3980 if (alt1.guards() == NULL || alt1.guards()->length() == 0) { | 4058 // If we have unwound to the bottom then backtrack. |
3981 RegExpNode* eats_anything_node = alt1.node(); | 4059 macro_assembler->CheckGreedyLoop(trace->backtrack()); |
3982 if (eats_anything_node->GetSuccessorOfOmnivorousTextNode(compiler) == | 4060 // Otherwise try the second priority at an earlier position. |
3983 this) { | 4061 macro_assembler->AdvanceCurrentPosition(-text_length); |
3984 // At this point we know that we are at a non-greedy loop that will eat | 4062 macro_assembler->GoTo(&second_choice); |
3985 // any character one at a time. Any non-anchored regexp has such a | 4063 return new_trace; |
3986 // loop prepended to it in order to find where it starts. We look for | 4064 } |
3987 // a pattern of the form ...abc... where we can look 6 characters ahead | 4065 |
3988 // and step forwards 3 if the character is not one of abc. Abc need | 4066 int ChoiceNode::EmitOptimizedUnanchoredSearch(RegExpCompiler* compiler, |
3989 // not be atoms, they can be any reasonably limited character class or | 4067 Trace* trace) { |
3990 // small alternation. | 4068 int eats_at_least = PreloadState::kEatsAtLeastNotYetInitialized; |
3991 DCHECK(trace->is_trivial()); // This is the case on LoopChoiceNodes. | 4069 if (alternatives_->length() != 2) return eats_at_least; |
3992 BoyerMooreLookahead* lookahead = bm_info(not_at_start); | 4070 |
3993 if (lookahead == NULL) { | 4071 GuardedAlternative alt1 = alternatives_->at(1); |
3994 eats_at_least = Min(kMaxLookaheadForBoyerMoore, | 4072 if (alt1.guards() != NULL && alt1.guards()->length() != 0) { |
3995 EatsAtLeast(kMaxLookaheadForBoyerMoore, | 4073 return eats_at_least; |
3996 kRecursionBudget, | 4074 } |
3997 not_at_start)); | 4075 RegExpNode* eats_anything_node = alt1.node(); |
3998 if (eats_at_least >= 1) { | 4076 if (eats_anything_node->GetSuccessorOfOmnivorousTextNode(compiler) != this) { |
3999 BoyerMooreLookahead* bm = | 4077 return eats_at_least; |
4000 new(zone()) BoyerMooreLookahead(eats_at_least, | 4078 } |
4001 compiler, | 4079 |
4002 zone()); | 4080 // Really we should be creating a new trace when we execute this function, |
4003 GuardedAlternative alt0 = alternatives_->at(0); | 4081 // but there is no need, because the code it generates cannot backtrack, and |
4004 alt0.node()->FillInBMInfo(0, kRecursionBudget, bm, not_at_start); | 4082 // we always arrive here with a trivial trace (since it's the entry to a |
4005 skip_was_emitted = bm->EmitSkipInstructions(macro_assembler); | 4083 // loop. That also implies that there are no preloaded characters, which is |
4006 } | 4084 // good, because it means we won't be violating any assumptions by |
4007 } else { | 4085 // overwriting those characters with new load instructions. |
4008 skip_was_emitted = lookahead->EmitSkipInstructions(macro_assembler); | 4086 DCHECK(trace->is_trivial()); |
4009 } | 4087 |
4010 } | 4088 RegExpMacroAssembler* macro_assembler = compiler->macro_assembler(); |
| 4089 // At this point we know that we are at a non-greedy loop that will eat |
| 4090 // any character one at a time. Any non-anchored regexp has such a |
| 4091 // loop prepended to it in order to find where it starts. We look for |
| 4092 // a pattern of the form ...abc... where we can look 6 characters ahead |
| 4093 // and step forwards 3 if the character is not one of abc. Abc need |
| 4094 // not be atoms, they can be any reasonably limited character class or |
| 4095 // small alternation. |
| 4096 BoyerMooreLookahead* bm = bm_info(false); |
| 4097 if (bm == NULL) { |
| 4098 eats_at_least = Min(kMaxLookaheadForBoyerMoore, |
| 4099 EatsAtLeast(kMaxLookaheadForBoyerMoore, |
| 4100 kRecursionBudget, |
| 4101 false)); |
| 4102 if (eats_at_least >= 1) { |
| 4103 bm = new(zone()) BoyerMooreLookahead(eats_at_least, |
| 4104 compiler, |
| 4105 zone()); |
| 4106 GuardedAlternative alt0 = alternatives_->at(0); |
| 4107 alt0.node()->FillInBMInfo(0, kRecursionBudget, bm, false); |
4011 } | 4108 } |
4012 } | 4109 } |
4013 | 4110 if (bm != NULL) { |
4014 if (eats_at_least == kEatsAtLeastNotYetInitialized) { | 4111 bm->EmitSkipInstructions(macro_assembler); |
4015 // Save some time by looking at most one machine word ahead. | 4112 } |
4016 eats_at_least = | 4113 return eats_at_least; |
4017 EatsAtLeast(compiler->ascii() ? 4 : 2, kRecursionBudget, not_at_start); | 4114 } |
4018 } | 4115 |
4019 int preload_characters = CalculatePreloadCharacters(compiler, eats_at_least); | 4116 |
4020 | 4117 void ChoiceNode::EmitChoices(RegExpCompiler* compiler, |
4021 bool preload_is_current = !skip_was_emitted && | 4118 AlternativeGenerationList* alt_gens, |
4022 (current_trace->characters_preloaded() == preload_characters); | 4119 int first_choice, |
4023 bool preload_has_checked_bounds = preload_is_current; | 4120 Trace* trace, |
4024 | 4121 bool is_greedy_loop, |
4025 AlternativeGenerationList alt_gens(choice_count, zone()); | 4122 PreloadState* preload) { |
| 4123 RegExpMacroAssembler* macro_assembler = compiler->macro_assembler(); |
| 4124 SetUpPreLoad(compiler, trace, preload); |
4026 | 4125 |
4027 // For now we just call all choices one after the other. The idea ultimately | 4126 // For now we just call all choices one after the other. The idea ultimately |
4028 // is to use the Dispatch table to try only the relevant ones. | 4127 // is to use the Dispatch table to try only the relevant ones. |
4029 for (int i = first_normal_choice; i < choice_count; i++) { | 4128 int choice_count = alternatives_->length(); |
| 4129 |
| 4130 int new_flush_budget = trace->flush_budget() / choice_count; |
| 4131 |
| 4132 for (int i = first_choice; i < choice_count; i++) { |
| 4133 bool is_last = i == choice_count - 1; |
4030 GuardedAlternative alternative = alternatives_->at(i); | 4134 GuardedAlternative alternative = alternatives_->at(i); |
4031 AlternativeGeneration* alt_gen = alt_gens.at(i); | 4135 AlternativeGeneration* alt_gen = alt_gens->at(i); |
4032 alt_gen->quick_check_details.set_characters(preload_characters); | 4136 alt_gen->quick_check_details.set_characters(preload->preload_characters_); |
4033 ZoneList<Guard*>* guards = alternative.guards(); | 4137 ZoneList<Guard*>* guards = alternative.guards(); |
4034 int guard_count = (guards == NULL) ? 0 : guards->length(); | 4138 int guard_count = (guards == NULL) ? 0 : guards->length(); |
4035 Trace new_trace(*current_trace); | 4139 Trace new_trace(*trace); |
4036 new_trace.set_characters_preloaded(preload_is_current ? | 4140 new_trace.set_characters_preloaded(preload->preload_is_current_ ? |
4037 preload_characters : | 4141 preload->preload_characters_ : |
4038 0); | 4142 0); |
4039 if (preload_has_checked_bounds) { | 4143 if (preload->preload_has_checked_bounds_) { |
4040 new_trace.set_bound_checked_up_to(preload_characters); | 4144 new_trace.set_bound_checked_up_to(preload->preload_characters_); |
4041 } | 4145 } |
4042 new_trace.quick_check_performed()->Clear(); | 4146 new_trace.quick_check_performed()->Clear(); |
4043 if (not_at_start_) new_trace.set_at_start(Trace::FALSE_VALUE); | 4147 if (not_at_start_) new_trace.set_at_start(Trace::FALSE_VALUE); |
4044 alt_gen->expects_preload = preload_is_current; | 4148 alt_gen->expects_preload = preload->preload_is_current_; |
4045 bool generate_full_check_inline = false; | 4149 bool generate_full_check_inline = false; |
4046 if (FLAG_regexp_optimization && | 4150 if (FLAG_regexp_optimization && |
4047 try_to_emit_quick_check_for_alternative(i) && | 4151 try_to_emit_quick_check_for_alternative(i == 0) && |
4048 alternative.node()->EmitQuickCheck(compiler, | 4152 alternative.node()->EmitQuickCheck(compiler, |
4049 &new_trace, | 4153 &new_trace, |
4050 preload_has_checked_bounds, | 4154 preload->preload_has_checked_bounds_, |
4051 &alt_gen->possible_success, | 4155 &alt_gen->possible_success, |
4052 &alt_gen->quick_check_details, | 4156 &alt_gen->quick_check_details, |
4053 i < choice_count - 1)) { | 4157 !is_last)) { |
4054 // Quick check was generated for this choice. | 4158 // Quick check was generated for this choice. |
4055 preload_is_current = true; | 4159 preload->preload_is_current_ = true; |
4056 preload_has_checked_bounds = true; | 4160 preload->preload_has_checked_bounds_ = true; |
4057 // On the last choice in the ChoiceNode we generated the quick | 4161 // On the last choice in the ChoiceNode we generated the quick |
4058 // check to fall through on possible success. So now we need to | 4162 // check to fall through on possible success. So now we need to |
4059 // generate the full check inline. | 4163 // generate the full check inline. |
4060 if (i == choice_count - 1) { | 4164 if (is_last) { |
4061 macro_assembler->Bind(&alt_gen->possible_success); | 4165 macro_assembler->Bind(&alt_gen->possible_success); |
4062 new_trace.set_quick_check_performed(&alt_gen->quick_check_details); | 4166 new_trace.set_quick_check_performed(&alt_gen->quick_check_details); |
4063 new_trace.set_characters_preloaded(preload_characters); | 4167 new_trace.set_characters_preloaded(preload->preload_characters_); |
4064 new_trace.set_bound_checked_up_to(preload_characters); | 4168 new_trace.set_bound_checked_up_to(preload->preload_characters_); |
4065 generate_full_check_inline = true; | 4169 generate_full_check_inline = true; |
4066 } | 4170 } |
4067 } else if (alt_gen->quick_check_details.cannot_match()) { | 4171 } else if (alt_gen->quick_check_details.cannot_match()) { |
4068 if (i == choice_count - 1 && !greedy_loop) { | 4172 if (is_last && !is_greedy_loop) { |
4069 macro_assembler->GoTo(trace->backtrack()); | 4173 macro_assembler->GoTo(trace->backtrack()); |
4070 } | 4174 } // Else just fall through to the next test. |
4071 continue; | 4175 continue; |
4072 } else { | 4176 } else { |
4073 // No quick check was generated. Put the full code here. | 4177 // No quick check was generated. Put the full code here. |
4074 // If this is not the first choice then there could be slow checks from | 4178 // If this is not the first choice then there could be slow checks from |
4075 // previous cases that go here when they fail. There's no reason to | 4179 // previous cases that go here when they fail. There's no reason to |
4076 // insist that they preload characters since the slow check we are about | 4180 // insist that they preload characters since the slow check we are about |
4077 // to generate probably can't use it. | 4181 // to generate probably can't use it. |
4078 if (i != first_normal_choice) { | 4182 if (i != first_choice) { |
4079 alt_gen->expects_preload = false; | 4183 alt_gen->expects_preload = false; |
4080 new_trace.InvalidateCurrentCharacter(); | 4184 new_trace.InvalidateCurrentCharacter(); |
4081 } | 4185 } |
4082 if (i < choice_count - 1) { | 4186 if (!is_last) { |
4083 new_trace.set_backtrack(&alt_gen->after); | 4187 new_trace.set_backtrack(&alt_gen->after); |
4084 } | 4188 } |
4085 generate_full_check_inline = true; | 4189 generate_full_check_inline = true; |
4086 } | 4190 } |
4087 if (generate_full_check_inline) { | 4191 if (generate_full_check_inline) { |
4088 if (new_trace.actions() != NULL) { | 4192 if (new_trace.actions() != NULL) { |
4089 new_trace.set_flush_budget(new_flush_budget); | 4193 new_trace.set_flush_budget(new_flush_budget); |
4090 } | 4194 } |
4091 for (int j = 0; j < guard_count; j++) { | 4195 for (int j = 0; j < guard_count; j++) { |
4092 GenerateGuard(macro_assembler, guards->at(j), &new_trace); | 4196 GenerateGuard(macro_assembler, guards->at(j), &new_trace); |
4093 } | 4197 } |
4094 alternative.node()->Emit(compiler, &new_trace); | 4198 alternative.node()->Emit(compiler, &new_trace); |
4095 preload_is_current = false; | 4199 preload->preload_is_current_ = false; |
4096 } | 4200 } |
4097 macro_assembler->Bind(&alt_gen->after); | 4201 macro_assembler->Bind(&alt_gen->after); |
4098 } | 4202 } |
4099 if (greedy_loop) { | |
4100 macro_assembler->Bind(&greedy_loop_label); | |
4101 // If we have unwound to the bottom then backtrack. | |
4102 macro_assembler->CheckGreedyLoop(trace->backtrack()); | |
4103 // Otherwise try the second priority at an earlier position. | |
4104 macro_assembler->AdvanceCurrentPosition(-text_length); | |
4105 macro_assembler->GoTo(&second_choice); | |
4106 } | |
4107 | |
4108 // At this point we need to generate slow checks for the alternatives where | |
4109 // the quick check was inlined. We can recognize these because the associated | |
4110 // label was bound. | |
4111 for (int i = first_normal_choice; i < choice_count - 1; i++) { | |
4112 AlternativeGeneration* alt_gen = alt_gens.at(i); | |
4113 Trace new_trace(*current_trace); | |
4114 // If there are actions to be flushed we have to limit how many times | |
4115 // they are flushed. Take the budget of the parent trace and distribute | |
4116 // it fairly amongst the children. | |
4117 if (new_trace.actions() != NULL) { | |
4118 new_trace.set_flush_budget(new_flush_budget); | |
4119 } | |
4120 EmitOutOfLineContinuation(compiler, | |
4121 &new_trace, | |
4122 alternatives_->at(i), | |
4123 alt_gen, | |
4124 preload_characters, | |
4125 alt_gens.at(i + 1)->expects_preload); | |
4126 } | |
4127 } | 4203 } |
4128 | 4204 |
4129 | 4205 |
4130 void ChoiceNode::EmitOutOfLineContinuation(RegExpCompiler* compiler, | 4206 void ChoiceNode::EmitOutOfLineContinuation(RegExpCompiler* compiler, |
4131 Trace* trace, | 4207 Trace* trace, |
4132 GuardedAlternative alternative, | 4208 GuardedAlternative alternative, |
4133 AlternativeGeneration* alt_gen, | 4209 AlternativeGeneration* alt_gen, |
4134 int preload_characters, | 4210 int preload_characters, |
4135 bool next_expects_preload) { | 4211 bool next_expects_preload) { |
4136 if (!alt_gen->possible_success.is_linked()) return; | 4212 if (!alt_gen->possible_success.is_linked()) return; |
(...skipping 1966 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
6103 } | 6179 } |
6104 | 6180 |
6105 return compiler.Assemble(¯o_assembler, | 6181 return compiler.Assemble(¯o_assembler, |
6106 node, | 6182 node, |
6107 data->capture_count, | 6183 data->capture_count, |
6108 pattern); | 6184 pattern); |
6109 } | 6185 } |
6110 | 6186 |
6111 | 6187 |
6112 }} // namespace v8::internal | 6188 }} // namespace v8::internal |
OLD | NEW |