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 2419 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2430 } | 2430 } |
2431 mask_ |= (pos->mask & char_mask) << char_shift; | 2431 mask_ |= (pos->mask & char_mask) << char_shift; |
2432 value_ |= (pos->value & char_mask) << char_shift; | 2432 value_ |= (pos->value & char_mask) << char_shift; |
2433 char_shift += asc ? 8 : 16; | 2433 char_shift += asc ? 8 : 16; |
2434 } | 2434 } |
2435 return found_useful_op; | 2435 return found_useful_op; |
2436 } | 2436 } |
2437 | 2437 |
2438 | 2438 |
2439 bool RegExpNode::EmitQuickCheck(RegExpCompiler* compiler, | 2439 bool RegExpNode::EmitQuickCheck(RegExpCompiler* compiler, |
| 2440 Trace* bounds_check_trace, |
2440 Trace* trace, | 2441 Trace* trace, |
2441 bool preload_has_checked_bounds, | 2442 bool preload_has_checked_bounds, |
2442 Label* on_possible_success, | 2443 Label* on_possible_success, |
2443 QuickCheckDetails* details, | 2444 QuickCheckDetails* details, |
2444 bool fall_through_on_failure) { | 2445 bool fall_through_on_failure) { |
2445 if (details->characters() == 0) return false; | 2446 if (details->characters() == 0) return false; |
2446 GetQuickCheckDetails( | 2447 GetQuickCheckDetails( |
2447 details, compiler, 0, trace->at_start() == Trace::FALSE_VALUE); | 2448 details, compiler, 0, trace->at_start() == Trace::FALSE_VALUE); |
2448 if (details->cannot_match()) return false; | 2449 if (details->cannot_match()) return false; |
2449 if (!details->Rationalize(compiler->one_byte())) return false; | 2450 if (!details->Rationalize(compiler->one_byte())) return false; |
2450 DCHECK(details->characters() == 1 || | 2451 DCHECK(details->characters() == 1 || |
2451 compiler->macro_assembler()->CanReadUnaligned()); | 2452 compiler->macro_assembler()->CanReadUnaligned()); |
2452 uint32_t mask = details->mask(); | 2453 uint32_t mask = details->mask(); |
2453 uint32_t value = details->value(); | 2454 uint32_t value = details->value(); |
2454 | 2455 |
2455 RegExpMacroAssembler* assembler = compiler->macro_assembler(); | 2456 RegExpMacroAssembler* assembler = compiler->macro_assembler(); |
2456 | 2457 |
2457 if (trace->characters_preloaded() != details->characters()) { | 2458 if (trace->characters_preloaded() != details->characters()) { |
| 2459 DCHECK(trace->cp_offset() == bounds_check_trace->cp_offset()); |
| 2460 // We are attempting to preload the minimum number of characters |
| 2461 // any choice would eat, so if the bounds check fails, then none of the |
| 2462 // choices can succeed, so we can just immediately backtrack, rather |
| 2463 // than go to the next choice. |
2458 assembler->LoadCurrentCharacter(trace->cp_offset(), | 2464 assembler->LoadCurrentCharacter(trace->cp_offset(), |
2459 trace->backtrack(), | 2465 bounds_check_trace->backtrack(), |
2460 !preload_has_checked_bounds, | 2466 !preload_has_checked_bounds, |
2461 details->characters()); | 2467 details->characters()); |
2462 } | 2468 } |
2463 | 2469 |
2464 | 2470 |
2465 bool need_mask = true; | 2471 bool need_mask = true; |
2466 | 2472 |
2467 if (details->characters() == 1) { | 2473 if (details->characters() == 1) { |
2468 // If number of characters preloaded is 1 then we used a byte or 16 bit | 2474 // If number of characters preloaded is 1 then we used a byte or 16 bit |
2469 // load so the value is already masked down. | 2475 // load so the value is already masked down. |
(...skipping 1378 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3848 * | | 3854 * | |
3849 * R | 3855 * R |
3850 * | | 3856 * | |
3851 * backtrack V | 3857 * backtrack V |
3852 * <----------Q4 | 3858 * <----------Q4 |
3853 * \ F | | 3859 * \ F | |
3854 * \ |S | 3860 * \ |S |
3855 * \ F V | 3861 * \ F V |
3856 * \-----S4 | 3862 * \-----S4 |
3857 * | 3863 * |
3858 * For greedy loops we reverse our expectation and expect to match rather | 3864 * For greedy loops we push the current position, then generate the code that |
3859 * than fail. Therefore we want the loop code to look like this (U is the | 3865 * eats the input specially in EmitGreedyLoop. The other choice (the |
3860 * unwind code that steps back in the greedy loop). The following alternatives | 3866 * continuation) is generated by the normal code in EmitChoices, and steps back |
3861 * look the same as above. | 3867 * in the input to the starting position when it fails to match. The loop code |
| 3868 * looks like this (U is the unwind code that steps back in the greedy loop). |
| 3869 * |
3862 * _____ | 3870 * _____ |
3863 * / \ | 3871 * / \ |
3864 * V | | 3872 * V | |
3865 * ----------> S1 | | 3873 * ----------> S1 | |
3866 * /| | | 3874 * /| | |
3867 * / |S | | 3875 * / |S | |
3868 * F/ \_____/ | 3876 * F/ \_____/ |
3869 * / | 3877 * / |
3870 * |<----------- | 3878 * |<----- |
3871 * | \ | 3879 * | \ |
3872 * V \ | 3880 * V |S |
3873 * Q2 ---> S2 \ | 3881 * Q2 ---> U----->backtrack |
3874 * | S / | | 3882 * | F / |
3875 * F| / | | 3883 * S| / |
3876 * | F/ | | 3884 * V F / |
3877 * | / | | 3885 * S2--/ |
3878 * | R | | |
3879 * | / | | |
3880 * F VL | | |
3881 * <------U | | |
3882 * back |S | | |
3883 * \______________/ | |
3884 */ | 3886 */ |
3885 | 3887 |
3886 GreedyLoopState::GreedyLoopState(bool not_at_start) { | 3888 GreedyLoopState::GreedyLoopState(bool not_at_start) { |
3887 counter_backtrack_trace_.set_backtrack(&label_); | 3889 counter_backtrack_trace_.set_backtrack(&label_); |
3888 if (not_at_start) counter_backtrack_trace_.set_at_start(false); | 3890 if (not_at_start) counter_backtrack_trace_.set_at_start(false); |
3889 } | 3891 } |
3890 | 3892 |
3891 | 3893 |
3892 void ChoiceNode::AssertGuardsMentionRegisters(Trace* trace) { | 3894 void ChoiceNode::AssertGuardsMentionRegisters(Trace* trace) { |
3893 #ifdef DEBUG | 3895 #ifdef DEBUG |
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3959 // match the traces produced pre-cleanup. | 3961 // match the traces produced pre-cleanup. |
3960 Label second_choice; | 3962 Label second_choice; |
3961 compiler->macro_assembler()->Bind(&second_choice); | 3963 compiler->macro_assembler()->Bind(&second_choice); |
3962 | 3964 |
3963 preload.eats_at_least_ = EmitOptimizedUnanchoredSearch(compiler, trace); | 3965 preload.eats_at_least_ = EmitOptimizedUnanchoredSearch(compiler, trace); |
3964 | 3966 |
3965 EmitChoices(compiler, | 3967 EmitChoices(compiler, |
3966 &alt_gens, | 3968 &alt_gens, |
3967 0, | 3969 0, |
3968 trace, | 3970 trace, |
3969 false, // Not greedy loop. | |
3970 &preload); | 3971 &preload); |
3971 } | 3972 } |
3972 | 3973 |
3973 // At this point we need to generate slow checks for the alternatives where | 3974 // At this point we need to generate slow checks for the alternatives where |
3974 // the quick check was inlined. We can recognize these because the associated | 3975 // the quick check was inlined. We can recognize these because the associated |
3975 // label was bound. | 3976 // label was bound. |
3976 int new_flush_budget = trace->flush_budget() / choice_count; | 3977 int new_flush_budget = trace->flush_budget() / choice_count; |
3977 for (int i = 0; i < choice_count - 1; i++) { | 3978 for (int i = 0; i < choice_count; i++) { |
3978 AlternativeGeneration* alt_gen = alt_gens.at(i); | 3979 AlternativeGeneration* alt_gen = alt_gens.at(i); |
3979 Trace new_trace(*trace); | 3980 Trace new_trace(*trace); |
3980 // If there are actions to be flushed we have to limit how many times | 3981 // If there are actions to be flushed we have to limit how many times |
3981 // they are flushed. Take the budget of the parent trace and distribute | 3982 // they are flushed. Take the budget of the parent trace and distribute |
3982 // it fairly amongst the children. | 3983 // it fairly amongst the children. |
3983 if (new_trace.actions() != NULL) { | 3984 if (new_trace.actions() != NULL) { |
3984 new_trace.set_flush_budget(new_flush_budget); | 3985 new_trace.set_flush_budget(new_flush_budget); |
3985 } | 3986 } |
| 3987 bool next_expects_preload = |
| 3988 i == choice_count - 1 ? false : alt_gens.at(i + 1)->expects_preload; |
3986 EmitOutOfLineContinuation(compiler, | 3989 EmitOutOfLineContinuation(compiler, |
3987 &new_trace, | 3990 &new_trace, |
3988 alternatives_->at(i), | 3991 alternatives_->at(i), |
3989 alt_gen, | 3992 alt_gen, |
3990 preload.preload_characters_, | 3993 preload.preload_characters_, |
3991 alt_gens.at(i + 1)->expects_preload); | 3994 next_expects_preload); |
3992 } | 3995 } |
3993 } | 3996 } |
3994 | 3997 |
3995 | 3998 |
3996 Trace* ChoiceNode::EmitGreedyLoop(RegExpCompiler* compiler, | 3999 Trace* ChoiceNode::EmitGreedyLoop(RegExpCompiler* compiler, |
3997 Trace* trace, | 4000 Trace* trace, |
3998 AlternativeGenerationList* alt_gens, | 4001 AlternativeGenerationList* alt_gens, |
3999 PreloadState* preload, | 4002 PreloadState* preload, |
4000 GreedyLoopState* greedy_loop_state, | 4003 GreedyLoopState* greedy_loop_state, |
4001 int text_length) { | 4004 int text_length) { |
(...skipping 20 matching lines...) Expand all Loading... |
4022 | 4025 |
4023 Label second_choice; // For use in greedy matches. | 4026 Label second_choice; // For use in greedy matches. |
4024 macro_assembler->Bind(&second_choice); | 4027 macro_assembler->Bind(&second_choice); |
4025 | 4028 |
4026 Trace* new_trace = greedy_loop_state->counter_backtrack_trace(); | 4029 Trace* new_trace = greedy_loop_state->counter_backtrack_trace(); |
4027 | 4030 |
4028 EmitChoices(compiler, | 4031 EmitChoices(compiler, |
4029 alt_gens, | 4032 alt_gens, |
4030 1, | 4033 1, |
4031 new_trace, | 4034 new_trace, |
4032 true, // Is greedy loop. | |
4033 preload); | 4035 preload); |
4034 | 4036 |
4035 macro_assembler->Bind(greedy_loop_state->label()); | 4037 macro_assembler->Bind(greedy_loop_state->label()); |
4036 // If we have unwound to the bottom then backtrack. | 4038 // If we have unwound to the bottom then backtrack. |
4037 macro_assembler->CheckGreedyLoop(trace->backtrack()); | 4039 macro_assembler->CheckGreedyLoop(trace->backtrack()); |
4038 // Otherwise try the second priority at an earlier position. | 4040 // Otherwise try the second priority at an earlier position. |
4039 macro_assembler->AdvanceCurrentPosition(-text_length); | 4041 macro_assembler->AdvanceCurrentPosition(-text_length); |
4040 macro_assembler->GoTo(&second_choice); | 4042 macro_assembler->GoTo(&second_choice); |
4041 return new_trace; | 4043 return new_trace; |
4042 } | 4044 } |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
4089 bm->EmitSkipInstructions(macro_assembler); | 4091 bm->EmitSkipInstructions(macro_assembler); |
4090 } | 4092 } |
4091 return eats_at_least; | 4093 return eats_at_least; |
4092 } | 4094 } |
4093 | 4095 |
4094 | 4096 |
4095 void ChoiceNode::EmitChoices(RegExpCompiler* compiler, | 4097 void ChoiceNode::EmitChoices(RegExpCompiler* compiler, |
4096 AlternativeGenerationList* alt_gens, | 4098 AlternativeGenerationList* alt_gens, |
4097 int first_choice, | 4099 int first_choice, |
4098 Trace* trace, | 4100 Trace* trace, |
4099 bool is_greedy_loop, | |
4100 PreloadState* preload) { | 4101 PreloadState* preload) { |
4101 RegExpMacroAssembler* macro_assembler = compiler->macro_assembler(); | 4102 RegExpMacroAssembler* macro_assembler = compiler->macro_assembler(); |
4102 SetUpPreLoad(compiler, trace, preload); | 4103 SetUpPreLoad(compiler, trace, preload); |
4103 | 4104 |
4104 // For now we just call all choices one after the other. The idea ultimately | 4105 // For now we just call all choices one after the other. The idea ultimately |
4105 // is to use the Dispatch table to try only the relevant ones. | 4106 // is to use the Dispatch table to try only the relevant ones. |
4106 int choice_count = alternatives_->length(); | 4107 int choice_count = alternatives_->length(); |
4107 | 4108 |
4108 int new_flush_budget = trace->flush_budget() / choice_count; | 4109 int new_flush_budget = trace->flush_budget() / choice_count; |
4109 | 4110 |
4110 for (int i = first_choice; i < choice_count; i++) { | 4111 for (int i = first_choice; i < choice_count; i++) { |
4111 bool is_last = i == choice_count - 1; | 4112 bool is_last = i == choice_count - 1; |
| 4113 bool fall_through_on_failure = !is_last; |
4112 GuardedAlternative alternative = alternatives_->at(i); | 4114 GuardedAlternative alternative = alternatives_->at(i); |
4113 AlternativeGeneration* alt_gen = alt_gens->at(i); | 4115 AlternativeGeneration* alt_gen = alt_gens->at(i); |
4114 alt_gen->quick_check_details.set_characters(preload->preload_characters_); | 4116 alt_gen->quick_check_details.set_characters(preload->preload_characters_); |
4115 ZoneList<Guard*>* guards = alternative.guards(); | 4117 ZoneList<Guard*>* guards = alternative.guards(); |
4116 int guard_count = (guards == NULL) ? 0 : guards->length(); | 4118 int guard_count = (guards == NULL) ? 0 : guards->length(); |
4117 Trace new_trace(*trace); | 4119 Trace new_trace(*trace); |
4118 new_trace.set_characters_preloaded(preload->preload_is_current_ ? | 4120 new_trace.set_characters_preloaded(preload->preload_is_current_ ? |
4119 preload->preload_characters_ : | 4121 preload->preload_characters_ : |
4120 0); | 4122 0); |
4121 if (preload->preload_has_checked_bounds_) { | 4123 if (preload->preload_has_checked_bounds_) { |
4122 new_trace.set_bound_checked_up_to(preload->preload_characters_); | 4124 new_trace.set_bound_checked_up_to(preload->preload_characters_); |
4123 } | 4125 } |
4124 new_trace.quick_check_performed()->Clear(); | 4126 new_trace.quick_check_performed()->Clear(); |
4125 if (not_at_start_) new_trace.set_at_start(Trace::FALSE_VALUE); | 4127 if (not_at_start_) new_trace.set_at_start(Trace::FALSE_VALUE); |
| 4128 if (!is_last) { |
| 4129 new_trace.set_backtrack(&alt_gen->after); |
| 4130 } |
4126 alt_gen->expects_preload = preload->preload_is_current_; | 4131 alt_gen->expects_preload = preload->preload_is_current_; |
4127 bool generate_full_check_inline = false; | 4132 bool generate_full_check_inline = false; |
4128 if (FLAG_regexp_optimization && | 4133 if (FLAG_regexp_optimization && |
4129 try_to_emit_quick_check_for_alternative(i == 0) && | 4134 try_to_emit_quick_check_for_alternative(i == 0) && |
4130 alternative.node()->EmitQuickCheck(compiler, | 4135 alternative.node()->EmitQuickCheck(compiler, |
| 4136 trace, |
4131 &new_trace, | 4137 &new_trace, |
4132 preload->preload_has_checked_bounds_, | 4138 preload->preload_has_checked_bounds_, |
4133 &alt_gen->possible_success, | 4139 &alt_gen->possible_success, |
4134 &alt_gen->quick_check_details, | 4140 &alt_gen->quick_check_details, |
4135 !is_last)) { | 4141 fall_through_on_failure)) { |
4136 // Quick check was generated for this choice. | 4142 // Quick check was generated for this choice. |
4137 preload->preload_is_current_ = true; | 4143 preload->preload_is_current_ = true; |
4138 preload->preload_has_checked_bounds_ = true; | 4144 preload->preload_has_checked_bounds_ = true; |
4139 // On the last choice in the ChoiceNode we generated the quick | 4145 // If we generated the quick check to fall through on possible success, |
4140 // check to fall through on possible success. So now we need to | 4146 // we now need to generate the full check inline. |
4141 // generate the full check inline. | 4147 if (!fall_through_on_failure) { |
4142 if (is_last) { | |
4143 macro_assembler->Bind(&alt_gen->possible_success); | 4148 macro_assembler->Bind(&alt_gen->possible_success); |
4144 new_trace.set_quick_check_performed(&alt_gen->quick_check_details); | 4149 new_trace.set_quick_check_performed(&alt_gen->quick_check_details); |
4145 new_trace.set_characters_preloaded(preload->preload_characters_); | 4150 new_trace.set_characters_preloaded(preload->preload_characters_); |
4146 new_trace.set_bound_checked_up_to(preload->preload_characters_); | 4151 new_trace.set_bound_checked_up_to(preload->preload_characters_); |
4147 generate_full_check_inline = true; | 4152 generate_full_check_inline = true; |
4148 } | 4153 } |
4149 } else if (alt_gen->quick_check_details.cannot_match()) { | 4154 } else if (alt_gen->quick_check_details.cannot_match()) { |
4150 if (is_last && !is_greedy_loop) { | 4155 if (!fall_through_on_failure) { |
4151 macro_assembler->GoTo(trace->backtrack()); | 4156 macro_assembler->GoTo(trace->backtrack()); |
4152 } // Else just fall through to the next test. | 4157 } |
4153 continue; | 4158 continue; |
4154 } else { | 4159 } else { |
4155 // No quick check was generated. Put the full code here. | 4160 // No quick check was generated. Put the full code here. |
4156 // If this is not the first choice then there could be slow checks from | 4161 // If this is not the first choice then there could be slow checks from |
4157 // previous cases that go here when they fail. There's no reason to | 4162 // previous cases that go here when they fail. There's no reason to |
4158 // insist that they preload characters since the slow check we are about | 4163 // insist that they preload characters since the slow check we are about |
4159 // to generate probably can't use it. | 4164 // to generate probably can't use it. |
4160 if (i != first_choice) { | 4165 if (i != first_choice) { |
4161 alt_gen->expects_preload = false; | 4166 alt_gen->expects_preload = false; |
4162 new_trace.InvalidateCurrentCharacter(); | 4167 new_trace.InvalidateCurrentCharacter(); |
4163 } | 4168 } |
4164 if (!is_last) { | |
4165 new_trace.set_backtrack(&alt_gen->after); | |
4166 } | |
4167 generate_full_check_inline = true; | 4169 generate_full_check_inline = true; |
4168 } | 4170 } |
4169 if (generate_full_check_inline) { | 4171 if (generate_full_check_inline) { |
4170 if (new_trace.actions() != NULL) { | 4172 if (new_trace.actions() != NULL) { |
4171 new_trace.set_flush_budget(new_flush_budget); | 4173 new_trace.set_flush_budget(new_flush_budget); |
4172 } | 4174 } |
4173 for (int j = 0; j < guard_count; j++) { | 4175 for (int j = 0; j < guard_count; j++) { |
4174 GenerateGuard(macro_assembler, guards->at(j), &new_trace); | 4176 GenerateGuard(macro_assembler, guards->at(j), &new_trace); |
4175 } | 4177 } |
4176 alternative.node()->Emit(compiler, &new_trace); | 4178 alternative.node()->Emit(compiler, &new_trace); |
(...skipping 1974 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
6151 } | 6153 } |
6152 | 6154 |
6153 return compiler.Assemble(¯o_assembler, | 6155 return compiler.Assemble(¯o_assembler, |
6154 node, | 6156 node, |
6155 data->capture_count, | 6157 data->capture_count, |
6156 pattern); | 6158 pattern); |
6157 } | 6159 } |
6158 | 6160 |
6159 | 6161 |
6160 }} // namespace v8::internal | 6162 }} // namespace v8::internal |
OLD | NEW |