Chromium Code Reviews| Index: src/regexp-macro-assembler-ia32.cc |
| =================================================================== |
| --- src/regexp-macro-assembler-ia32.cc (revision 0) |
| +++ src/regexp-macro-assembler-ia32.cc (revision 830) |
| @@ -0,0 +1,605 @@ |
| +// Copyright 2008 the V8 project authors. All rights reserved. |
| +// Redistribution and use in source and binary forms, with or without |
| +// modification, are permitted provided that the following conditions are |
| +// met: |
| +// |
| +// * Redistributions of source code must retain the above copyright |
| +// notice, this list of conditions and the following disclaimer. |
| +// * Redistributions in binary form must reproduce the above |
| +// copyright notice, this list of conditions and the following |
| +// disclaimer in the documentation and/or other materials provided |
| +// with the distribution. |
| +// * Neither the name of Google Inc. nor the names of its |
| +// contributors may be used to endorse or promote products derived |
| +// from this software without specific prior written permission. |
| +// |
| +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| + |
| +#include <string.h> |
| +#include "v8.h" |
| +#include "log.h" |
| +#include "ast.h" |
| +#include "macro-assembler.h" |
| +#include "regexp-macro-assembler-ia32.h" |
| + |
| +namespace v8 { namespace internal { |
| +/* |
| + * This assembler uses the following register assignment convention |
| + * - edx : current character, or kEndOfInput if current position is not |
| + * inside string. The kEndOfInput value is greater than 0xffff, |
| + * so any tests that don't check whether the current position |
| + * is inside the correct range should retain bits above the |
| + * 15th in their computations, and fail if the value is too |
| + * great. |
| + * - edi : current position in input, as negative offset from end of string. |
| + * - esi : end of input (points to byte after last character in input). |
| + * - ebp : points to the location above the registers on the stack, |
| + * as if by the "enter <register_count>" opcode. |
| + * - esp : points to tip of backtracking stack. |
| + * |
| + * The registers eax, ebx and ecx are free to use for computations. |
| + * |
| + * Each call to a public method should retain this convention. |
| + * The stack will have the following structure: |
| + * - int* capture_array (int[num_saved_registers_], for output). |
| + * - end of input (index of end of string, relative to *string_base) |
| + * - start of input (index of first character in string, relative |
| + * to *string_base) |
| + * - void** string_base (location of a handle containing the string) |
| + * - return address |
| + * - backup of esi |
| + * - backup of edi |
| + * ebp-> - old ebp |
| + * - register 0 ebp[-4] |
| + * - register 1 ebp[-8] |
| + * - ... |
| + * |
| + * The data before ebp must be placed there by the calling code, e.g., |
| + * by calling the code as cast to: |
| + * bool (*match)(String** string_base, |
| + * int start_offset, |
| + * int end_offset, |
| + * int* capture_output_array) |
| + */ |
| + |
| +#define __ masm_-> |
| + |
| +RegExpMacroAssemblerIA32::RegExpMacroAssemblerIA32( |
| + Mode mode, |
| + int registers_to_save, |
| + bool ignore_case) |
| + : masm_(new MacroAssembler(NULL, kRegExpCodeSize)), |
|
Mads Ager (chromium)
2008/11/25 21:09:41
Guess what? :-)
|
| + constants_(kRegExpConstantsSize), |
| + mode_(mode), |
| + num_registers_(registers_to_save), |
| + num_saved_registers_(registers_to_save), |
| + ignore_case_(ignore_case), |
| + entry_label_(), |
| + start_label_(), |
| + success_label_(), |
| + exit_label_(), |
| + self_(Heap::undefined_value()) { |
| + __ jmp(&entry_label_); // We'll write the entry code later. |
| + __ bind(&start_label_); // And then continue from here. |
| +} |
| + |
| + |
| +RegExpMacroAssemblerIA32::~RegExpMacroAssemblerIA32() { |
| + delete masm_; |
| + // Unuse labels in case we throw away the assembler without calling GetCode. |
| + entry_label_.Unuse(); |
| + start_label_.Unuse(); |
| + success_label_.Unuse(); |
| + exit_label_.Unuse(); |
| +} |
| + |
| + |
| +void RegExpMacroAssemblerIA32::AdvanceCurrentPosition(int by) { |
| + ASSERT(by > 0); |
| + Label inside_string; |
| + __ add(Operand(edi), Immediate(by * char_size())); |
| + __ j(below, &inside_string); |
| + Backtrack(); |
| + |
| + __ bind(&inside_string); |
| +} |
| + |
| + |
| +void RegExpMacroAssemblerIA32::AdvanceRegister(int reg, int by) { |
| + ASSERT(reg >= 0); |
| + ASSERT(reg < num_registers_); |
| + __ add(register_location(reg), Immediate(by)); |
| +} |
| + |
| + |
| +void RegExpMacroAssemblerIA32::Backtrack() { |
| + __ pop(ecx); |
| + __ add(Operand(ecx), Immediate(self_)); |
| + __ jmp(Operand(ecx)); |
| +} |
| + |
| + |
| +void RegExpMacroAssemblerIA32::Bind(Label* label) { |
| + __ bind(label); |
| +} |
| + |
| +void RegExpMacroAssemblerIA32::CheckBitmap(uc16 start, |
| + Label* bitmap, |
| + Label* on_zero) { |
| + ReadCurrentChar(eax); |
| + __ sub(Operand(eax), Immediate(start)); |
| + __ cmp(eax, 64); // FIXME: 64 = length_of_bitmap_in_bits. |
| + BranchOrBacktrack(greater_equal, on_zero); |
| + __ mov(ebx, eax); |
| + __ shr(ebx, 3); |
| + // TODO(lrn): Where is the bitmap stored? Pass the bitmap as argument instead. |
| + // __ mov(ecx, position_of_bitmap); |
| + __ movzx_b(ebx, Operand(ecx, ebx, times_1, 0)); |
| + __ and_(eax, (1<<3)-1); |
| + __ bt(Operand(ebx), eax); |
| + __ j(carry, on_zero); |
| +} |
| + |
| + |
| +void RegExpMacroAssemblerIA32::CheckCharacter(uc16 c, Label* on_equal) { |
| + __ cmp(edx, c); |
| + BranchOrBacktrack(equal, on_equal); |
| +} |
| + |
| + |
| +void RegExpMacroAssemblerIA32::CheckCharacterGT(uc16 limit, Label* on_greater) { |
| + __ cmp(edx, limit); |
| + BranchOrBacktrack(greater, on_greater); |
| +} |
| + |
| + |
| +void RegExpMacroAssemblerIA32::CheckCharacterLT(uc16 limit, Label* on_less) { |
| + __ cmp(edx, limit); |
| + BranchOrBacktrack(less, on_less); |
| +} |
| + |
| + |
| +void RegExpMacroAssemblerIA32::CheckCharacters(Vector<const uc16> str, |
| + int cp_offset, |
| + Label* on_failure) { |
| + int byte_length = str.length() * char_size(); |
| + int start_offset = cp_offset * char_size(); |
| + __ mov(ebx, edi); |
| + __ add(Operand(ebx), Immediate(start_offset + byte_length)); |
| + BranchOrBacktrack(greater_equal, on_failure); |
| + |
| + ArraySlice constant_buffer = constants_.GetBuffer(str.length(), char_size()); |
| + for (int i = 0; i < str.length(); i++) { |
| + if (mode_ == ASCII) { |
| + constant_buffer.at<char>(i) = static_cast<char>(str[i]); |
| + } else { |
| + memcpy(constant_buffer.location<void>(), |
| + str.start(), |
| + str.length() * sizeof(uc16)); |
| + } |
| + } |
| + |
| + __ mov(eax, edi); |
| + __ mov(ebx, esi); |
| + __ lea(edi, Operand(esi, edi, times_1, start_offset)); |
| + LoadConstantBufferAddress(esi, &constant_buffer); |
| + __ mov(ecx, str.length()); |
| + if (mode_ == ASCII) { |
| + __ rep_cmpsb(); |
| + } else { |
| + ASSERT(mode_ == UC16); |
| + __ rep_cmpsw(); |
| + } |
| + __ mov(esi, ebx); |
| + __ mov(edi, eax); |
| + BranchOrBacktrack(not_equal, on_failure); |
| +} |
| + |
| + |
| +void RegExpMacroAssemblerIA32::CheckCurrentPosition(int register_index, |
| + Label* on_equal) { |
| + __ cmp(edi, register_location(register_index)); |
| + BranchOrBacktrack(equal, on_equal); |
| +} |
| + |
| + |
| +void RegExpMacroAssemblerIA32::CheckNotBackReference( |
| + int start_reg, Label* on_no_match) { |
| + if (ignore_case_) { |
| + UNIMPLEMENTED(); |
| + } |
| + Label fallthrough; |
| + __ mov(eax, register_location(start_reg)); |
| + __ mov(ecx, register_location(start_reg + 1)); |
| + __ sub(ecx, Operand(eax)); // Length to check. |
| + __ j(equal, &fallthrough); // Covers the case where it's not bound (-1,-1). |
| + __ mov(ebx, Operand(edi)); |
| + __ push(esi); |
| + __ add(edi, Operand(esi)); |
| + __ add(esi, Operand(eax)); |
| + if (mode_ == ASCII) { |
| + __ rep_cmpsb(); |
| + } else { |
| + __ rep_cmpsw(); |
| + } |
| + __ pop(esi); |
| + __ mov(edi, Operand(ebx)); |
| + BranchOrBacktrack(not_equal, on_no_match); |
| + __ bind(&fallthrough); |
| +} |
| + |
| + |
| +void RegExpMacroAssemblerIA32::CheckNotCharacter(uc16 c, Label* on_not_equal) { |
| + __ cmp(edx, c); |
| + BranchOrBacktrack(not_equal, on_not_equal); |
| +} |
| + |
| + |
| +void RegExpMacroAssemblerIA32::CheckNotCharacterAfterOr(uc16 c, |
| + uc16 mask, |
| + Label* on_not_equal) { |
| + __ mov(eax, Operand(edx)); |
| + __ or_(eax, mask); |
| + __ cmp(eax, c); |
| + BranchOrBacktrack(not_equal, on_not_equal); |
| +} |
| + |
| + |
| +void RegExpMacroAssemblerIA32::CheckNotCharacterAfterMinusOr( |
| + uc16 c, |
| + uc16 mask, |
| + Label* on_not_equal) { |
| + __ lea(eax, Operand(edx, -mask)); |
| + __ or_(eax, mask); |
| + __ cmp(eax, c); |
| + BranchOrBacktrack(not_equal, on_not_equal); |
| +} |
| + |
| + |
| +void RegExpMacroAssemblerIA32::DispatchHalfNibbleMap( |
| + uc16 start, |
| + Label* half_nibble_map, |
| + const Vector<Label*>& destinations) { |
| + ReadCurrentChar(eax); |
| + __ sub(Operand(eax), Immediate(start)); |
| + |
| + __ mov(ecx, eax); |
| + __ shr(eax, 2); |
| + // FIXME: ecx must hold address of map |
| + __ movzx_b(eax, Operand(ecx, eax, times_1, 0)); |
| + __ and_(ecx, 0x03); |
| + __ add(ecx, Operand(ecx)); |
| + __ shr(eax); // Shift right cl times |
| + |
| + Label second_bit_set, case_3, case_1; |
| + __ test(eax, Immediate(0x02)); |
| + __ j(not_zero, &second_bit_set); |
| + __ test(eax, Immediate(0x01)); |
| + __ j(not_zero, &case_1); |
| + // Case 0: |
| + __ jmp(destinations[0]); |
| + __ bind(&case_1); |
| + // Case 1: |
| + __ jmp(destinations[1]); |
| + __ bind(&second_bit_set); |
| + __ test(eax, Immediate(0x01)); |
| + __ j(not_zero, &case_3); |
| + // Case 2 |
| + __ jmp(destinations[2]); |
| + __ bind(&case_3); |
| + // Case 3: |
| + __ jmp(destinations[3]); |
| +} |
| + |
| + |
| +void RegExpMacroAssemblerIA32::DispatchByteMap( |
| + uc16 start, |
| + Label* byte_map, |
| + const Vector<Label*>& destinations) { |
| + Label fallthrough; |
| + ReadCurrentChar(eax); |
| + __ sub(Operand(eax), Immediate(start)); |
| + __ cmp(eax, 64); // FIXME: 64 = size of map. Found somehow?? |
| + __ j(greater_equal, &fallthrough); |
| + // FIXME: ecx must hold address of map |
| + __ movzx_b(eax, Operand(ecx, eax, times_1, 0)); |
| + // jump table: jump to destinations[eax]; |
| + |
| + __ bind(&fallthrough); |
| +} |
| + |
| + |
| + |
| +void RegExpMacroAssemblerIA32::DispatchHighByteMap( |
| + byte start, |
| + Label* byte_map, |
| + const Vector<Label*>& destinations) { |
| + Label fallthrough; |
| + ReadCurrentChar(eax); |
| + __ shr(eax, 8); |
| + __ sub(Operand(eax), Immediate(start)); |
| + __ cmp(eax, destinations.length() - start); |
| + __ j(greater_equal, &fallthrough); |
| + |
| + // TODO(lrn) jumptable: jump to destinations[eax] |
| + __ bind(&fallthrough); |
| +} |
| + |
| + |
| +void RegExpMacroAssemblerIA32::EmitOrLink(Label* label) { |
| + UNREACHABLE(); // Has no use. |
| +} |
| + |
| + |
| +void RegExpMacroAssemblerIA32::Fail() { |
| + __ mov(eax, 0); |
| + __ jmp(&exit_label_); |
| +} |
| + |
| + |
| +Handle<Object> RegExpMacroAssemblerIA32::GetCode() { |
| + // Finalize code - write the entry point code now we know how many |
| + // registers we need. |
| + |
| + // Entry code: |
| + __ bind(&entry_label_); |
| + __ push(esi); |
| + __ push(edi); |
| + __ enter(Immediate(num_registers_ * sizeof(uint32_t))); |
| + __ mov(esi, Operand(ebp, kInputEndOffset)); |
| + __ mov(edi, Operand(ebp, kInputStartOffset)); |
| + __ sub(edi, Operand(esi)); |
| + __ mov(edx, Operand(ebp, kInputBuffer)); |
| + __ mov(edx, Operand(edx, 0)); |
| + __ add(esi, Operand(edx)); |
| + __ jmp(&start_label_); |
| + |
| + // Exit code: |
| + __ bind(&success_label_); |
| + __ mov(ebx, Operand(ebp, kRegisterOutput)); |
| + __ mov(ecx, Operand(ebp, kInputEndOffset)); |
| + __ sub(ecx, Operand(ebp, kInputStartOffset)); |
| + for (int i = 0; i < num_saved_registers_; i++) { |
| + __ mov(eax, register_location(i)); |
| + __ sub(eax, Operand(ecx)); // Convert to index from start, not end. |
| + __ mov(Operand(ebx, i * sizeof(int32_t)), eax); |
| + } |
| + // copy captures to output |
| + __ mov(eax, Immediate(1)); |
| + |
| + __ bind(&exit_label_); |
| + __ leave(); |
| + __ pop(edi); |
| + __ pop(esi); |
| + __ ret(0); |
| + |
| + CodeDesc code_desc; |
| + masm_->GetCode(&code_desc); |
| + Handle<Code> code = Factory::NewCode(code_desc, |
| + NULL, |
| + Code::ComputeFlags(Code::REGEXP), |
| + self_); |
| + LOG(CodeCreateEvent("RegExp", *code, "(Compiled RegExp)")); |
| + return Handle<Object>::cast(code); |
| +} |
| + |
| + |
| +void RegExpMacroAssemblerIA32::GoTo(Label* to) { |
| + __ jmp(to); |
| +} |
| + |
| + |
| + |
| +void RegExpMacroAssemblerIA32::IfRegisterGE(int reg, |
| + int comparand, |
| + Label* if_ge) { |
| + __ cmp(register_location(reg), Immediate(comparand)); |
| + BranchOrBacktrack(greater_equal, if_ge); |
| +} |
| + |
| + |
| + |
| +void RegExpMacroAssemblerIA32::IfRegisterLT(int reg, |
| + int comparand, |
| + Label* if_lt) { |
| + __ cmp(register_location(reg), Immediate(comparand)); |
| + BranchOrBacktrack(less, if_lt); |
| +} |
| + |
| + |
| + |
| +RegExpMacroAssembler::IrregexpImplementation |
| + RegExpMacroAssemblerIA32::Implementation() { |
| + return kIA32Implementation; |
| +} |
| + |
| + |
| + |
| +void RegExpMacroAssemblerIA32::LoadCurrentCharacter(int cp_offset, |
| + Label* on_end_of_input) { |
| + ASSERT(cp_offset >= 0); |
| + ASSERT(cp_offset < (1<<30)); // Be sane! (And ensure negation works) |
| + __ cmp(edi, -cp_offset); |
| + BranchOrBacktrack(less_equal, on_end_of_input); |
| + ReadChar(edx, cp_offset); |
| +} |
| + |
| + |
| +void RegExpMacroAssemblerIA32::PopCurrentPosition() { |
| + __ pop(edi); |
| +} |
| + |
| + |
| +void RegExpMacroAssemblerIA32::PopRegister(int register_index) { |
| + RecordRegister(register_index); |
| + __ pop(register_location(register_index)); |
| +} |
| + |
| + |
| +void RegExpMacroAssemblerIA32::PushBacktrack(Label* label) { |
| + // Check for preemption first. |
| + Label no_preempt; |
| + Label retry_preempt; |
| + // Check for preemption. |
| + ExternalReference stack_limit = |
| + ExternalReference::address_of_stack_guard_limit(); |
| + __ cmp(esp, Operand::StaticVariable(stack_limit)); |
| + __ j(above, &no_preempt); |
| + |
| + __ push(edi); // Current position. |
| + __ push(edx); // Current character. |
| + // Restore original edi, esi. |
| + __ mov(edi, Operand(ebp, kBackup_edi)); |
| + __ mov(esi, Operand(ebp, kBackup_esi)); |
| + |
| + __ bind(&retry_preempt); |
| + // simulate stack for Runtime call. |
| + __ push(Immediate(0)); // Dummy receiver |
| + __ CallRuntime(Runtime::kStackGuard, 0); |
| + __ cmp(esp, Operand::StaticVariable(stack_limit)); |
| + __ j(below_equal, &retry_preempt); |
| + |
| + __ pop(edx); |
| + __ pop(edi); |
| + __ mov(esi, Operand(ebp, kInputBuffer)); |
| + __ mov(esi, Operand(esi, 0)); |
| + __ add(esi, Operand(ebp, kInputEndOffset)); |
| + |
| + __ bind(&no_preempt); |
| + |
| + Label cont; |
| + __ push(label, RelocInfo::NONE); |
| +} |
| + |
| + |
| +void RegExpMacroAssemblerIA32::PushCurrentPosition() { |
| + __ push(edi); |
| +} |
| + |
| + |
| +void RegExpMacroAssemblerIA32::PushRegister(int register_index) { |
| + __ push(register_location(register_index)); |
| +} |
| + |
| + |
| +void RegExpMacroAssemblerIA32::ReadCurrentPositionFromRegister(int reg) { |
| + __ mov(edi, register_location(reg)); |
| +} |
| + |
| + |
| +void RegExpMacroAssemblerIA32::ReadStackPointerFromRegister(int reg) { |
| + __ mov(esp, register_location(reg)); |
| +} |
| + |
| + |
| +void RegExpMacroAssemblerIA32::SetRegister(int register_index, int to) { |
| + RecordRegister(register_index); |
| + __ mov(register_location(register_index), Immediate(to)); |
| +} |
| + |
| + |
| +void RegExpMacroAssemblerIA32::Succeed() { |
| + __ jmp(&success_label_); |
| +} |
| + |
| + |
| +void RegExpMacroAssemblerIA32::WriteCurrentPositionToRegister( |
| + int register_index) { |
| + __ mov(register_location(register_index), edi); |
| +} |
| + |
| +void RegExpMacroAssemblerIA32::WriteStackPointerToRegister(int reg) { |
| + __ mov(register_location(reg), esp); |
| +} |
| + |
| + |
| +// Private methods: |
| + |
| +Operand RegExpMacroAssemblerIA32::register_location( |
| + int register_index) { |
| + ASSERT(register_index < (1<<30)); |
| + return Operand(ebp, -((register_index + 1) * sizeof(uint32_t))); |
| +} |
| + |
| + |
| +size_t RegExpMacroAssemblerIA32::char_size() { |
| + return static_cast<size_t>(mode_); |
| +} |
| + |
| + |
| +void RegExpMacroAssemblerIA32::BranchOrBacktrack(Condition condition, |
| + Label* to) { |
| + if (condition < 0) { // No condition |
| + if (to == NULL) { |
| + Backtrack(); |
| + return; |
| + } |
| + __ jmp(to); |
| + return; |
| + } else if (to == NULL) { |
| + Label skip; |
| + __ j(NegateCondition(condition), &skip); |
| + Backtrack(); |
| + __ bind(&skip); |
| + return; |
| + } |
| + __ j(condition, to); |
| +} |
| + |
| + |
| +void RegExpMacroAssemblerIA32::Canonicalize(Register reg) { |
| + if (mode_ == ASCII) { |
| + Label end; |
| + __ cmp(Operand(reg), Immediate('a')); |
| + __ j(below, &end); |
| + __ cmp(Operand(reg), Immediate('z')); |
| + __ j(above, &end); |
| + __ sub(Operand(reg), Immediate('a' - 'A')); |
| + __ bind(&end); |
| + return; |
| + } |
| + ASSERT(mode_ == UC16); |
| + // TODO(lrn): Use some tables. |
| +} |
| + |
| + |
| +void RegExpMacroAssemblerIA32::RecordRegister(int register_index) { |
| + if (register_index >= num_registers_) { |
| + num_registers_ = register_index + 1; |
| + } |
| +} |
| + |
| + |
| +void RegExpMacroAssemblerIA32::ReadChar(Register destination, int offset) { |
| + if (mode_ == ASCII) { |
| + __ movzx_b(destination, Operand(esi, edi, times_1, offset)); |
| + return; |
| + } |
| + ASSERT(mode_ == UC16); |
| + __ movzx_w(destination, Operand(esi, edi, times_1, offset * 2)); |
| +} |
| + |
| + |
| +void RegExpMacroAssemblerIA32::ReadCurrentChar(Register destination) { |
| + __ mov(destination, edx); |
| +} |
| + |
| + |
| +void RegExpMacroAssemblerIA32::LoadConstantBufferAddress(Register reg, |
| + ArraySlice* buffer) { |
| + __ mov(reg, buffer->array()); |
| + __ add(Operand(reg), Immediate(buffer->base_offset())); |
| +} |
| + |
| +#undef __ |
| +}} |