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