| /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
| * vim: set ts=8 sts=4 et sw=4 tw=99: */ |
| |
| // Copyright 2012 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 "irregexp/NativeRegExpMacroAssembler.h" |
| |
| #include "irregexp/RegExpStack.h" |
| #include "jit/Linker.h" |
| #ifdef JS_ION_PERF |
| # include "jit/PerfSpewer.h" |
| #endif |
| #include "vm/MatchPairs.h" |
| |
| #include "jit/MacroAssembler-inl.h" |
| |
| using namespace js; |
| using namespace js::irregexp; |
| using namespace js::jit; |
| |
| /* |
| * This assembler uses the following register assignment convention: |
| * |
| * - current_character : |
| * Must be loaded using LoadCurrentCharacter before using any of the |
| * dispatch methods. Temporarily stores the index of capture start after a |
| * matching pass for a global regexp. |
| * - current_position : |
| * Current position in input, as negative offset from end of string. |
| * Please notice that this is the byte offset, not the character offset! |
| * - input_end_pointer : |
| * Points to byte after last character in the input. |
| * - backtrack_stack_pointer : |
| * Points to tip of the heap allocated backtrack stack |
| * - StackPointer : |
| * Points to tip of the native stack, used to access arguments, local |
| * variables and RegExp registers. |
| * |
| * The tempN registers are free to use for computations. |
| */ |
| |
| NativeRegExpMacroAssembler::NativeRegExpMacroAssembler(LifoAlloc* alloc, RegExpShared* shared, |
| JSRuntime* rt, Mode mode, int registers_to_save) |
| : RegExpMacroAssembler(*alloc, shared, registers_to_save), |
| runtime(rt), mode_(mode) |
| { |
| // Find physical registers for each compiler register. |
| AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All()); |
| |
| input_end_pointer = regs.takeAny(); |
| current_character = regs.takeAny(); |
| current_position = regs.takeAny(); |
| backtrack_stack_pointer = regs.takeAny(); |
| temp0 = regs.takeAny(); |
| temp1 = regs.takeAny(); |
| temp2 = regs.takeAny(); |
| |
| JitSpew(JitSpew_Codegen, |
| "Starting RegExp (input_end_pointer %s) (current_character %s)" |
| " (current_position %s) (backtrack_stack_pointer %s) (temp0 %s) temp1 (%s) temp2 (%s)", |
| input_end_pointer.name(), |
| current_character.name(), |
| current_position.name(), |
| backtrack_stack_pointer.name(), |
| temp0.name(), |
| temp1.name(), |
| temp2.name()); |
| |
| savedNonVolatileRegisters = SavedNonVolatileRegisters(regs); |
| |
| masm.jump(&entry_label_); |
| masm.bind(&start_label_); |
| } |
| |
| #define SPEW_PREFIX JitSpew_Codegen, "!!! " |
| |
| // The signature of the code which this generates is: |
| // |
| // void execute(InputOutputData*); |
| RegExpCode |
| NativeRegExpMacroAssembler::GenerateCode(JSContext* cx, bool match_only) |
| { |
| if (!cx->compartment()->ensureJitCompartmentExists(cx)) |
| return RegExpCode(); |
| |
| JitSpew(SPEW_PREFIX "GenerateCode"); |
| |
| // We need an even number of registers, for stack alignment. |
| if (num_registers_ % 2 == 1) |
| num_registers_++; |
| |
| Label return_temp0; |
| |
| // Finalize code - write the entry point code now we know how many |
| // registers we need. |
| masm.bind(&entry_label_); |
| |
| #ifdef JS_CODEGEN_ARM64 |
| // ARM64 communicates stack address via sp, but uses a pseudo-sp for addressing. |
| MOZ_ASSERT(!masm.GetStackPointer64().Is(sp)); |
| masm.Mov(masm.GetStackPointer64(), sp); |
| #endif |
| |
| // Push non-volatile registers which might be modified by jitcode. |
| size_t pushedNonVolatileRegisters = 0; |
| for (GeneralRegisterForwardIterator iter(savedNonVolatileRegisters); iter.more(); ++iter) { |
| masm.Push(*iter); |
| pushedNonVolatileRegisters++; |
| } |
| |
| #if defined(XP_IOS) && defined(JS_CODEGEN_ARM) |
| // The stack is 4-byte aligned on iOS, force 8-byte alignment. |
| masm.movePtr(StackPointer, temp0); |
| masm.andPtr(Imm32(~7), StackPointer); |
| masm.push(temp0); |
| masm.push(temp0); |
| #endif |
| |
| #ifndef JS_CODEGEN_X86 |
| // The InputOutputData* is stored as an argument, save it on the stack |
| // above the frame. |
| masm.Push(IntArgReg0); |
| #endif |
| |
| size_t frameSize = sizeof(FrameData) + num_registers_ * sizeof(void*); |
| frameSize = JS_ROUNDUP(frameSize + masm.framePushed(), ABIStackAlignment) - masm.framePushed(); |
| |
| // Actually emit code to start a new stack frame. |
| masm.reserveStack(frameSize); |
| masm.checkStackAlignment(); |
| |
| // Check if we have space on the stack. Use the *NoInterrupt stack limit to |
| // avoid failing repeatedly when the regex code is called from Ion JIT code, |
| // see bug 1208819. |
| Label stack_ok; |
| void* stack_limit = runtime->addressOfJitStackLimitNoInterrupt(); |
| masm.branchStackPtrRhs(Assembler::Below, AbsoluteAddress(stack_limit), &stack_ok); |
| |
| // Exit with an exception. There is not enough space on the stack |
| // for our working registers. |
| masm.movePtr(ImmWord(RegExpRunStatus_Error), temp0); |
| masm.jump(&return_temp0); |
| |
| masm.bind(&stack_ok); |
| |
| #ifdef XP_WIN |
| // Ensure that we write to each stack page, in order. Skipping a page |
| // on Windows can cause segmentation faults. Assuming page size is 4k. |
| const int kPageSize = 4096; |
| for (int i = frameSize - sizeof(void*); i >= 0; i -= kPageSize) |
| masm.storePtr(temp0, Address(masm.getStackPointer(), i)); |
| #endif // XP_WIN |
| |
| #ifndef JS_CODEGEN_X86 |
| // The InputOutputData* is stored on the stack immediately above the frame. |
| Address inputOutputAddress(masm.getStackPointer(), frameSize); |
| #else |
| // The InputOutputData* is left in its original on stack location. |
| Address inputOutputAddress(masm.getStackPointer(), |
| frameSize + (pushedNonVolatileRegisters + 1) * sizeof(void*)); |
| #endif |
| |
| masm.loadPtr(inputOutputAddress, temp0); |
| |
| // Copy output registers to FrameData. |
| if (!match_only) { |
| Register matchPairsRegister = input_end_pointer; |
| masm.loadPtr(Address(temp0, offsetof(InputOutputData, matches)), matchPairsRegister); |
| masm.loadPtr(Address(matchPairsRegister, MatchPairs::offsetOfPairs()), temp1); |
| masm.storePtr(temp1, Address(masm.getStackPointer(), offsetof(FrameData, outputRegisters))); |
| masm.load32(Address(matchPairsRegister, MatchPairs::offsetOfPairCount()), temp1); |
| masm.lshiftPtr(Imm32(1), temp1); |
| masm.store32(temp1, Address(masm.getStackPointer(), offsetof(FrameData, numOutputRegisters))); |
| |
| #ifdef DEBUG |
| // Bounds check numOutputRegisters. |
| Label enoughRegisters; |
| masm.branchPtr(Assembler::GreaterThanOrEqual, |
| temp1, ImmWord(num_saved_registers_), &enoughRegisters); |
| masm.assumeUnreachable("Not enough output registers for RegExp"); |
| masm.bind(&enoughRegisters); |
| #endif |
| } |
| |
| // Load string end pointer. |
| masm.loadPtr(Address(temp0, offsetof(InputOutputData, inputEnd)), input_end_pointer); |
| |
| // Load input start pointer, and copy to FrameData. |
| masm.loadPtr(Address(temp0, offsetof(InputOutputData, inputStart)), current_position); |
| masm.storePtr(current_position, Address(masm.getStackPointer(), offsetof(FrameData, inputStart))); |
| |
| // Load start index, and copy to FrameData. |
| masm.loadPtr(Address(temp0, offsetof(InputOutputData, startIndex)), temp1); |
| masm.storePtr(temp1, Address(masm.getStackPointer(), offsetof(FrameData, startIndex))); |
| |
| // Set up input position to be negative offset from string end. |
| masm.subPtr(input_end_pointer, current_position); |
| |
| // Set temp0 to address of char before start of the string. |
| // (effectively string position -1). |
| masm.computeEffectiveAddress(Address(current_position, -char_size()), temp0); |
| |
| // Store this value on the frame, for use when clearing |
| // position registers. |
| masm.storePtr(temp0, Address(masm.getStackPointer(), offsetof(FrameData, inputStartMinusOne))); |
| |
| // Update current position based on start index. |
| masm.computeEffectiveAddress(BaseIndex(current_position, temp1, factor()), current_position); |
| |
| Label load_char_start_regexp, start_regexp; |
| |
| // Load newline if index is at start, previous character otherwise. |
| masm.branchPtr(Assembler::NotEqual, |
| Address(masm.getStackPointer(), offsetof(FrameData, startIndex)), ImmWord(0), |
| &load_char_start_regexp); |
| masm.movePtr(ImmWord('\n'), current_character); |
| masm.jump(&start_regexp); |
| |
| // Global regexp restarts matching here. |
| masm.bind(&load_char_start_regexp); |
| |
| // Load previous char as initial value of current character register. |
| LoadCurrentCharacterUnchecked(-1, 1); |
| masm.bind(&start_regexp); |
| |
| // Initialize on-stack registers. |
| MOZ_ASSERT(num_saved_registers_ > 0); |
| |
| // Fill saved registers with initial value = start offset - 1 |
| // Fill in stack push order, to avoid accessing across an unwritten |
| // page (a problem on Windows). |
| if (num_saved_registers_ > 8) { |
| masm.movePtr(ImmWord(register_offset(0)), temp1); |
| Label init_loop; |
| masm.bind(&init_loop); |
| masm.storePtr(temp0, BaseIndex(masm.getStackPointer(), temp1, TimesOne)); |
| masm.addPtr(ImmWord(sizeof(void*)), temp1); |
| masm.branchPtr(Assembler::LessThan, temp1, |
| ImmWord(register_offset(num_saved_registers_)), &init_loop); |
| } else { |
| // Unroll the loop. |
| for (int i = 0; i < num_saved_registers_; i++) |
| masm.storePtr(temp0, register_location(i)); |
| } |
| |
| // Initialize backtrack stack pointer. |
| masm.loadPtr(AbsoluteAddress(runtime->regexpStack.addressOfBase()), backtrack_stack_pointer); |
| masm.storePtr(backtrack_stack_pointer, |
| Address(masm.getStackPointer(), offsetof(FrameData, backtrackStackBase))); |
| |
| masm.jump(&start_label_); |
| |
| // Exit code: |
| if (success_label_.used()) { |
| MOZ_ASSERT(num_saved_registers_ > 0); |
| |
| Address outputRegistersAddress(masm.getStackPointer(), offsetof(FrameData, outputRegisters)); |
| |
| // Save captures when successful. |
| masm.bind(&success_label_); |
| |
| if (!match_only) { |
| Register outputRegisters = temp1; |
| Register inputByteLength = backtrack_stack_pointer; |
| |
| masm.loadPtr(outputRegistersAddress, outputRegisters); |
| |
| masm.loadPtr(inputOutputAddress, temp0); |
| masm.loadPtr(Address(temp0, offsetof(InputOutputData, inputEnd)), inputByteLength); |
| masm.subPtr(Address(temp0, offsetof(InputOutputData, inputStart)), inputByteLength); |
| |
| // Copy captures to output. Note that registers on the C stack are pointer width |
| // so that they might hold pointers, but output registers are int32_t. |
| for (int i = 0; i < num_saved_registers_; i++) { |
| masm.loadPtr(register_location(i), temp0); |
| if (i == 0 && global_with_zero_length_check()) { |
| // Keep capture start in current_character for the zero-length check later. |
| masm.movePtr(temp0, current_character); |
| } |
| |
| // Convert to index from start of string, not end. |
| masm.addPtr(inputByteLength, temp0); |
| |
| // Convert byte index to character index. |
| if (mode_ == CHAR16) |
| masm.rshiftPtrArithmetic(Imm32(1), temp0); |
| |
| masm.store32(temp0, Address(outputRegisters, i * sizeof(int32_t))); |
| } |
| } |
| |
| // Restart matching if the regular expression is flagged as global. |
| if (global()) { |
| // Increment success counter. |
| masm.add32(Imm32(1), Address(masm.getStackPointer(), offsetof(FrameData, successfulCaptures))); |
| |
| Address numOutputRegistersAddress(masm.getStackPointer(), offsetof(FrameData, numOutputRegisters)); |
| |
| // Capture results have been stored, so the number of remaining global |
| // output registers is reduced by the number of stored captures. |
| masm.load32(numOutputRegistersAddress, temp0); |
| |
| masm.sub32(Imm32(num_saved_registers_), temp0); |
| |
| // Check whether we have enough room for another set of capture results. |
| masm.branch32(Assembler::LessThan, temp0, Imm32(num_saved_registers_), &exit_label_); |
| |
| masm.store32(temp0, numOutputRegistersAddress); |
| |
| // Advance the location for output. |
| masm.add32(Imm32(num_saved_registers_ * sizeof(void*)), outputRegistersAddress); |
| |
| // Prepare temp0 to initialize registers with its value in the next run. |
| masm.loadPtr(Address(masm.getStackPointer(), offsetof(FrameData, inputStartMinusOne)), temp0); |
| |
| if (global_with_zero_length_check()) { |
| // Special case for zero-length matches. |
| |
| // The capture start index was loaded into current_character above. |
| masm.branchPtr(Assembler::NotEqual, current_position, current_character, |
| &load_char_start_regexp); |
| |
| // edi (offset from the end) is zero if we already reached the end. |
| masm.branchTestPtr(Assembler::Zero, current_position, current_position, |
| &exit_label_); |
| |
| // Advance current position after a zero-length match. |
| masm.addPtr(Imm32(char_size()), current_position); |
| } |
| |
| masm.jump(&load_char_start_regexp); |
| } else { |
| masm.movePtr(ImmWord(RegExpRunStatus_Success), temp0); |
| } |
| } |
| |
| masm.bind(&exit_label_); |
| |
| if (global()) { |
| // Return the number of successful captures. |
| masm.load32(Address(masm.getStackPointer(), offsetof(FrameData, successfulCaptures)), temp0); |
| } |
| |
| masm.bind(&return_temp0); |
| |
| // Store the result to the input structure. |
| masm.loadPtr(inputOutputAddress, temp1); |
| masm.storePtr(temp0, Address(temp1, offsetof(InputOutputData, result))); |
| |
| #ifndef JS_CODEGEN_X86 |
| // Include the InputOutputData* when adjusting the stack size. |
| masm.freeStack(frameSize + sizeof(void*)); |
| #else |
| masm.freeStack(frameSize); |
| #endif |
| |
| #if defined(XP_IOS) && defined(JS_CODEGEN_ARM) |
| masm.pop(temp0); |
| masm.movePtr(temp0, StackPointer); |
| #endif |
| |
| // Restore non-volatile registers which were saved on entry. |
| for (GeneralRegisterBackwardIterator iter(savedNonVolatileRegisters); iter.more(); ++iter) |
| masm.Pop(*iter); |
| |
| masm.abiret(); |
| |
| // Backtrack code (branch target for conditional backtracks). |
| if (backtrack_label_.used()) { |
| masm.bind(&backtrack_label_); |
| Backtrack(); |
| } |
| |
| // Backtrack stack overflow code. |
| if (stack_overflow_label_.used()) { |
| // Reached if the backtrack-stack limit has been hit. temp2 holds the |
| // StackPointer to use for accessing FrameData. |
| masm.bind(&stack_overflow_label_); |
| |
| Label grow_failed; |
| |
| masm.movePtr(ImmPtr(runtime), temp1); |
| |
| // Save registers before calling C function |
| LiveGeneralRegisterSet volatileRegs(GeneralRegisterSet::Volatile()); |
| #if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64) |
| volatileRegs.add(Register::FromCode(Registers::lr)); |
| #elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64) |
| volatileRegs.add(Register::FromCode(Registers::ra)); |
| #endif |
| volatileRegs.takeUnchecked(temp0); |
| volatileRegs.takeUnchecked(temp1); |
| masm.PushRegsInMask(volatileRegs); |
| |
| masm.setupUnalignedABICall(temp0); |
| masm.passABIArg(temp1); |
| masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, GrowBacktrackStack)); |
| masm.storeCallResult(temp0); |
| |
| masm.PopRegsInMask(volatileRegs); |
| |
| // If return false, we have failed to grow the stack, and |
| // must exit with a stack-overflow exception. Do this in the caller |
| // so that the stack is adjusted by our return instruction. |
| Label return_from_overflow_handler; |
| masm.branchTest32(Assembler::Zero, temp0, temp0, &return_from_overflow_handler); |
| |
| // Otherwise, store the new backtrack stack base and recompute the new |
| // top of the stack. |
| Address backtrackStackBaseAddress(temp2, offsetof(FrameData, backtrackStackBase)); |
| masm.subPtr(backtrackStackBaseAddress, backtrack_stack_pointer); |
| |
| masm.loadPtr(AbsoluteAddress(runtime->regexpStack.addressOfBase()), temp1); |
| masm.storePtr(temp1, backtrackStackBaseAddress); |
| masm.addPtr(temp1, backtrack_stack_pointer); |
| |
| // Resume execution in calling code. |
| masm.bind(&return_from_overflow_handler); |
| masm.abiret(); |
| } |
| |
| if (exit_with_exception_label_.used()) { |
| // If any of the code above needed to exit with an exception. |
| masm.bind(&exit_with_exception_label_); |
| |
| // Exit with an error result to signal thrown exception. |
| masm.movePtr(ImmWord(RegExpRunStatus_Error), temp0); |
| masm.jump(&return_temp0); |
| } |
| |
| Linker linker(masm); |
| AutoFlushICache afc("RegExp"); |
| JitCode* code = linker.newCode<NoGC>(cx, REGEXP_CODE); |
| if (!code) { |
| ReportOutOfMemory(cx); |
| return RegExpCode(); |
| } |
| |
| #ifdef JS_ION_PERF |
| writePerfSpewerJitCodeProfile(code, "RegExp"); |
| #endif |
| |
| AutoWritableJitCode awjc(code); |
| |
| for (size_t i = 0; i < labelPatches.length(); i++) { |
| LabelPatch& v = labelPatches[i]; |
| MOZ_ASSERT(!v.label); |
| Assembler::PatchDataWithValueCheck(CodeLocationLabel(code, v.patchOffset), |
| ImmPtr(code->raw() + v.labelOffset), |
| ImmPtr(0)); |
| } |
| |
| JitSpew(JitSpew_Codegen, "Created RegExp (raw %p length %d)", |
| (void*) code->raw(), (int) masm.bytesNeeded()); |
| |
| RegExpCode res; |
| res.jitCode = code; |
| return res; |
| } |
| |
| int |
| NativeRegExpMacroAssembler::stack_limit_slack() |
| { |
| return RegExpStack::kStackLimitSlack; |
| } |
| |
| void |
| NativeRegExpMacroAssembler::AdvanceCurrentPosition(int by) |
| { |
| JitSpew(SPEW_PREFIX "AdvanceCurrentPosition(%d)", by); |
| |
| if (by != 0) |
| masm.addPtr(Imm32(by * char_size()), current_position); |
| } |
| |
| void |
| NativeRegExpMacroAssembler::AdvanceRegister(int reg, int by) |
| { |
| JitSpew(SPEW_PREFIX "AdvanceRegister(%d, %d)", reg, by); |
| |
| MOZ_ASSERT(reg >= 0); |
| MOZ_ASSERT(reg < num_registers_); |
| if (by != 0) |
| masm.addPtr(Imm32(by), register_location(reg)); |
| } |
| |
| void |
| NativeRegExpMacroAssembler::Backtrack() |
| { |
| JitSpew(SPEW_PREFIX "Backtrack"); |
| |
| // Check for an interrupt. |
| Label noInterrupt; |
| masm.branch32(Assembler::Equal, |
| AbsoluteAddress(runtime->addressOfInterruptUint32()), Imm32(0), |
| &noInterrupt); |
| masm.movePtr(ImmWord(RegExpRunStatus_Error), temp0); |
| masm.jump(&exit_label_); |
| masm.bind(&noInterrupt); |
| |
| // Pop code location from backtrack stack and jump to location. |
| PopBacktrack(temp0); |
| masm.jump(temp0); |
| } |
| |
| void |
| NativeRegExpMacroAssembler::Bind(Label* label) |
| { |
| JitSpew(SPEW_PREFIX "Bind"); |
| |
| masm.bind(label); |
| } |
| |
| void |
| NativeRegExpMacroAssembler::CheckAtStart(Label* on_at_start) |
| { |
| JitSpew(SPEW_PREFIX "CheckAtStart"); |
| |
| Label not_at_start; |
| |
| // Did we start the match at the start of the string at all? |
| Address startIndex(masm.getStackPointer(), offsetof(FrameData, startIndex)); |
| masm.branchPtr(Assembler::NotEqual, startIndex, ImmWord(0), ¬_at_start); |
| |
| // If we did, are we still at the start of the input? |
| masm.computeEffectiveAddress(BaseIndex(input_end_pointer, current_position, TimesOne), temp0); |
| |
| Address inputStart(masm.getStackPointer(), offsetof(FrameData, inputStart)); |
| masm.branchPtr(Assembler::Equal, inputStart, temp0, BranchOrBacktrack(on_at_start)); |
| |
| masm.bind(¬_at_start); |
| } |
| |
| void |
| NativeRegExpMacroAssembler::CheckNotAtStart(Label* on_not_at_start) |
| { |
| JitSpew(SPEW_PREFIX "CheckNotAtStart"); |
| |
| // Did we start the match at the start of the string at all? |
| Address startIndex(masm.getStackPointer(), offsetof(FrameData, startIndex)); |
| masm.branchPtr(Assembler::NotEqual, startIndex, ImmWord(0), BranchOrBacktrack(on_not_at_start)); |
| |
| // If we did, are we still at the start of the input? |
| masm.computeEffectiveAddress(BaseIndex(input_end_pointer, current_position, TimesOne), temp0); |
| |
| Address inputStart(masm.getStackPointer(), offsetof(FrameData, inputStart)); |
| masm.branchPtr(Assembler::NotEqual, inputStart, temp0, BranchOrBacktrack(on_not_at_start)); |
| } |
| |
| void |
| NativeRegExpMacroAssembler::CheckCharacter(unsigned c, Label* on_equal) |
| { |
| JitSpew(SPEW_PREFIX "CheckCharacter(%d)", (int) c); |
| masm.branch32(Assembler::Equal, current_character, Imm32(c), BranchOrBacktrack(on_equal)); |
| } |
| |
| void |
| NativeRegExpMacroAssembler::CheckNotCharacter(unsigned c, Label* on_not_equal) |
| { |
| JitSpew(SPEW_PREFIX "CheckNotCharacter(%d)", (int) c); |
| masm.branch32(Assembler::NotEqual, current_character, Imm32(c), BranchOrBacktrack(on_not_equal)); |
| } |
| |
| void |
| NativeRegExpMacroAssembler::CheckCharacterAfterAnd(unsigned c, unsigned and_with, |
| Label* on_equal) |
| { |
| JitSpew(SPEW_PREFIX "CheckCharacterAfterAnd(%d, %d)", (int) c, (int) and_with); |
| |
| if (c == 0) { |
| masm.branchTest32(Assembler::Zero, current_character, Imm32(and_with), |
| BranchOrBacktrack(on_equal)); |
| } else { |
| masm.move32(Imm32(and_with), temp0); |
| masm.and32(current_character, temp0); |
| masm.branch32(Assembler::Equal, temp0, Imm32(c), BranchOrBacktrack(on_equal)); |
| } |
| } |
| |
| void |
| NativeRegExpMacroAssembler::CheckNotCharacterAfterAnd(unsigned c, unsigned and_with, |
| Label* on_not_equal) |
| { |
| JitSpew(SPEW_PREFIX "CheckNotCharacterAfterAnd(%d, %d)", (int) c, (int) and_with); |
| |
| if (c == 0) { |
| masm.branchTest32(Assembler::NonZero, current_character, Imm32(and_with), |
| BranchOrBacktrack(on_not_equal)); |
| } else { |
| masm.move32(Imm32(and_with), temp0); |
| masm.and32(current_character, temp0); |
| masm.branch32(Assembler::NotEqual, temp0, Imm32(c), BranchOrBacktrack(on_not_equal)); |
| } |
| } |
| |
| void |
| NativeRegExpMacroAssembler::CheckCharacterGT(char16_t c, Label* on_greater) |
| { |
| JitSpew(SPEW_PREFIX "CheckCharacterGT(%d)", (int) c); |
| masm.branch32(Assembler::GreaterThan, current_character, Imm32(c), |
| BranchOrBacktrack(on_greater)); |
| } |
| |
| void |
| NativeRegExpMacroAssembler::CheckCharacterLT(char16_t c, Label* on_less) |
| { |
| JitSpew(SPEW_PREFIX "CheckCharacterLT(%d)", (int) c); |
| masm.branch32(Assembler::LessThan, current_character, Imm32(c), BranchOrBacktrack(on_less)); |
| } |
| |
| void |
| NativeRegExpMacroAssembler::CheckGreedyLoop(Label* on_tos_equals_current_position) |
| { |
| JitSpew(SPEW_PREFIX "CheckGreedyLoop"); |
| |
| Label fallthrough; |
| masm.branchPtr(Assembler::NotEqual, |
| Address(backtrack_stack_pointer, -int(sizeof(void*))), current_position, |
| &fallthrough); |
| masm.subPtr(Imm32(sizeof(void*)), backtrack_stack_pointer); // Pop. |
| JumpOrBacktrack(on_tos_equals_current_position); |
| masm.bind(&fallthrough); |
| } |
| |
| void |
| NativeRegExpMacroAssembler::CheckNotBackReference(int start_reg, Label* on_no_match) |
| { |
| JitSpew(SPEW_PREFIX "CheckNotBackReference(%d)", start_reg); |
| |
| Label fallthrough; |
| Label success; |
| Label fail; |
| |
| // Find length of back-referenced capture. |
| masm.loadPtr(register_location(start_reg), current_character); |
| masm.loadPtr(register_location(start_reg + 1), temp0); |
| masm.subPtr(current_character, temp0); // Length to check. |
| |
| // Fail on partial or illegal capture (start of capture after end of capture). |
| masm.branchPtr(Assembler::LessThan, temp0, ImmWord(0), BranchOrBacktrack(on_no_match)); |
| |
| // Succeed on empty capture (including no capture). |
| masm.branchPtr(Assembler::Equal, temp0, ImmWord(0), &fallthrough); |
| |
| // Check that there are sufficient characters left in the input. |
| masm.movePtr(current_position, temp1); |
| masm.addPtr(temp0, temp1); |
| masm.branchPtr(Assembler::GreaterThan, temp1, ImmWord(0), BranchOrBacktrack(on_no_match)); |
| |
| // Save register to make it available below. |
| masm.push(backtrack_stack_pointer); |
| |
| // Compute pointers to match string and capture string |
| masm.computeEffectiveAddress(BaseIndex(input_end_pointer, current_position, TimesOne), temp1); // Start of match. |
| masm.addPtr(input_end_pointer, current_character); // Start of capture. |
| masm.computeEffectiveAddress(BaseIndex(temp0, temp1, TimesOne), backtrack_stack_pointer); // End of match. |
| |
| Label loop; |
| masm.bind(&loop); |
| if (mode_ == ASCII) { |
| masm.load8ZeroExtend(Address(current_character, 0), temp0); |
| masm.load8ZeroExtend(Address(temp1, 0), temp2); |
| } else { |
| MOZ_ASSERT(mode_ == CHAR16); |
| masm.load16ZeroExtend(Address(current_character, 0), temp0); |
| masm.load16ZeroExtend(Address(temp1, 0), temp2); |
| } |
| masm.branch32(Assembler::NotEqual, temp0, temp2, &fail); |
| |
| // Increment pointers into capture and match string. |
| masm.addPtr(Imm32(char_size()), current_character); |
| masm.addPtr(Imm32(char_size()), temp1); |
| |
| // Check if we have reached end of match area. |
| masm.branchPtr(Assembler::Below, temp1, backtrack_stack_pointer, &loop); |
| masm.jump(&success); |
| |
| masm.bind(&fail); |
| |
| // Restore backtrack stack pointer. |
| masm.pop(backtrack_stack_pointer); |
| JumpOrBacktrack(on_no_match); |
| |
| masm.bind(&success); |
| |
| // Move current character position to position after match. |
| masm.movePtr(backtrack_stack_pointer, current_position); |
| masm.subPtr(input_end_pointer, current_position); |
| |
| // Restore backtrack stack pointer. |
| masm.pop(backtrack_stack_pointer); |
| |
| masm.bind(&fallthrough); |
| } |
| |
| void |
| NativeRegExpMacroAssembler::CheckNotBackReferenceIgnoreCase(int start_reg, Label* on_no_match) |
| { |
| JitSpew(SPEW_PREFIX "CheckNotBackReferenceIgnoreCase(%d)", start_reg); |
| |
| Label fallthrough; |
| |
| masm.loadPtr(register_location(start_reg), current_character); // Index of start of capture |
| masm.loadPtr(register_location(start_reg + 1), temp1); // Index of end of capture |
| masm.subPtr(current_character, temp1); // Length of capture. |
| |
| // The length of a capture should not be negative. This can only happen |
| // if the end of the capture is unrecorded, or at a point earlier than |
| // the start of the capture. |
| masm.branchPtr(Assembler::LessThan, temp1, ImmWord(0), BranchOrBacktrack(on_no_match)); |
| |
| // If length is zero, either the capture is empty or it is completely |
| // uncaptured. In either case succeed immediately. |
| masm.branchPtr(Assembler::Equal, temp1, ImmWord(0), &fallthrough); |
| |
| // Check that there are sufficient characters left in the input. |
| masm.movePtr(current_position, temp0); |
| masm.addPtr(temp1, temp0); |
| masm.branchPtr(Assembler::GreaterThan, temp0, ImmWord(0), BranchOrBacktrack(on_no_match)); |
| |
| if (mode_ == ASCII) { |
| Label success, fail; |
| |
| // Save register contents to make the registers available below. After |
| // this, the temp0, temp2, and current_position registers are available. |
| masm.push(current_position); |
| |
| masm.addPtr(input_end_pointer, current_character); // Start of capture. |
| masm.addPtr(input_end_pointer, current_position); // Start of text to match against capture. |
| masm.addPtr(current_position, temp1); // End of text to match against capture. |
| |
| Label loop, loop_increment; |
| masm.bind(&loop); |
| masm.load8ZeroExtend(Address(current_position, 0), temp0); |
| masm.load8ZeroExtend(Address(current_character, 0), temp2); |
| masm.branch32(Assembler::Equal, temp0, temp2, &loop_increment); |
| |
| // Mismatch, try case-insensitive match (converting letters to lower-case). |
| masm.or32(Imm32(0x20), temp0); // Convert match character to lower-case. |
| |
| // Is temp0 a lowercase letter? |
| Label convert_capture; |
| masm.computeEffectiveAddress(Address(temp0, -'a'), temp2); |
| masm.branch32(Assembler::BelowOrEqual, temp2, Imm32(static_cast<int32_t>('z' - 'a')), |
| &convert_capture); |
| |
| // Latin-1: Check for values in range [224,254] but not 247. |
| masm.sub32(Imm32(224 - 'a'), temp2); |
| masm.branch32(Assembler::Above, temp2, Imm32(254 - 224), &fail); |
| |
| // Check for 247. |
| masm.branch32(Assembler::Equal, temp2, Imm32(247 - 224), &fail); |
| |
| masm.bind(&convert_capture); |
| |
| // Also convert capture character. |
| masm.load8ZeroExtend(Address(current_character, 0), temp2); |
| masm.or32(Imm32(0x20), temp2); |
| |
| masm.branch32(Assembler::NotEqual, temp0, temp2, &fail); |
| |
| masm.bind(&loop_increment); |
| |
| // Increment pointers into match and capture strings. |
| masm.addPtr(Imm32(1), current_character); |
| masm.addPtr(Imm32(1), current_position); |
| |
| // Compare to end of match, and loop if not done. |
| masm.branchPtr(Assembler::Below, current_position, temp1, &loop); |
| masm.jump(&success); |
| |
| masm.bind(&fail); |
| |
| // Restore original values before failing. |
| masm.pop(current_position); |
| JumpOrBacktrack(on_no_match); |
| |
| masm.bind(&success); |
| |
| // Drop original character position value. |
| masm.addToStackPtr(Imm32(sizeof(uintptr_t))); |
| |
| // Compute new value of character position after the matched part. |
| masm.subPtr(input_end_pointer, current_position); |
| } else { |
| MOZ_ASSERT(mode_ == CHAR16); |
| |
| // Note: temp1 needs to be saved/restored if it is volatile, as it is used after the call. |
| LiveGeneralRegisterSet volatileRegs(GeneralRegisterSet::Volatile()); |
| volatileRegs.takeUnchecked(temp0); |
| volatileRegs.takeUnchecked(temp2); |
| masm.PushRegsInMask(volatileRegs); |
| |
| // Set byte_offset1. |
| // Start of capture, where current_character already holds string-end negative offset. |
| masm.addPtr(input_end_pointer, current_character); |
| |
| // Set byte_offset2. |
| // Found by adding negative string-end offset of current position |
| // to end of string. |
| masm.addPtr(input_end_pointer, current_position); |
| |
| // Parameters are |
| // Address byte_offset1 - Address captured substring's start. |
| // Address byte_offset2 - Address of current character position. |
| // size_t byte_length - length of capture in bytes(!) |
| masm.setupUnalignedABICall(temp0); |
| masm.passABIArg(current_character); |
| masm.passABIArg(current_position); |
| masm.passABIArg(temp1); |
| int (*fun)(const char16_t*, const char16_t*, size_t) = CaseInsensitiveCompareStrings; |
| masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, fun)); |
| masm.storeCallResult(temp0); |
| |
| masm.PopRegsInMask(volatileRegs); |
| |
| // Check if function returned non-zero for success or zero for failure. |
| masm.branchTest32(Assembler::Zero, temp0, temp0, BranchOrBacktrack(on_no_match)); |
| |
| // On success, increment position by length of capture. |
| masm.addPtr(temp1, current_position); |
| } |
| |
| masm.bind(&fallthrough); |
| } |
| |
| void |
| NativeRegExpMacroAssembler::CheckNotCharacterAfterMinusAnd(char16_t c, char16_t minus, char16_t and_with, |
| Label* on_not_equal) |
| { |
| JitSpew(SPEW_PREFIX "CheckNotCharacterAfterMinusAnd(%d, %d, %d)", (int) c, |
| (int) minus, (int) and_with); |
| |
| masm.computeEffectiveAddress(Address(current_character, -minus), temp0); |
| if (c == 0) { |
| masm.branchTest32(Assembler::NonZero, temp0, Imm32(and_with), |
| BranchOrBacktrack(on_not_equal)); |
| } else { |
| masm.and32(Imm32(and_with), temp0); |
| masm.branch32(Assembler::NotEqual, temp0, Imm32(c), BranchOrBacktrack(on_not_equal)); |
| } |
| } |
| |
| void |
| NativeRegExpMacroAssembler::CheckCharacterInRange(char16_t from, char16_t to, |
| Label* on_in_range) |
| { |
| JitSpew(SPEW_PREFIX "CheckCharacterInRange(%d, %d)", (int) from, (int) to); |
| |
| masm.computeEffectiveAddress(Address(current_character, -from), temp0); |
| masm.branch32(Assembler::BelowOrEqual, temp0, Imm32(to - from), BranchOrBacktrack(on_in_range)); |
| } |
| |
| void |
| NativeRegExpMacroAssembler::CheckCharacterNotInRange(char16_t from, char16_t to, |
| Label* on_not_in_range) |
| { |
| JitSpew(SPEW_PREFIX "CheckCharacterNotInRange(%d, %d)", (int) from, (int) to); |
| |
| masm.computeEffectiveAddress(Address(current_character, -from), temp0); |
| masm.branch32(Assembler::Above, temp0, Imm32(to - from), BranchOrBacktrack(on_not_in_range)); |
| } |
| |
| void |
| NativeRegExpMacroAssembler::CheckBitInTable(uint8_t* table, Label* on_bit_set) |
| { |
| JitSpew(SPEW_PREFIX "CheckBitInTable"); |
| |
| masm.movePtr(ImmPtr(table), temp0); |
| |
| // kTableMask is currently 127, so we need to mask even if the input is |
| // Latin1. V8 has the same issue. |
| static_assert(JSString::MAX_LATIN1_CHAR > kTableMask, |
| "No need to mask if MAX_LATIN1_CHAR <= kTableMask"); |
| masm.move32(Imm32(kTableSize - 1), temp1); |
| masm.and32(current_character, temp1); |
| |
| masm.load8ZeroExtend(BaseIndex(temp0, temp1, TimesOne), temp0); |
| masm.branchTest32(Assembler::NonZero, temp0, temp0, BranchOrBacktrack(on_bit_set)); |
| } |
| |
| void |
| NativeRegExpMacroAssembler::Fail() |
| { |
| JitSpew(SPEW_PREFIX "Fail"); |
| |
| if (!global()) |
| masm.movePtr(ImmWord(RegExpRunStatus_Success_NotFound), temp0); |
| masm.jump(&exit_label_); |
| } |
| |
| void |
| NativeRegExpMacroAssembler::IfRegisterGE(int reg, int comparand, Label* if_ge) |
| { |
| JitSpew(SPEW_PREFIX "IfRegisterGE(%d, %d)", reg, comparand); |
| masm.branchPtr(Assembler::GreaterThanOrEqual, register_location(reg), ImmWord(comparand), |
| BranchOrBacktrack(if_ge)); |
| } |
| |
| void |
| NativeRegExpMacroAssembler::IfRegisterLT(int reg, int comparand, Label* if_lt) |
| { |
| JitSpew(SPEW_PREFIX "IfRegisterLT(%d, %d)", reg, comparand); |
| masm.branchPtr(Assembler::LessThan, register_location(reg), ImmWord(comparand), |
| BranchOrBacktrack(if_lt)); |
| } |
| |
| void |
| NativeRegExpMacroAssembler::IfRegisterEqPos(int reg, Label* if_eq) |
| { |
| JitSpew(SPEW_PREFIX "IfRegisterEqPos(%d)", reg); |
| masm.branchPtr(Assembler::Equal, register_location(reg), current_position, |
| BranchOrBacktrack(if_eq)); |
| } |
| |
| void |
| NativeRegExpMacroAssembler::LoadCurrentCharacter(int cp_offset, Label* on_end_of_input, |
| bool check_bounds, int characters) |
| { |
| JitSpew(SPEW_PREFIX "LoadCurrentCharacter(%d, %d)", cp_offset, characters); |
| |
| MOZ_ASSERT(cp_offset >= -1); // ^ and \b can look behind one character. |
| MOZ_ASSERT(cp_offset < (1<<30)); // Be sane! (And ensure negation works) |
| if (check_bounds) |
| CheckPosition(cp_offset + characters - 1, on_end_of_input); |
| LoadCurrentCharacterUnchecked(cp_offset, characters); |
| } |
| |
| void |
| NativeRegExpMacroAssembler::LoadCurrentCharacterUnchecked(int cp_offset, int characters) |
| { |
| JitSpew(SPEW_PREFIX "LoadCurrentCharacterUnchecked(%d, %d)", cp_offset, characters); |
| |
| if (mode_ == ASCII) { |
| BaseIndex address(input_end_pointer, current_position, TimesOne, cp_offset); |
| if (characters == 4) { |
| masm.load32(address, current_character); |
| } else if (characters == 2) { |
| masm.load16ZeroExtend(address, current_character); |
| } else { |
| MOZ_ASSERT(characters == 1); |
| masm.load8ZeroExtend(address, current_character); |
| } |
| } else { |
| MOZ_ASSERT(mode_ == CHAR16); |
| MOZ_ASSERT(characters <= 2); |
| BaseIndex address(input_end_pointer, current_position, TimesOne, cp_offset * sizeof(char16_t)); |
| if (characters == 2) |
| masm.load32(address, current_character); |
| else |
| masm.load16ZeroExtend(address, current_character); |
| } |
| } |
| |
| void |
| NativeRegExpMacroAssembler::PopCurrentPosition() |
| { |
| JitSpew(SPEW_PREFIX "PopCurrentPosition"); |
| |
| PopBacktrack(current_position); |
| } |
| |
| void |
| NativeRegExpMacroAssembler::PopRegister(int register_index) |
| { |
| JitSpew(SPEW_PREFIX "PopRegister(%d)", register_index); |
| |
| PopBacktrack(temp0); |
| masm.storePtr(temp0, register_location(register_index)); |
| } |
| |
| void |
| NativeRegExpMacroAssembler::PushBacktrack(Label* label) |
| { |
| JitSpew(SPEW_PREFIX "PushBacktrack"); |
| |
| CodeOffset patchOffset = masm.movWithPatch(ImmPtr(nullptr), temp0); |
| |
| MOZ_ASSERT(!label->bound()); |
| |
| { |
| AutoEnterOOMUnsafeRegion oomUnsafe; |
| if (!labelPatches.append(LabelPatch(label, patchOffset))) |
| oomUnsafe.crash("NativeRegExpMacroAssembler::PushBacktrack"); |
| } |
| |
| PushBacktrack(temp0); |
| CheckBacktrackStackLimit(); |
| } |
| |
| void |
| NativeRegExpMacroAssembler::BindBacktrack(Label* label) |
| { |
| JitSpew(SPEW_PREFIX "BindBacktrack"); |
| |
| Bind(label); |
| |
| for (size_t i = 0; i < labelPatches.length(); i++) { |
| LabelPatch& v = labelPatches[i]; |
| if (v.label == label) { |
| v.labelOffset = label->offset(); |
| v.label = nullptr; |
| break; |
| } |
| } |
| } |
| |
| void |
| NativeRegExpMacroAssembler::PushBacktrack(Register source) |
| { |
| JitSpew(SPEW_PREFIX "PushBacktrack"); |
| |
| MOZ_ASSERT(source != backtrack_stack_pointer); |
| |
| // Notice: This updates flags, unlike normal Push. |
| masm.storePtr(source, Address(backtrack_stack_pointer, 0)); |
| masm.addPtr(Imm32(sizeof(void*)), backtrack_stack_pointer); |
| } |
| |
| void |
| NativeRegExpMacroAssembler::PushBacktrack(int32_t value) |
| { |
| JitSpew(SPEW_PREFIX "PushBacktrack(%d)", (int) value); |
| |
| // Notice: This updates flags, unlike normal Push. |
| masm.storePtr(ImmWord(value), Address(backtrack_stack_pointer, 0)); |
| masm.addPtr(Imm32(sizeof(void*)), backtrack_stack_pointer); |
| } |
| |
| void |
| NativeRegExpMacroAssembler::PopBacktrack(Register target) |
| { |
| JitSpew(SPEW_PREFIX "PopBacktrack"); |
| |
| MOZ_ASSERT(target != backtrack_stack_pointer); |
| |
| // Notice: This updates flags, unlike normal Pop. |
| masm.subPtr(Imm32(sizeof(void*)), backtrack_stack_pointer); |
| masm.loadPtr(Address(backtrack_stack_pointer, 0), target); |
| } |
| |
| void |
| NativeRegExpMacroAssembler::CheckBacktrackStackLimit() |
| { |
| JitSpew(SPEW_PREFIX "CheckBacktrackStackLimit"); |
| |
| const void* limitAddr = runtime->regexpStack.addressOfLimit(); |
| |
| Label no_stack_overflow; |
| masm.branchPtr(Assembler::AboveOrEqual, AbsoluteAddress(limitAddr), |
| backtrack_stack_pointer, &no_stack_overflow); |
| |
| // Copy the stack pointer before the call() instruction modifies it. |
| masm.moveStackPtrTo(temp2); |
| |
| masm.call(&stack_overflow_label_); |
| masm.bind(&no_stack_overflow); |
| |
| // Exit with an exception if the call failed. |
| masm.branchTest32(Assembler::Zero, temp0, temp0, &exit_with_exception_label_); |
| } |
| |
| void |
| NativeRegExpMacroAssembler::PushCurrentPosition() |
| { |
| JitSpew(SPEW_PREFIX "PushCurrentPosition"); |
| |
| PushBacktrack(current_position); |
| } |
| |
| void |
| NativeRegExpMacroAssembler::PushRegister(int register_index, StackCheckFlag check_stack_limit) |
| { |
| JitSpew(SPEW_PREFIX "PushRegister(%d)", register_index); |
| |
| masm.loadPtr(register_location(register_index), temp0); |
| PushBacktrack(temp0); |
| if (check_stack_limit) |
| CheckBacktrackStackLimit(); |
| } |
| |
| void |
| NativeRegExpMacroAssembler::ReadCurrentPositionFromRegister(int reg) |
| { |
| JitSpew(SPEW_PREFIX "ReadCurrentPositionFromRegister(%d)", reg); |
| |
| masm.loadPtr(register_location(reg), current_position); |
| } |
| |
| void |
| NativeRegExpMacroAssembler::WriteCurrentPositionToRegister(int reg, int cp_offset) |
| { |
| JitSpew(SPEW_PREFIX "WriteCurrentPositionToRegister(%d, %d)", reg, cp_offset); |
| |
| if (cp_offset == 0) { |
| masm.storePtr(current_position, register_location(reg)); |
| } else { |
| masm.computeEffectiveAddress(Address(current_position, cp_offset * char_size()), temp0); |
| masm.storePtr(temp0, register_location(reg)); |
| } |
| } |
| |
| void |
| NativeRegExpMacroAssembler::ReadBacktrackStackPointerFromRegister(int reg) |
| { |
| JitSpew(SPEW_PREFIX "ReadBacktrackStackPointerFromRegister(%d)", reg); |
| |
| masm.loadPtr(register_location(reg), backtrack_stack_pointer); |
| masm.addPtr(Address(masm.getStackPointer(), |
| offsetof(FrameData, backtrackStackBase)), backtrack_stack_pointer); |
| } |
| |
| void |
| NativeRegExpMacroAssembler::WriteBacktrackStackPointerToRegister(int reg) |
| { |
| JitSpew(SPEW_PREFIX "WriteBacktrackStackPointerToRegister(%d)", reg); |
| |
| masm.movePtr(backtrack_stack_pointer, temp0); |
| masm.subPtr(Address(masm.getStackPointer(), |
| offsetof(FrameData, backtrackStackBase)), temp0); |
| masm.storePtr(temp0, register_location(reg)); |
| } |
| |
| void |
| NativeRegExpMacroAssembler::SetCurrentPositionFromEnd(int by) |
| { |
| JitSpew(SPEW_PREFIX "SetCurrentPositionFromEnd(%d)", by); |
| |
| Label after_position; |
| masm.branchPtr(Assembler::GreaterThanOrEqual, current_position, |
| ImmWord(-by * char_size()), &after_position); |
| masm.movePtr(ImmWord(-by * char_size()), current_position); |
| |
| // On RegExp code entry (where this operation is used), the character before |
| // the current position is expected to be already loaded. |
| // We have advanced the position, so it's safe to read backwards. |
| LoadCurrentCharacterUnchecked(-1, 1); |
| masm.bind(&after_position); |
| } |
| |
| void |
| NativeRegExpMacroAssembler::SetRegister(int register_index, int to) |
| { |
| JitSpew(SPEW_PREFIX "SetRegister(%d, %d)", register_index, to); |
| |
| MOZ_ASSERT(register_index >= num_saved_registers_); // Reserved for positions! |
| masm.storePtr(ImmWord(to), register_location(register_index)); |
| } |
| |
| bool |
| NativeRegExpMacroAssembler::Succeed() |
| { |
| JitSpew(SPEW_PREFIX "Succeed"); |
| |
| masm.jump(&success_label_); |
| return global(); |
| } |
| |
| void |
| NativeRegExpMacroAssembler::ClearRegisters(int reg_from, int reg_to) |
| { |
| JitSpew(SPEW_PREFIX "ClearRegisters(%d, %d)", reg_from, reg_to); |
| |
| MOZ_ASSERT(reg_from <= reg_to); |
| masm.loadPtr(Address(masm.getStackPointer(), offsetof(FrameData, inputStartMinusOne)), temp0); |
| for (int reg = reg_from; reg <= reg_to; reg++) |
| masm.storePtr(temp0, register_location(reg)); |
| } |
| |
| void |
| NativeRegExpMacroAssembler::CheckPosition(int cp_offset, Label* on_outside_input) |
| { |
| JitSpew(SPEW_PREFIX "CheckPosition(%d)", cp_offset); |
| masm.branchPtr(Assembler::GreaterThanOrEqual, current_position, |
| ImmWord(-cp_offset * char_size()), BranchOrBacktrack(on_outside_input)); |
| } |
| |
| Label* |
| NativeRegExpMacroAssembler::BranchOrBacktrack(Label* branch) |
| { |
| if (branch) |
| return branch; |
| return &backtrack_label_; |
| } |
| |
| void |
| NativeRegExpMacroAssembler::JumpOrBacktrack(Label* to) |
| { |
| JitSpew(SPEW_PREFIX "JumpOrBacktrack"); |
| |
| if (to) |
| masm.jump(to); |
| else |
| Backtrack(); |
| } |
| |
| bool |
| NativeRegExpMacroAssembler::CheckSpecialCharacterClass(char16_t type, Label* on_no_match) |
| { |
| JitSpew(SPEW_PREFIX "CheckSpecialCharacterClass(%d)", (int) type); |
| |
| Label* branch = BranchOrBacktrack(on_no_match); |
| |
| // Range checks (c in min..max) are generally implemented by an unsigned |
| // (c - min) <= (max - min) check |
| switch (type) { |
| case 's': |
| // Match space-characters. |
| if (mode_ == ASCII) { |
| // One byte space characters are '\t'..'\r', ' ' and \u00a0. |
| Label success; |
| masm.branch32(Assembler::Equal, current_character, Imm32(' '), &success); |
| |
| // Check range 0x09..0x0d. |
| masm.computeEffectiveAddress(Address(current_character, -'\t'), temp0); |
| masm.branch32(Assembler::BelowOrEqual, temp0, Imm32('\r' - '\t'), &success); |
| |
| // \u00a0 (NBSP). |
| masm.branch32(Assembler::NotEqual, temp0, Imm32(0x00a0 - '\t'), branch); |
| |
| masm.bind(&success); |
| return true; |
| } |
| return false; |
| case 'S': |
| // The emitted code for generic character classes is good enough. |
| return false; |
| case 'd': |
| // Match ASCII digits ('0'..'9') |
| masm.computeEffectiveAddress(Address(current_character, -'0'), temp0); |
| masm.branch32(Assembler::Above, temp0, Imm32('9' - '0'), branch); |
| return true; |
| case 'D': |
| // Match non ASCII-digits |
| masm.computeEffectiveAddress(Address(current_character, -'0'), temp0); |
| masm.branch32(Assembler::BelowOrEqual, temp0, Imm32('9' - '0'), branch); |
| return true; |
| case '.': { |
| // Match non-newlines (not 0x0a('\n'), 0x0d('\r'), 0x2028 and 0x2029) |
| masm.move32(current_character, temp0); |
| masm.xor32(Imm32(0x01), temp0); |
| |
| // See if current character is '\n'^1 or '\r'^1, i.e., 0x0b or 0x0c |
| masm.sub32(Imm32(0x0b), temp0); |
| masm.branch32(Assembler::BelowOrEqual, temp0, Imm32(0x0c - 0x0b), branch); |
| if (mode_ == CHAR16) { |
| // Compare original value to 0x2028 and 0x2029, using the already |
| // computed (current_char ^ 0x01 - 0x0b). I.e., check for |
| // 0x201d (0x2028 - 0x0b) or 0x201e. |
| masm.sub32(Imm32(0x2028 - 0x0b), temp0); |
| masm.branch32(Assembler::BelowOrEqual, temp0, Imm32(0x2029 - 0x2028), branch); |
| } |
| return true; |
| } |
| case 'w': { |
| if (mode_ != ASCII) { |
| // Table is 128 entries, so all ASCII characters can be tested. |
| masm.branch32(Assembler::Above, current_character, Imm32('z'), branch); |
| } |
| MOZ_ASSERT(0 == word_character_map[0]); // Character '\0' is not a word char. |
| masm.movePtr(ImmPtr(word_character_map), temp0); |
| masm.load8ZeroExtend(BaseIndex(temp0, current_character, TimesOne), temp0); |
| masm.branchTest32(Assembler::Zero, temp0, temp0, branch); |
| return true; |
| } |
| case 'W': { |
| Label done; |
| if (mode_ != ASCII) { |
| // Table is 128 entries, so all ASCII characters can be tested. |
| masm.branch32(Assembler::Above, current_character, Imm32('z'), &done); |
| } |
| MOZ_ASSERT(0 == word_character_map[0]); // Character '\0' is not a word char. |
| masm.movePtr(ImmPtr(word_character_map), temp0); |
| masm.load8ZeroExtend(BaseIndex(temp0, current_character, TimesOne), temp0); |
| masm.branchTest32(Assembler::NonZero, temp0, temp0, branch); |
| if (mode_ != ASCII) |
| masm.bind(&done); |
| return true; |
| } |
| // Non-standard classes (with no syntactic shorthand) used internally. |
| case '*': |
| // Match any character. |
| return true; |
| case 'n': { |
| // Match newlines (0x0a('\n'), 0x0d('\r'), 0x2028 or 0x2029). |
| // The opposite of '.'. |
| masm.move32(current_character, temp0); |
| masm.xor32(Imm32(0x01), temp0); |
| |
| // See if current character is '\n'^1 or '\r'^1, i.e., 0x0b or 0x0c |
| masm.sub32(Imm32(0x0b), temp0); |
| |
| if (mode_ == ASCII) { |
| masm.branch32(Assembler::Above, temp0, Imm32(0x0c - 0x0b), branch); |
| } else { |
| Label done; |
| masm.branch32(Assembler::BelowOrEqual, temp0, Imm32(0x0c - 0x0b), &done); |
| MOZ_ASSERT(CHAR16 == mode_); |
| |
| // Compare original value to 0x2028 and 0x2029, using the already |
| // computed (current_char ^ 0x01 - 0x0b). I.e., check for |
| // 0x201d (0x2028 - 0x0b) or 0x201e. |
| masm.sub32(Imm32(0x2028 - 0x0b), temp0); |
| masm.branch32(Assembler::Above, temp0, Imm32(1), branch); |
| |
| masm.bind(&done); |
| } |
| return true; |
| } |
| // No custom implementation (yet): |
| default: |
| return false; |
| } |
| } |
| |
| bool |
| NativeRegExpMacroAssembler::CanReadUnaligned() |
| { |
| #if defined(JS_CODEGEN_ARM) |
| return !jit::HasAlignmentFault(); |
| #elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64) |
| return false; |
| #else |
| return true; |
| #endif |
| } |
| |
| const uint8_t |
| NativeRegExpMacroAssembler::word_character_map[] = |
| { |
| 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, |
| 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, |
| 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, |
| 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, |
| |
| 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, |
| 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, |
| 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, // '0' - '7' |
| 0xffu, 0xffu, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, // '8' - '9' |
| |
| 0x00u, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, // 'A' - 'G' |
| 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, // 'H' - 'O' |
| 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, // 'P' - 'W' |
| 0xffu, 0xffu, 0xffu, 0x00u, 0x00u, 0x00u, 0x00u, 0xffu, // 'X' - 'Z', '_' |
| |
| 0x00u, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, // 'a' - 'g' |
| 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, // 'h' - 'o' |
| 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, // 'p' - 'w' |
| 0xffu, 0xffu, 0xffu, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, // 'x' - 'z' |
| |
| // Latin-1 range |
| 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, |
| 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, |
| 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, |
| 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, |
| |
| 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, |
| 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, |
| 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, |
| 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, |
| |
| 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, |
| 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, |
| 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, |
| 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, |
| |
| 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, |
| 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, |
| 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, |
| 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, |
| }; |