Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=8 sts=4 et sw=4 tw=99: */
3 :
4 : // Copyright 2012 the V8 project authors. All rights reserved.
5 : // Redistribution and use in source and binary forms, with or without
6 : // modification, are permitted provided that the following conditions are
7 : // met:
8 : //
9 : // * Redistributions of source code must retain the above copyright
10 : // notice, this list of conditions and the following disclaimer.
11 : // * Redistributions in binary form must reproduce the above
12 : // copyright notice, this list of conditions and the following
13 : // disclaimer in the documentation and/or other materials provided
14 : // with the distribution.
15 : // * Neither the name of Google Inc. nor the names of its
16 : // contributors may be used to endorse or promote products derived
17 : // from this software without specific prior written permission.
18 : //
19 : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 : // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 : // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 : // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 : // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 : // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 : // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 : // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 : // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 : // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 :
31 : #include "irregexp/NativeRegExpMacroAssembler.h"
32 :
33 : #include "irregexp/RegExpStack.h"
34 : #include "jit/Linker.h"
35 : #ifdef JS_ION_PERF
36 : # include "jit/PerfSpewer.h"
37 : #endif
38 : #include "vm/MatchPairs.h"
39 : #include "vtune/VTuneWrapper.h"
40 :
41 : #include "jit/MacroAssembler-inl.h"
42 :
43 : using namespace js;
44 : using namespace js::irregexp;
45 : using namespace js::jit;
46 :
47 : /*
48 : * This assembler uses the following register assignment convention:
49 : *
50 : * - current_character :
51 : * Must be loaded using LoadCurrentCharacter before using any of the
52 : * dispatch methods. Temporarily stores the index of capture start after a
53 : * matching pass for a global regexp.
54 : * - current_position :
55 : * Current position in input, as negative offset from end of string.
56 : * Please notice that this is the byte offset, not the character offset!
57 : * - input_end_pointer :
58 : * Points to byte after last character in the input.
59 : * - backtrack_stack_pointer :
60 : * Points to tip of the heap allocated backtrack stack
61 : * - StackPointer :
62 : * Points to tip of the native stack, used to access arguments, local
63 : * variables and RegExp registers.
64 : *
65 : * The tempN registers are free to use for computations.
66 : */
67 :
68 40 : NativeRegExpMacroAssembler::NativeRegExpMacroAssembler(JSContext* cx, LifoAlloc* alloc,
69 : Mode mode, int registers_to_save,
70 40 : RegExpShared::JitCodeTables& tables)
71 : : RegExpMacroAssembler(cx, *alloc, registers_to_save),
72 40 : tables(tables), cx(cx), mode_(mode)
73 : {
74 : // Find physical registers for each compiler register.
75 40 : AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
76 :
77 40 : input_end_pointer = regs.takeAny();
78 40 : current_character = regs.takeAny();
79 40 : current_position = regs.takeAny();
80 40 : backtrack_stack_pointer = regs.takeAny();
81 40 : temp0 = regs.takeAny();
82 40 : temp1 = regs.takeAny();
83 40 : temp2 = regs.takeAny();
84 :
85 40 : JitSpew(JitSpew_Codegen,
86 : "Starting RegExp (input_end_pointer %s) (current_character %s)"
87 : " (current_position %s) (backtrack_stack_pointer %s) (temp0 %s) temp1 (%s) temp2 (%s)",
88 : input_end_pointer.name(),
89 : current_character.name(),
90 : current_position.name(),
91 : backtrack_stack_pointer.name(),
92 : temp0.name(),
93 : temp1.name(),
94 40 : temp2.name());
95 :
96 40 : savedNonVolatileRegisters = SavedNonVolatileRegisters(regs);
97 :
98 40 : masm.jump(&entry_label_);
99 40 : masm.bind(&start_label_);
100 40 : }
101 :
102 : #define SPEW_PREFIX JitSpew_Codegen, "!!! "
103 :
104 : // The signature of the code which this generates is:
105 : //
106 : // void execute(InputOutputData*);
107 : RegExpCode
108 40 : NativeRegExpMacroAssembler::GenerateCode(JSContext* cx, bool match_only)
109 : {
110 40 : if (!cx->compartment()->ensureJitCompartmentExists(cx))
111 0 : return RegExpCode();
112 :
113 40 : JitSpew(SPEW_PREFIX "GenerateCode");
114 :
115 : // We need an even number of registers, for stack alignment.
116 40 : if (num_registers_ % 2 == 1)
117 1 : num_registers_++;
118 :
119 80 : Label return_temp0;
120 :
121 : // Finalize code - write the entry point code now we know how many
122 : // registers we need.
123 40 : masm.bind(&entry_label_);
124 :
125 : #ifdef JS_CODEGEN_ARM64
126 : // ARM64 communicates stack address via sp, but uses a pseudo-sp for addressing.
127 : masm.initStackPtr();
128 : #endif
129 :
130 : // Push non-volatile registers which might be modified by jitcode.
131 40 : size_t pushedNonVolatileRegisters = 0;
132 120 : for (GeneralRegisterForwardIterator iter(savedNonVolatileRegisters); iter.more(); ++iter) {
133 80 : masm.Push(*iter);
134 80 : pushedNonVolatileRegisters++;
135 : }
136 :
137 : #if defined(XP_IOS) && defined(JS_CODEGEN_ARM)
138 : // The stack is 4-byte aligned on iOS, force 8-byte alignment.
139 : masm.movePtr(StackPointer, temp0);
140 : masm.andPtr(Imm32(~7), StackPointer);
141 : masm.push(temp0);
142 : masm.push(temp0);
143 : #endif
144 :
145 : #ifndef JS_CODEGEN_X86
146 : // The InputOutputData* is stored as an argument, save it on the stack
147 : // above the frame.
148 40 : masm.Push(IntArgReg0);
149 : #endif
150 :
151 40 : size_t frameSize = sizeof(FrameData) + num_registers_ * sizeof(void*);
152 40 : frameSize = JS_ROUNDUP(frameSize + masm.framePushed(), ABIStackAlignment) - masm.framePushed();
153 :
154 : // Actually emit code to start a new stack frame.
155 40 : masm.reserveStack(frameSize);
156 40 : masm.checkStackAlignment();
157 :
158 : // Check if we have space on the stack. Use the *NoInterrupt stack limit to
159 : // avoid failing repeatedly when the regex code is called from Ion JIT code,
160 : // see bug 1208819.
161 80 : Label stack_ok;
162 40 : void* context_addr = cx->zone()->group()->addressOfOwnerContext();
163 40 : masm.loadPtr(AbsoluteAddress(context_addr), temp0);
164 40 : Address limit_addr(temp0, offsetof(JSContext, jitStackLimitNoInterrupt));
165 40 : masm.branchStackPtrRhs(Assembler::Below, limit_addr, &stack_ok);
166 :
167 : // Exit with an exception. There is not enough space on the stack
168 : // for our working registers.
169 40 : masm.movePtr(ImmWord(RegExpRunStatus_Error), temp0);
170 40 : masm.jump(&return_temp0);
171 :
172 40 : masm.bind(&stack_ok);
173 :
174 : #ifdef XP_WIN
175 : // Ensure that we write to each stack page, in order. Skipping a page
176 : // on Windows can cause segmentation faults. Assuming page size is 4k.
177 : const int kPageSize = 4096;
178 : for (int i = frameSize - sizeof(void*); i >= 0; i -= kPageSize)
179 : masm.storePtr(temp0, Address(masm.getStackPointer(), i));
180 : #endif // XP_WIN
181 :
182 : #ifndef JS_CODEGEN_X86
183 : // The InputOutputData* is stored on the stack immediately above the frame.
184 40 : Address inputOutputAddress(masm.getStackPointer(), frameSize);
185 : #else
186 : // The InputOutputData* is left in its original on stack location.
187 : Address inputOutputAddress(masm.getStackPointer(),
188 : frameSize + (pushedNonVolatileRegisters + 1) * sizeof(void*));
189 : #endif
190 :
191 40 : masm.loadPtr(inputOutputAddress, temp0);
192 :
193 : // Copy output registers to FrameData.
194 40 : if (!match_only) {
195 22 : Register matchPairsRegister = input_end_pointer;
196 22 : masm.loadPtr(Address(temp0, offsetof(InputOutputData, matches)), matchPairsRegister);
197 22 : masm.loadPtr(Address(matchPairsRegister, MatchPairs::offsetOfPairs()), temp1);
198 22 : masm.storePtr(temp1, Address(masm.getStackPointer(), offsetof(FrameData, outputRegisters)));
199 22 : masm.load32(Address(matchPairsRegister, MatchPairs::offsetOfPairCount()), temp1);
200 22 : masm.lshiftPtr(Imm32(1), temp1);
201 22 : masm.store32(temp1, Address(masm.getStackPointer(), offsetof(FrameData, numOutputRegisters)));
202 :
203 : #ifdef DEBUG
204 : // Bounds check numOutputRegisters.
205 44 : Label enoughRegisters;
206 44 : masm.branchPtr(Assembler::GreaterThanOrEqual,
207 44 : temp1, ImmWord(num_saved_registers_), &enoughRegisters);
208 22 : masm.assumeUnreachable("Not enough output registers for RegExp");
209 22 : masm.bind(&enoughRegisters);
210 : #endif
211 : } else {
212 18 : Register endIndexRegister = input_end_pointer;
213 18 : masm.loadPtr(Address(temp0, offsetof(InputOutputData, endIndex)), endIndexRegister);
214 18 : masm.storePtr(endIndexRegister, Address(masm.getStackPointer(), offsetof(FrameData, endIndex)));
215 : }
216 :
217 : // Load string end pointer.
218 40 : masm.loadPtr(Address(temp0, offsetof(InputOutputData, inputEnd)), input_end_pointer);
219 :
220 : // Load input start pointer, and copy to FrameData.
221 40 : masm.loadPtr(Address(temp0, offsetof(InputOutputData, inputStart)), current_position);
222 40 : masm.storePtr(current_position, Address(masm.getStackPointer(), offsetof(FrameData, inputStart)));
223 :
224 : // Load start index, and copy to FrameData.
225 40 : masm.loadPtr(Address(temp0, offsetof(InputOutputData, startIndex)), temp1);
226 40 : masm.storePtr(temp1, Address(masm.getStackPointer(), offsetof(FrameData, startIndex)));
227 :
228 : // Set up input position to be negative offset from string end.
229 40 : masm.subPtr(input_end_pointer, current_position);
230 :
231 : // Set temp0 to address of char before start of the string.
232 : // (effectively string position -1).
233 40 : masm.computeEffectiveAddress(Address(current_position, -char_size()), temp0);
234 :
235 : // Store this value on the frame, for use when clearing
236 : // position registers.
237 40 : masm.storePtr(temp0, Address(masm.getStackPointer(), offsetof(FrameData, inputStartMinusOne)));
238 :
239 : // Update current position based on start index.
240 40 : masm.computeEffectiveAddress(BaseIndex(current_position, temp1, factor()), current_position);
241 :
242 80 : Label load_char_start_regexp, start_regexp;
243 :
244 : // Load newline if index is at start, previous character otherwise.
245 120 : masm.branchPtr(Assembler::NotEqual,
246 80 : Address(masm.getStackPointer(), offsetof(FrameData, startIndex)), ImmWord(0),
247 40 : &load_char_start_regexp);
248 40 : masm.movePtr(ImmWord('\n'), current_character);
249 40 : masm.jump(&start_regexp);
250 :
251 : // Global regexp restarts matching here.
252 40 : masm.bind(&load_char_start_regexp);
253 :
254 : // Load previous char as initial value of current character register.
255 40 : LoadCurrentCharacterUnchecked(-1, 1);
256 40 : masm.bind(&start_regexp);
257 :
258 : // Initialize on-stack registers.
259 40 : MOZ_ASSERT(num_saved_registers_ > 0);
260 :
261 : // Fill saved registers with initial value = start offset - 1
262 : // Fill in stack push order, to avoid accessing across an unwritten
263 : // page (a problem on Windows).
264 40 : if (num_saved_registers_ > 8) {
265 1 : masm.movePtr(ImmWord(register_offset(0)), temp1);
266 2 : Label init_loop;
267 1 : masm.bind(&init_loop);
268 1 : masm.storePtr(temp0, BaseIndex(masm.getStackPointer(), temp1, TimesOne));
269 1 : masm.addPtr(ImmWord(sizeof(void*)), temp1);
270 2 : masm.branchPtr(Assembler::LessThan, temp1,
271 2 : ImmWord(register_offset(num_saved_registers_)), &init_loop);
272 : } else {
273 : // Unroll the loop.
274 149 : for (int i = 0; i < num_saved_registers_; i++)
275 110 : masm.storePtr(temp0, register_location(i));
276 : }
277 :
278 : // Initialize backtrack stack pointer.
279 40 : size_t baseOffset = offsetof(JSContext, regexpStack) + RegExpStack::offsetOfBase();
280 40 : masm.loadPtr(AbsoluteAddress(context_addr), backtrack_stack_pointer);
281 40 : masm.loadPtr(Address(backtrack_stack_pointer, baseOffset), backtrack_stack_pointer);
282 40 : masm.storePtr(backtrack_stack_pointer,
283 80 : Address(masm.getStackPointer(), offsetof(FrameData, backtrackStackBase)));
284 :
285 40 : masm.jump(&start_label_);
286 :
287 : // Exit code:
288 40 : if (success_label_.used()) {
289 39 : MOZ_ASSERT(num_saved_registers_ > 0);
290 :
291 39 : Address outputRegistersAddress(masm.getStackPointer(), offsetof(FrameData, outputRegisters));
292 :
293 : // Save captures when successful.
294 39 : masm.bind(&success_label_);
295 :
296 39 : if (!match_only) {
297 21 : Register outputRegisters = temp1;
298 21 : Register inputByteLength = backtrack_stack_pointer;
299 :
300 21 : masm.loadPtr(outputRegistersAddress, outputRegisters);
301 :
302 21 : masm.loadPtr(inputOutputAddress, temp0);
303 21 : masm.loadPtr(Address(temp0, offsetof(InputOutputData, inputEnd)), inputByteLength);
304 21 : masm.subPtr(Address(temp0, offsetof(InputOutputData, inputStart)), inputByteLength);
305 :
306 : // Copy captures to output. Note that registers on the C stack are pointer width
307 : // so that they might hold pointers, but output registers are int32_t.
308 101 : for (int i = 0; i < num_saved_registers_; i++) {
309 80 : masm.loadPtr(register_location(i), temp0);
310 80 : if (i == 0 && global_with_zero_length_check()) {
311 : // Keep capture start in current_character for the zero-length check later.
312 0 : masm.movePtr(temp0, current_character);
313 : }
314 :
315 : // Convert to index from start of string, not end.
316 80 : masm.addPtr(inputByteLength, temp0);
317 :
318 : // Convert byte index to character index.
319 80 : if (mode_ == CHAR16)
320 16 : masm.rshiftPtrArithmetic(Imm32(1), temp0);
321 :
322 80 : masm.store32(temp0, Address(outputRegisters, i * sizeof(int32_t)));
323 : }
324 : }
325 :
326 : // Restart matching if the regular expression is flagged as global.
327 39 : if (global()) {
328 : // Increment success counter.
329 0 : masm.add32(Imm32(1), Address(masm.getStackPointer(), offsetof(FrameData, successfulCaptures)));
330 :
331 0 : Address numOutputRegistersAddress(masm.getStackPointer(), offsetof(FrameData, numOutputRegisters));
332 :
333 : // Capture results have been stored, so the number of remaining global
334 : // output registers is reduced by the number of stored captures.
335 0 : masm.load32(numOutputRegistersAddress, temp0);
336 :
337 0 : masm.sub32(Imm32(num_saved_registers_), temp0);
338 :
339 : // Check whether we have enough room for another set of capture results.
340 0 : masm.branch32(Assembler::LessThan, temp0, Imm32(num_saved_registers_), &exit_label_);
341 :
342 0 : masm.store32(temp0, numOutputRegistersAddress);
343 :
344 : // Advance the location for output.
345 0 : masm.add32(Imm32(num_saved_registers_ * sizeof(void*)), outputRegistersAddress);
346 :
347 : // Prepare temp0 to initialize registers with its value in the next run.
348 0 : masm.loadPtr(Address(masm.getStackPointer(), offsetof(FrameData, inputStartMinusOne)), temp0);
349 :
350 0 : if (global_with_zero_length_check()) {
351 : // Special case for zero-length matches.
352 :
353 : // The capture start index was loaded into current_character above.
354 0 : masm.branchPtr(Assembler::NotEqual, current_position, current_character,
355 0 : &load_char_start_regexp);
356 :
357 : // edi (offset from the end) is zero if we already reached the end.
358 0 : masm.branchTestPtr(Assembler::Zero, current_position, current_position,
359 0 : &exit_label_);
360 :
361 : // Advance current position after a zero-length match.
362 0 : masm.addPtr(Imm32(char_size()), current_position);
363 : }
364 :
365 0 : masm.jump(&load_char_start_regexp);
366 : } else {
367 39 : if (match_only) {
368 : // Store endIndex.
369 :
370 18 : Register endIndexRegister = temp1;
371 18 : Register inputByteLength = backtrack_stack_pointer;
372 :
373 18 : masm.loadPtr(Address(masm.getStackPointer(), offsetof(FrameData, endIndex)), endIndexRegister);
374 :
375 18 : masm.loadPtr(inputOutputAddress, temp0);
376 18 : masm.loadPtr(Address(temp0, offsetof(InputOutputData, inputEnd)), inputByteLength);
377 18 : masm.subPtr(Address(temp0, offsetof(InputOutputData, inputStart)), inputByteLength);
378 :
379 18 : masm.loadPtr(register_location(1), temp0);
380 :
381 : // Convert to index from start of string, not end.
382 18 : masm.addPtr(inputByteLength, temp0);
383 :
384 : // Convert byte index to character index.
385 18 : if (mode_ == CHAR16)
386 1 : masm.rshiftPtrArithmetic(Imm32(1), temp0);
387 :
388 18 : masm.store32(temp0, Address(endIndexRegister, 0));
389 : }
390 :
391 39 : masm.movePtr(ImmWord(RegExpRunStatus_Success), temp0);
392 : }
393 : }
394 :
395 40 : masm.bind(&exit_label_);
396 :
397 40 : if (global()) {
398 : // Return the number of successful captures.
399 0 : masm.load32(Address(masm.getStackPointer(), offsetof(FrameData, successfulCaptures)), temp0);
400 : }
401 :
402 40 : masm.bind(&return_temp0);
403 :
404 : // Store the result to the input structure.
405 40 : masm.loadPtr(inputOutputAddress, temp1);
406 40 : masm.storePtr(temp0, Address(temp1, offsetof(InputOutputData, result)));
407 :
408 : #ifndef JS_CODEGEN_X86
409 : // Include the InputOutputData* when adjusting the stack size.
410 40 : masm.freeStack(frameSize + sizeof(void*));
411 : #else
412 : masm.freeStack(frameSize);
413 : #endif
414 :
415 : #if defined(XP_IOS) && defined(JS_CODEGEN_ARM)
416 : masm.pop(temp0);
417 : masm.movePtr(temp0, StackPointer);
418 : #endif
419 :
420 : // Restore non-volatile registers which were saved on entry.
421 120 : for (GeneralRegisterBackwardIterator iter(savedNonVolatileRegisters); iter.more(); ++iter)
422 80 : masm.Pop(*iter);
423 :
424 40 : masm.abiret();
425 :
426 : // Backtrack code (branch target for conditional backtracks).
427 40 : if (backtrack_label_.used()) {
428 39 : masm.bind(&backtrack_label_);
429 39 : Backtrack();
430 : }
431 :
432 : // Backtrack stack overflow code.
433 40 : if (stack_overflow_label_.used()) {
434 : // Reached if the backtrack-stack limit has been hit. temp2 holds the
435 : // StackPointer to use for accessing FrameData.
436 40 : masm.bind(&stack_overflow_label_);
437 :
438 80 : Label grow_failed;
439 :
440 40 : masm.movePtr(ImmPtr(cx->runtime()), temp1);
441 :
442 : // Save registers before calling C function
443 40 : LiveGeneralRegisterSet volatileRegs(GeneralRegisterSet::Volatile());
444 : #if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64)
445 : volatileRegs.add(Register::FromCode(Registers::lr));
446 : #elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
447 : volatileRegs.add(Register::FromCode(Registers::ra));
448 : #endif
449 40 : volatileRegs.takeUnchecked(temp0);
450 40 : volatileRegs.takeUnchecked(temp1);
451 40 : masm.PushRegsInMask(volatileRegs);
452 :
453 40 : masm.setupUnalignedABICall(temp0);
454 40 : masm.passABIArg(temp1);
455 40 : masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, GrowBacktrackStack));
456 40 : masm.storeCallWordResult(temp0);
457 :
458 40 : masm.PopRegsInMask(volatileRegs);
459 :
460 : // If return false, we have failed to grow the stack, and
461 : // must exit with a stack-overflow exception. Do this in the caller
462 : // so that the stack is adjusted by our return instruction.
463 80 : Label return_from_overflow_handler;
464 40 : masm.branchTest32(Assembler::Zero, temp0, temp0, &return_from_overflow_handler);
465 :
466 : // Otherwise, store the new backtrack stack base and recompute the new
467 : // top of the stack.
468 40 : Address backtrackStackBaseAddress(temp2, offsetof(FrameData, backtrackStackBase));
469 40 : masm.subPtr(backtrackStackBaseAddress, backtrack_stack_pointer);
470 :
471 40 : void* context_addr = cx->zone()->group()->addressOfOwnerContext();
472 40 : size_t baseOffset = offsetof(JSContext, regexpStack) + RegExpStack::offsetOfBase();
473 40 : masm.loadPtr(AbsoluteAddress(context_addr), temp1);
474 40 : masm.loadPtr(Address(temp1, baseOffset), temp1);
475 40 : masm.storePtr(temp1, backtrackStackBaseAddress);
476 40 : masm.addPtr(temp1, backtrack_stack_pointer);
477 :
478 : // Resume execution in calling code.
479 40 : masm.bind(&return_from_overflow_handler);
480 40 : masm.abiret();
481 : }
482 :
483 40 : if (exit_with_exception_label_.used()) {
484 : // If any of the code above needed to exit with an exception.
485 40 : masm.bind(&exit_with_exception_label_);
486 :
487 : // Exit with an error result to signal thrown exception.
488 40 : masm.movePtr(ImmWord(RegExpRunStatus_Error), temp0);
489 40 : masm.jump(&return_temp0);
490 : }
491 :
492 80 : Linker linker(masm);
493 80 : AutoFlushICache afc("RegExp");
494 40 : JitCode* code = linker.newCode<NoGC>(cx, REGEXP_CODE);
495 40 : if (!code) {
496 0 : ReportOutOfMemory(cx);
497 0 : return RegExpCode();
498 : }
499 :
500 : #ifdef JS_ION_PERF
501 : writePerfSpewerJitCodeProfile(code, "RegExp");
502 : #endif
503 :
504 : #ifdef MOZ_VTUNE
505 40 : vtune::MarkRegExp(code, match_only);
506 : #endif
507 :
508 246 : for (size_t i = 0; i < labelPatches.length(); i++) {
509 206 : LabelPatch& v = labelPatches[i];
510 206 : MOZ_ASSERT(!v.label);
511 618 : Assembler::PatchDataWithValueCheck(CodeLocationLabel(code, v.patchOffset),
512 206 : ImmPtr(code->raw() + v.labelOffset),
513 206 : ImmPtr(0));
514 : }
515 :
516 40 : JitSpew(JitSpew_Codegen, "Created RegExp (raw %p length %d)",
517 80 : (void*) code->raw(), (int) masm.bytesNeeded());
518 :
519 40 : RegExpCode res;
520 40 : res.jitCode = code;
521 40 : return res;
522 : }
523 :
524 : int
525 166 : NativeRegExpMacroAssembler::stack_limit_slack()
526 : {
527 166 : return RegExpStack::kStackLimitSlack;
528 : }
529 :
530 : void
531 221 : NativeRegExpMacroAssembler::AdvanceCurrentPosition(int by)
532 : {
533 221 : JitSpew(SPEW_PREFIX "AdvanceCurrentPosition(%d)", by);
534 :
535 221 : if (by != 0)
536 221 : masm.addPtr(Imm32(by * char_size()), current_position);
537 221 : }
538 :
539 : void
540 13 : NativeRegExpMacroAssembler::AdvanceRegister(int reg, int by)
541 : {
542 13 : JitSpew(SPEW_PREFIX "AdvanceRegister(%d, %d)", reg, by);
543 :
544 13 : MOZ_ASSERT(reg >= 0);
545 13 : MOZ_ASSERT(reg < num_registers_);
546 13 : if (by != 0)
547 13 : masm.addPtr(Imm32(by), register_location(reg));
548 13 : }
549 :
550 : void
551 121 : NativeRegExpMacroAssembler::Backtrack()
552 : {
553 121 : JitSpew(SPEW_PREFIX "Backtrack");
554 :
555 : // Check for an interrupt.
556 242 : Label noInterrupt;
557 121 : void* contextAddr = cx->zone()->group()->addressOfOwnerContext();
558 121 : masm.loadPtr(AbsoluteAddress(contextAddr), temp0);
559 242 : masm.branch32(Assembler::Equal, Address(temp0, offsetof(JSContext, interrupt_)), Imm32(0),
560 121 : &noInterrupt);
561 121 : masm.movePtr(ImmWord(RegExpRunStatus_Error), temp0);
562 121 : masm.jump(&exit_label_);
563 121 : masm.bind(&noInterrupt);
564 :
565 : // Pop code location from backtrack stack and jump to location.
566 121 : PopBacktrack(temp0);
567 121 : masm.jump(temp0);
568 121 : }
569 :
570 : void
571 1051 : NativeRegExpMacroAssembler::Bind(Label* label)
572 : {
573 1051 : JitSpew(SPEW_PREFIX "Bind");
574 :
575 1051 : masm.bind(label);
576 1051 : }
577 :
578 : void
579 1 : NativeRegExpMacroAssembler::CheckAtStart(Label* on_at_start)
580 : {
581 1 : JitSpew(SPEW_PREFIX "CheckAtStart");
582 :
583 2 : Label not_at_start;
584 :
585 : // Did we start the match at the start of the string at all?
586 1 : Address startIndex(masm.getStackPointer(), offsetof(FrameData, startIndex));
587 1 : masm.branchPtr(Assembler::NotEqual, startIndex, ImmWord(0), ¬_at_start);
588 :
589 : // If we did, are we still at the start of the input?
590 1 : masm.computeEffectiveAddress(BaseIndex(input_end_pointer, current_position, TimesOne), temp0);
591 :
592 1 : Address inputStart(masm.getStackPointer(), offsetof(FrameData, inputStart));
593 1 : masm.branchPtr(Assembler::Equal, inputStart, temp0, BranchOrBacktrack(on_at_start));
594 :
595 1 : masm.bind(¬_at_start);
596 1 : }
597 :
598 : void
599 21 : NativeRegExpMacroAssembler::CheckNotAtStart(Label* on_not_at_start)
600 : {
601 21 : JitSpew(SPEW_PREFIX "CheckNotAtStart");
602 :
603 : // Did we start the match at the start of the string at all?
604 21 : Address startIndex(masm.getStackPointer(), offsetof(FrameData, startIndex));
605 21 : masm.branchPtr(Assembler::NotEqual, startIndex, ImmWord(0), BranchOrBacktrack(on_not_at_start));
606 :
607 : // If we did, are we still at the start of the input?
608 21 : masm.computeEffectiveAddress(BaseIndex(input_end_pointer, current_position, TimesOne), temp0);
609 :
610 21 : Address inputStart(masm.getStackPointer(), offsetof(FrameData, inputStart));
611 21 : masm.branchPtr(Assembler::NotEqual, inputStart, temp0, BranchOrBacktrack(on_not_at_start));
612 21 : }
613 :
614 : void
615 73 : NativeRegExpMacroAssembler::CheckCharacter(unsigned c, Label* on_equal)
616 : {
617 73 : JitSpew(SPEW_PREFIX "CheckCharacter(%d)", (int) c);
618 73 : masm.branch32(Assembler::Equal, current_character, Imm32(c), BranchOrBacktrack(on_equal));
619 73 : }
620 :
621 : void
622 334 : NativeRegExpMacroAssembler::CheckNotCharacter(unsigned c, Label* on_not_equal)
623 : {
624 334 : JitSpew(SPEW_PREFIX "CheckNotCharacter(%d)", (int) c);
625 334 : masm.branch32(Assembler::NotEqual, current_character, Imm32(c), BranchOrBacktrack(on_not_equal));
626 334 : }
627 :
628 : void
629 49 : NativeRegExpMacroAssembler::CheckCharacterAfterAnd(unsigned c, unsigned and_with,
630 : Label* on_equal)
631 : {
632 49 : JitSpew(SPEW_PREFIX "CheckCharacterAfterAnd(%d, %d)", (int) c, (int) and_with);
633 :
634 49 : if (c == 0) {
635 12 : masm.branchTest32(Assembler::Zero, current_character, Imm32(and_with),
636 6 : BranchOrBacktrack(on_equal));
637 : } else {
638 43 : masm.move32(Imm32(and_with), temp0);
639 43 : masm.and32(current_character, temp0);
640 43 : masm.branch32(Assembler::Equal, temp0, Imm32(c), BranchOrBacktrack(on_equal));
641 : }
642 49 : }
643 :
644 : void
645 111 : NativeRegExpMacroAssembler::CheckNotCharacterAfterAnd(unsigned c, unsigned and_with,
646 : Label* on_not_equal)
647 : {
648 111 : JitSpew(SPEW_PREFIX "CheckNotCharacterAfterAnd(%d, %d)", (int) c, (int) and_with);
649 :
650 111 : if (c == 0) {
651 12 : masm.branchTest32(Assembler::NonZero, current_character, Imm32(and_with),
652 6 : BranchOrBacktrack(on_not_equal));
653 : } else {
654 105 : masm.move32(Imm32(and_with), temp0);
655 105 : masm.and32(current_character, temp0);
656 105 : masm.branch32(Assembler::NotEqual, temp0, Imm32(c), BranchOrBacktrack(on_not_equal));
657 : }
658 111 : }
659 :
660 : void
661 7 : NativeRegExpMacroAssembler::CheckCharacterGT(char16_t c, Label* on_greater)
662 : {
663 7 : JitSpew(SPEW_PREFIX "CheckCharacterGT(%d)", (int) c);
664 14 : masm.branch32(Assembler::GreaterThan, current_character, Imm32(c),
665 7 : BranchOrBacktrack(on_greater));
666 7 : }
667 :
668 : void
669 0 : NativeRegExpMacroAssembler::CheckCharacterLT(char16_t c, Label* on_less)
670 : {
671 0 : JitSpew(SPEW_PREFIX "CheckCharacterLT(%d)", (int) c);
672 0 : masm.branch32(Assembler::LessThan, current_character, Imm32(c), BranchOrBacktrack(on_less));
673 0 : }
674 :
675 : void
676 26 : NativeRegExpMacroAssembler::CheckGreedyLoop(Label* on_tos_equals_current_position)
677 : {
678 26 : JitSpew(SPEW_PREFIX "CheckGreedyLoop");
679 :
680 52 : Label fallthrough;
681 26 : masm.branchPtr(Assembler::NotEqual,
682 52 : Address(backtrack_stack_pointer, -int(sizeof(void*))), current_position,
683 26 : &fallthrough);
684 26 : masm.subPtr(Imm32(sizeof(void*)), backtrack_stack_pointer); // Pop.
685 26 : JumpOrBacktrack(on_tos_equals_current_position);
686 26 : masm.bind(&fallthrough);
687 26 : }
688 :
689 : void
690 0 : NativeRegExpMacroAssembler::CheckNotBackReference(int start_reg, Label* on_no_match)
691 : {
692 0 : JitSpew(SPEW_PREFIX "CheckNotBackReference(%d)", start_reg);
693 :
694 0 : Label fallthrough;
695 0 : Label success;
696 0 : Label fail;
697 :
698 : // Find length of back-referenced capture.
699 0 : masm.loadPtr(register_location(start_reg), current_character);
700 0 : masm.loadPtr(register_location(start_reg + 1), temp0);
701 0 : masm.subPtr(current_character, temp0); // Length to check.
702 :
703 : // Fail on partial or illegal capture (start of capture after end of capture).
704 0 : masm.branchPtr(Assembler::LessThan, temp0, ImmWord(0), BranchOrBacktrack(on_no_match));
705 :
706 : // Succeed on empty capture (including no capture).
707 0 : masm.branchPtr(Assembler::Equal, temp0, ImmWord(0), &fallthrough);
708 :
709 : // Check that there are sufficient characters left in the input.
710 0 : masm.movePtr(current_position, temp1);
711 0 : masm.addPtr(temp0, temp1);
712 0 : masm.branchPtr(Assembler::GreaterThan, temp1, ImmWord(0), BranchOrBacktrack(on_no_match));
713 :
714 : // Save register to make it available below.
715 0 : masm.push(backtrack_stack_pointer);
716 :
717 : // Compute pointers to match string and capture string
718 0 : masm.computeEffectiveAddress(BaseIndex(input_end_pointer, current_position, TimesOne), temp1); // Start of match.
719 0 : masm.addPtr(input_end_pointer, current_character); // Start of capture.
720 0 : masm.computeEffectiveAddress(BaseIndex(temp0, temp1, TimesOne), backtrack_stack_pointer); // End of match.
721 :
722 0 : Label loop;
723 0 : masm.bind(&loop);
724 0 : if (mode_ == LATIN1) {
725 0 : masm.load8ZeroExtend(Address(current_character, 0), temp0);
726 0 : masm.load8ZeroExtend(Address(temp1, 0), temp2);
727 : } else {
728 0 : MOZ_ASSERT(mode_ == CHAR16);
729 0 : masm.load16ZeroExtend(Address(current_character, 0), temp0);
730 0 : masm.load16ZeroExtend(Address(temp1, 0), temp2);
731 : }
732 0 : masm.branch32(Assembler::NotEqual, temp0, temp2, &fail);
733 :
734 : // Increment pointers into capture and match string.
735 0 : masm.addPtr(Imm32(char_size()), current_character);
736 0 : masm.addPtr(Imm32(char_size()), temp1);
737 :
738 : // Check if we have reached end of match area.
739 0 : masm.branchPtr(Assembler::Below, temp1, backtrack_stack_pointer, &loop);
740 0 : masm.jump(&success);
741 :
742 0 : masm.bind(&fail);
743 :
744 : // Restore backtrack stack pointer.
745 0 : masm.pop(backtrack_stack_pointer);
746 0 : JumpOrBacktrack(on_no_match);
747 :
748 0 : masm.bind(&success);
749 :
750 : // Move current character position to position after match.
751 0 : masm.movePtr(backtrack_stack_pointer, current_position);
752 0 : masm.subPtr(input_end_pointer, current_position);
753 :
754 : // Restore backtrack stack pointer.
755 0 : masm.pop(backtrack_stack_pointer);
756 :
757 0 : masm.bind(&fallthrough);
758 0 : }
759 :
760 : void
761 0 : NativeRegExpMacroAssembler::CheckNotBackReferenceIgnoreCase(int start_reg, Label* on_no_match,
762 : bool unicode)
763 : {
764 0 : JitSpew(SPEW_PREFIX "CheckNotBackReferenceIgnoreCase(%d, %d)", start_reg, unicode);
765 :
766 0 : Label fallthrough;
767 :
768 0 : masm.loadPtr(register_location(start_reg), current_character); // Index of start of capture
769 0 : masm.loadPtr(register_location(start_reg + 1), temp1); // Index of end of capture
770 0 : masm.subPtr(current_character, temp1); // Length of capture.
771 :
772 : // The length of a capture should not be negative. This can only happen
773 : // if the end of the capture is unrecorded, or at a point earlier than
774 : // the start of the capture.
775 0 : masm.branchPtr(Assembler::LessThan, temp1, ImmWord(0), BranchOrBacktrack(on_no_match));
776 :
777 : // If length is zero, either the capture is empty or it is completely
778 : // uncaptured. In either case succeed immediately.
779 0 : masm.branchPtr(Assembler::Equal, temp1, ImmWord(0), &fallthrough);
780 :
781 : // Check that there are sufficient characters left in the input.
782 0 : masm.movePtr(current_position, temp0);
783 0 : masm.addPtr(temp1, temp0);
784 0 : masm.branchPtr(Assembler::GreaterThan, temp0, ImmWord(0), BranchOrBacktrack(on_no_match));
785 :
786 0 : if (mode_ == LATIN1) {
787 0 : Label success, fail;
788 :
789 : // Save register contents to make the registers available below. After
790 : // this, the temp0, temp2, and current_position registers are available.
791 0 : masm.push(current_position);
792 :
793 0 : masm.addPtr(input_end_pointer, current_character); // Start of capture.
794 0 : masm.addPtr(input_end_pointer, current_position); // Start of text to match against capture.
795 0 : masm.addPtr(current_position, temp1); // End of text to match against capture.
796 :
797 0 : Label loop, loop_increment;
798 0 : masm.bind(&loop);
799 0 : masm.load8ZeroExtend(Address(current_position, 0), temp0);
800 0 : masm.load8ZeroExtend(Address(current_character, 0), temp2);
801 0 : masm.branch32(Assembler::Equal, temp0, temp2, &loop_increment);
802 :
803 : // Mismatch, try case-insensitive match (converting letters to lower-case).
804 0 : masm.or32(Imm32(0x20), temp0); // Convert match character to lower-case.
805 :
806 : // Is temp0 a lowercase letter?
807 0 : Label convert_capture;
808 0 : masm.computeEffectiveAddress(Address(temp0, -'a'), temp2);
809 0 : masm.branch32(Assembler::BelowOrEqual, temp2, Imm32(static_cast<int32_t>('z' - 'a')),
810 0 : &convert_capture);
811 :
812 : // Latin-1: Check for values in range [224,254] but not 247.
813 0 : masm.sub32(Imm32(224 - 'a'), temp2);
814 0 : masm.branch32(Assembler::Above, temp2, Imm32(254 - 224), &fail);
815 :
816 : // Check for 247.
817 0 : masm.branch32(Assembler::Equal, temp2, Imm32(247 - 224), &fail);
818 :
819 0 : masm.bind(&convert_capture);
820 :
821 : // Also convert capture character.
822 0 : masm.load8ZeroExtend(Address(current_character, 0), temp2);
823 0 : masm.or32(Imm32(0x20), temp2);
824 :
825 0 : masm.branch32(Assembler::NotEqual, temp0, temp2, &fail);
826 :
827 0 : masm.bind(&loop_increment);
828 :
829 : // Increment pointers into match and capture strings.
830 0 : masm.addPtr(Imm32(1), current_character);
831 0 : masm.addPtr(Imm32(1), current_position);
832 :
833 : // Compare to end of match, and loop if not done.
834 0 : masm.branchPtr(Assembler::Below, current_position, temp1, &loop);
835 0 : masm.jump(&success);
836 :
837 0 : masm.bind(&fail);
838 :
839 : // Restore original values before failing.
840 0 : masm.pop(current_position);
841 0 : JumpOrBacktrack(on_no_match);
842 :
843 0 : masm.bind(&success);
844 :
845 : // Drop original character position value.
846 0 : masm.addToStackPtr(Imm32(sizeof(uintptr_t)));
847 :
848 : // Compute new value of character position after the matched part.
849 0 : masm.subPtr(input_end_pointer, current_position);
850 : } else {
851 0 : MOZ_ASSERT(mode_ == CHAR16);
852 :
853 : // Note: temp1 needs to be saved/restored if it is volatile, as it is used after the call.
854 0 : LiveGeneralRegisterSet volatileRegs(GeneralRegisterSet::Volatile());
855 0 : volatileRegs.takeUnchecked(temp0);
856 0 : volatileRegs.takeUnchecked(temp2);
857 0 : masm.PushRegsInMask(volatileRegs);
858 :
859 : // Set byte_offset1.
860 : // Start of capture, where current_character already holds string-end negative offset.
861 0 : masm.addPtr(input_end_pointer, current_character);
862 :
863 : // Set byte_offset2.
864 : // Found by adding negative string-end offset of current position
865 : // to end of string.
866 0 : masm.addPtr(input_end_pointer, current_position);
867 :
868 : // Parameters are
869 : // Address byte_offset1 - Address captured substring's start.
870 : // Address byte_offset2 - Address of current character position.
871 : // size_t byte_length - length of capture in bytes(!)
872 0 : masm.setupUnalignedABICall(temp0);
873 0 : masm.passABIArg(current_character);
874 0 : masm.passABIArg(current_position);
875 0 : masm.passABIArg(temp1);
876 0 : if (!unicode) {
877 0 : int (*fun)(const char16_t*, const char16_t*, size_t) = CaseInsensitiveCompareStrings;
878 0 : masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, fun));
879 : } else {
880 0 : int (*fun)(const char16_t*, const char16_t*, size_t) = CaseInsensitiveCompareUCStrings;
881 0 : masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, fun));
882 : }
883 0 : masm.storeCallWordResult(temp0);
884 :
885 0 : masm.PopRegsInMask(volatileRegs);
886 :
887 : // Check if function returned non-zero for success or zero for failure.
888 0 : masm.branchTest32(Assembler::Zero, temp0, temp0, BranchOrBacktrack(on_no_match));
889 :
890 : // On success, increment position by length of capture.
891 0 : masm.addPtr(temp1, current_position);
892 : }
893 :
894 0 : masm.bind(&fallthrough);
895 0 : }
896 :
897 : void
898 0 : NativeRegExpMacroAssembler::CheckNotCharacterAfterMinusAnd(char16_t c, char16_t minus, char16_t and_with,
899 : Label* on_not_equal)
900 : {
901 0 : JitSpew(SPEW_PREFIX "CheckNotCharacterAfterMinusAnd(%d, %d, %d)", (int) c,
902 0 : (int) minus, (int) and_with);
903 :
904 0 : masm.computeEffectiveAddress(Address(current_character, -minus), temp0);
905 0 : if (c == 0) {
906 0 : masm.branchTest32(Assembler::NonZero, temp0, Imm32(and_with),
907 0 : BranchOrBacktrack(on_not_equal));
908 : } else {
909 0 : masm.and32(Imm32(and_with), temp0);
910 0 : masm.branch32(Assembler::NotEqual, temp0, Imm32(c), BranchOrBacktrack(on_not_equal));
911 : }
912 0 : }
913 :
914 : void
915 14 : NativeRegExpMacroAssembler::CheckCharacterInRange(char16_t from, char16_t to,
916 : Label* on_in_range)
917 : {
918 14 : JitSpew(SPEW_PREFIX "CheckCharacterInRange(%d, %d)", (int) from, (int) to);
919 :
920 14 : masm.computeEffectiveAddress(Address(current_character, -from), temp0);
921 14 : masm.branch32(Assembler::BelowOrEqual, temp0, Imm32(to - from), BranchOrBacktrack(on_in_range));
922 14 : }
923 :
924 : void
925 31 : NativeRegExpMacroAssembler::CheckCharacterNotInRange(char16_t from, char16_t to,
926 : Label* on_not_in_range)
927 : {
928 31 : JitSpew(SPEW_PREFIX "CheckCharacterNotInRange(%d, %d)", (int) from, (int) to);
929 :
930 31 : masm.computeEffectiveAddress(Address(current_character, -from), temp0);
931 31 : masm.branch32(Assembler::Above, temp0, Imm32(to - from), BranchOrBacktrack(on_not_in_range));
932 31 : }
933 :
934 : void
935 16 : NativeRegExpMacroAssembler::CheckBitInTable(RegExpShared::JitCodeTable table, Label* on_bit_set)
936 : {
937 16 : JitSpew(SPEW_PREFIX "CheckBitInTable");
938 :
939 16 : masm.movePtr(ImmPtr(table.get()), temp0);
940 :
941 : // kTableMask is currently 127, so we need to mask even if the input is
942 : // Latin1. V8 has the same issue.
943 : static_assert(JSString::MAX_LATIN1_CHAR > kTableMask,
944 : "No need to mask if MAX_LATIN1_CHAR <= kTableMask");
945 16 : masm.move32(Imm32(kTableSize - 1), temp1);
946 16 : masm.and32(current_character, temp1);
947 :
948 16 : masm.load8ZeroExtend(BaseIndex(temp0, temp1, TimesOne), temp0);
949 16 : masm.branchTest32(Assembler::NonZero, temp0, temp0, BranchOrBacktrack(on_bit_set));
950 :
951 : // Transfer ownership of |table| to the |tables| Vector.
952 : {
953 32 : AutoEnterOOMUnsafeRegion oomUnsafe;
954 16 : if (!tables.append(Move(table)))
955 0 : oomUnsafe.crash("RegExp table append");
956 : }
957 16 : }
958 :
959 : void
960 40 : NativeRegExpMacroAssembler::Fail()
961 : {
962 40 : JitSpew(SPEW_PREFIX "Fail");
963 :
964 40 : if (!global())
965 40 : masm.movePtr(ImmWord(RegExpRunStatus_Success_NotFound), temp0);
966 40 : masm.jump(&exit_label_);
967 40 : }
968 :
969 : void
970 11 : NativeRegExpMacroAssembler::IfRegisterGE(int reg, int comparand, Label* if_ge)
971 : {
972 11 : JitSpew(SPEW_PREFIX "IfRegisterGE(%d, %d)", reg, comparand);
973 22 : masm.branchPtr(Assembler::GreaterThanOrEqual, register_location(reg), ImmWord(comparand),
974 11 : BranchOrBacktrack(if_ge));
975 11 : }
976 :
977 : void
978 5 : NativeRegExpMacroAssembler::IfRegisterLT(int reg, int comparand, Label* if_lt)
979 : {
980 5 : JitSpew(SPEW_PREFIX "IfRegisterLT(%d, %d)", reg, comparand);
981 10 : masm.branchPtr(Assembler::LessThan, register_location(reg), ImmWord(comparand),
982 5 : BranchOrBacktrack(if_lt));
983 5 : }
984 :
985 : void
986 0 : NativeRegExpMacroAssembler::IfRegisterEqPos(int reg, Label* if_eq)
987 : {
988 0 : JitSpew(SPEW_PREFIX "IfRegisterEqPos(%d)", reg);
989 0 : masm.branchPtr(Assembler::Equal, register_location(reg), current_position,
990 0 : BranchOrBacktrack(if_eq));
991 0 : }
992 :
993 : void
994 591 : NativeRegExpMacroAssembler::LoadCurrentCharacter(int cp_offset, Label* on_end_of_input,
995 : bool check_bounds, int characters)
996 : {
997 591 : JitSpew(SPEW_PREFIX "LoadCurrentCharacter(%d, %d)", cp_offset, characters);
998 :
999 591 : MOZ_ASSERT(cp_offset >= -1); // ^ and \b can look behind one character.
1000 591 : MOZ_ASSERT(cp_offset < (1<<30)); // Be sane! (And ensure negation works)
1001 591 : if (check_bounds)
1002 196 : CheckPosition(cp_offset + characters - 1, on_end_of_input);
1003 591 : LoadCurrentCharacterUnchecked(cp_offset, characters);
1004 591 : }
1005 :
1006 : void
1007 631 : NativeRegExpMacroAssembler::LoadCurrentCharacterUnchecked(int cp_offset, int characters)
1008 : {
1009 631 : JitSpew(SPEW_PREFIX "LoadCurrentCharacterUnchecked(%d, %d)", cp_offset, characters);
1010 :
1011 631 : if (mode_ == LATIN1) {
1012 578 : BaseIndex address(input_end_pointer, current_position, TimesOne, cp_offset);
1013 578 : if (characters == 4) {
1014 39 : masm.load32(address, current_character);
1015 539 : } else if (characters == 2) {
1016 29 : masm.load16ZeroExtend(address, current_character);
1017 : } else {
1018 510 : MOZ_ASSERT(characters == 1);
1019 510 : masm.load8ZeroExtend(address, current_character);
1020 : }
1021 : } else {
1022 53 : MOZ_ASSERT(mode_ == CHAR16);
1023 53 : MOZ_ASSERT(characters <= 2);
1024 53 : BaseIndex address(input_end_pointer, current_position, TimesOne, cp_offset * sizeof(char16_t));
1025 53 : if (characters == 2)
1026 12 : masm.load32(address, current_character);
1027 : else
1028 41 : masm.load16ZeroExtend(address, current_character);
1029 : }
1030 631 : }
1031 :
1032 : void
1033 129 : NativeRegExpMacroAssembler::PopCurrentPosition()
1034 : {
1035 129 : JitSpew(SPEW_PREFIX "PopCurrentPosition");
1036 :
1037 129 : PopBacktrack(current_position);
1038 129 : }
1039 :
1040 : void
1041 44 : NativeRegExpMacroAssembler::PopRegister(int register_index)
1042 : {
1043 44 : JitSpew(SPEW_PREFIX "PopRegister(%d)", register_index);
1044 :
1045 44 : PopBacktrack(temp0);
1046 44 : masm.storePtr(temp0, register_location(register_index));
1047 44 : }
1048 :
1049 : void
1050 206 : NativeRegExpMacroAssembler::PushBacktrack(Label* label)
1051 : {
1052 206 : JitSpew(SPEW_PREFIX "PushBacktrack");
1053 :
1054 206 : CodeOffset patchOffset = masm.movWithPatch(ImmPtr(nullptr), temp0);
1055 :
1056 206 : MOZ_ASSERT(!label->bound());
1057 :
1058 : {
1059 412 : AutoEnterOOMUnsafeRegion oomUnsafe;
1060 206 : if (!labelPatches.append(LabelPatch(label, patchOffset)))
1061 0 : oomUnsafe.crash("NativeRegExpMacroAssembler::PushBacktrack");
1062 : }
1063 :
1064 206 : PushBacktrack(temp0);
1065 206 : CheckBacktrackStackLimit();
1066 206 : }
1067 :
1068 : void
1069 206 : NativeRegExpMacroAssembler::BindBacktrack(Label* label)
1070 : {
1071 206 : JitSpew(SPEW_PREFIX "BindBacktrack");
1072 :
1073 206 : Bind(label);
1074 :
1075 1421 : for (size_t i = 0; i < labelPatches.length(); i++) {
1076 1421 : LabelPatch& v = labelPatches[i];
1077 1421 : if (v.label == label) {
1078 206 : v.labelOffset = label->offset();
1079 206 : v.label = nullptr;
1080 206 : break;
1081 : }
1082 : }
1083 206 : }
1084 :
1085 : void
1086 405 : NativeRegExpMacroAssembler::PushBacktrack(Register source)
1087 : {
1088 405 : JitSpew(SPEW_PREFIX "PushBacktrack");
1089 :
1090 405 : MOZ_ASSERT(source != backtrack_stack_pointer);
1091 :
1092 : // Notice: This updates flags, unlike normal Push.
1093 405 : masm.storePtr(source, Address(backtrack_stack_pointer, 0));
1094 405 : masm.addPtr(Imm32(sizeof(void*)), backtrack_stack_pointer);
1095 405 : }
1096 :
1097 : void
1098 0 : NativeRegExpMacroAssembler::PushBacktrack(int32_t value)
1099 : {
1100 0 : JitSpew(SPEW_PREFIX "PushBacktrack(%d)", (int) value);
1101 :
1102 : // Notice: This updates flags, unlike normal Push.
1103 0 : masm.storePtr(ImmWord(value), Address(backtrack_stack_pointer, 0));
1104 0 : masm.addPtr(Imm32(sizeof(void*)), backtrack_stack_pointer);
1105 0 : }
1106 :
1107 : void
1108 294 : NativeRegExpMacroAssembler::PopBacktrack(Register target)
1109 : {
1110 294 : JitSpew(SPEW_PREFIX "PopBacktrack");
1111 :
1112 294 : MOZ_ASSERT(target != backtrack_stack_pointer);
1113 :
1114 : // Notice: This updates flags, unlike normal Pop.
1115 294 : masm.subPtr(Imm32(sizeof(void*)), backtrack_stack_pointer);
1116 294 : masm.loadPtr(Address(backtrack_stack_pointer, 0), target);
1117 294 : }
1118 :
1119 : void
1120 206 : NativeRegExpMacroAssembler::CheckBacktrackStackLimit()
1121 : {
1122 206 : JitSpew(SPEW_PREFIX "CheckBacktrackStackLimit");
1123 :
1124 412 : Label no_stack_overflow;
1125 206 : void* context_addr = cx->zone()->group()->addressOfOwnerContext();
1126 206 : size_t limitOffset = offsetof(JSContext, regexpStack) + RegExpStack::offsetOfLimit();
1127 206 : masm.loadPtr(AbsoluteAddress(context_addr), temp1);
1128 412 : masm.branchPtr(Assembler::AboveOrEqual, Address(temp1, limitOffset),
1129 206 : backtrack_stack_pointer, &no_stack_overflow);
1130 :
1131 : // Copy the stack pointer before the call() instruction modifies it.
1132 206 : masm.moveStackPtrTo(temp2);
1133 :
1134 206 : masm.call(&stack_overflow_label_);
1135 206 : masm.bind(&no_stack_overflow);
1136 :
1137 : // Exit with an exception if the call failed.
1138 206 : masm.branchTest32(Assembler::Zero, temp0, temp0, &exit_with_exception_label_);
1139 206 : }
1140 :
1141 : void
1142 155 : NativeRegExpMacroAssembler::PushCurrentPosition()
1143 : {
1144 155 : JitSpew(SPEW_PREFIX "PushCurrentPosition");
1145 :
1146 155 : PushBacktrack(current_position);
1147 155 : }
1148 :
1149 : void
1150 44 : NativeRegExpMacroAssembler::PushRegister(int register_index, StackCheckFlag check_stack_limit)
1151 : {
1152 44 : JitSpew(SPEW_PREFIX "PushRegister(%d)", register_index);
1153 :
1154 44 : masm.loadPtr(register_location(register_index), temp0);
1155 44 : PushBacktrack(temp0);
1156 44 : if (check_stack_limit)
1157 0 : CheckBacktrackStackLimit();
1158 44 : }
1159 :
1160 : void
1161 22 : NativeRegExpMacroAssembler::ReadCurrentPositionFromRegister(int reg)
1162 : {
1163 22 : JitSpew(SPEW_PREFIX "ReadCurrentPositionFromRegister(%d)", reg);
1164 :
1165 22 : masm.loadPtr(register_location(reg), current_position);
1166 22 : }
1167 :
1168 : void
1169 253 : NativeRegExpMacroAssembler::WriteCurrentPositionToRegister(int reg, int cp_offset)
1170 : {
1171 253 : JitSpew(SPEW_PREFIX "WriteCurrentPositionToRegister(%d, %d)", reg, cp_offset);
1172 :
1173 253 : if (cp_offset == 0) {
1174 172 : masm.storePtr(current_position, register_location(reg));
1175 : } else {
1176 81 : masm.computeEffectiveAddress(Address(current_position, cp_offset * char_size()), temp0);
1177 81 : masm.storePtr(temp0, register_location(reg));
1178 : }
1179 253 : }
1180 :
1181 : void
1182 22 : NativeRegExpMacroAssembler::ReadBacktrackStackPointerFromRegister(int reg)
1183 : {
1184 22 : JitSpew(SPEW_PREFIX "ReadBacktrackStackPointerFromRegister(%d)", reg);
1185 :
1186 22 : masm.loadPtr(register_location(reg), backtrack_stack_pointer);
1187 44 : masm.addPtr(Address(masm.getStackPointer(),
1188 22 : offsetof(FrameData, backtrackStackBase)), backtrack_stack_pointer);
1189 22 : }
1190 :
1191 : void
1192 12 : NativeRegExpMacroAssembler::WriteBacktrackStackPointerToRegister(int reg)
1193 : {
1194 12 : JitSpew(SPEW_PREFIX "WriteBacktrackStackPointerToRegister(%d)", reg);
1195 :
1196 12 : masm.movePtr(backtrack_stack_pointer, temp0);
1197 24 : masm.subPtr(Address(masm.getStackPointer(),
1198 12 : offsetof(FrameData, backtrackStackBase)), temp0);
1199 12 : masm.storePtr(temp0, register_location(reg));
1200 12 : }
1201 :
1202 : void
1203 0 : NativeRegExpMacroAssembler::SetCurrentPositionFromEnd(int by)
1204 : {
1205 0 : JitSpew(SPEW_PREFIX "SetCurrentPositionFromEnd(%d)", by);
1206 :
1207 0 : Label after_position;
1208 0 : masm.branchPtr(Assembler::GreaterThanOrEqual, current_position,
1209 0 : ImmWord(-by * char_size()), &after_position);
1210 0 : masm.movePtr(ImmWord(-by * char_size()), current_position);
1211 :
1212 : // On RegExp code entry (where this operation is used), the character before
1213 : // the current position is expected to be already loaded.
1214 : // We have advanced the position, so it's safe to read backwards.
1215 0 : LoadCurrentCharacterUnchecked(-1, 1);
1216 0 : masm.bind(&after_position);
1217 0 : }
1218 :
1219 : void
1220 11 : NativeRegExpMacroAssembler::SetRegister(int register_index, int to)
1221 : {
1222 11 : JitSpew(SPEW_PREFIX "SetRegister(%d, %d)", register_index, to);
1223 :
1224 11 : MOZ_ASSERT(register_index >= num_saved_registers_); // Reserved for positions!
1225 11 : masm.storePtr(ImmWord(to), register_location(register_index));
1226 11 : }
1227 :
1228 : bool
1229 66 : NativeRegExpMacroAssembler::Succeed()
1230 : {
1231 66 : JitSpew(SPEW_PREFIX "Succeed");
1232 :
1233 66 : masm.jump(&success_label_);
1234 66 : return global();
1235 : }
1236 :
1237 : void
1238 60 : NativeRegExpMacroAssembler::ClearRegisters(int reg_from, int reg_to)
1239 : {
1240 60 : JitSpew(SPEW_PREFIX "ClearRegisters(%d, %d)", reg_from, reg_to);
1241 :
1242 60 : MOZ_ASSERT(reg_from <= reg_to);
1243 60 : masm.loadPtr(Address(masm.getStackPointer(), offsetof(FrameData, inputStartMinusOne)), temp0);
1244 135 : for (int reg = reg_from; reg <= reg_to; reg++)
1245 75 : masm.storePtr(temp0, register_location(reg));
1246 60 : }
1247 :
1248 : void
1249 230 : NativeRegExpMacroAssembler::CheckPosition(int cp_offset, Label* on_outside_input)
1250 : {
1251 230 : JitSpew(SPEW_PREFIX "CheckPosition(%d)", cp_offset);
1252 690 : masm.branchPtr(Assembler::GreaterThanOrEqual, current_position,
1253 460 : ImmWord(-cp_offset * char_size()), BranchOrBacktrack(on_outside_input));
1254 230 : }
1255 :
1256 : Label*
1257 942 : NativeRegExpMacroAssembler::BranchOrBacktrack(Label* branch)
1258 : {
1259 942 : if (branch)
1260 692 : return branch;
1261 250 : return &backtrack_label_;
1262 : }
1263 :
1264 : void
1265 390 : NativeRegExpMacroAssembler::JumpOrBacktrack(Label* to)
1266 : {
1267 390 : JitSpew(SPEW_PREFIX "JumpOrBacktrack");
1268 :
1269 390 : if (to)
1270 357 : masm.jump(to);
1271 : else
1272 33 : Backtrack();
1273 390 : }
1274 :
1275 : bool
1276 18 : NativeRegExpMacroAssembler::CheckSpecialCharacterClass(char16_t type, Label* on_no_match)
1277 : {
1278 18 : JitSpew(SPEW_PREFIX "CheckSpecialCharacterClass(%d)", (int) type);
1279 :
1280 18 : Label* branch = BranchOrBacktrack(on_no_match);
1281 :
1282 : // Range checks (c in min..max) are generally implemented by an unsigned
1283 : // (c - min) <= (max - min) check
1284 18 : switch (type) {
1285 : case 's':
1286 : // Match space-characters.
1287 2 : if (mode_ == LATIN1) {
1288 : // One byte space characters are '\t'..'\r', ' ' and \u00a0.
1289 4 : Label success;
1290 2 : masm.branch32(Assembler::Equal, current_character, Imm32(' '), &success);
1291 :
1292 : // Check range 0x09..0x0d.
1293 2 : masm.computeEffectiveAddress(Address(current_character, -'\t'), temp0);
1294 2 : masm.branch32(Assembler::BelowOrEqual, temp0, Imm32('\r' - '\t'), &success);
1295 :
1296 : // \u00a0 (NBSP).
1297 2 : masm.branch32(Assembler::NotEqual, temp0, Imm32(0x00a0 - '\t'), branch);
1298 :
1299 2 : masm.bind(&success);
1300 2 : return true;
1301 : }
1302 0 : return false;
1303 : case 'S':
1304 : // The emitted code for generic character classes is good enough.
1305 3 : return false;
1306 : case 'd':
1307 : // Match LATIN1 digits ('0'..'9')
1308 0 : masm.computeEffectiveAddress(Address(current_character, -'0'), temp0);
1309 0 : masm.branch32(Assembler::Above, temp0, Imm32('9' - '0'), branch);
1310 0 : return true;
1311 : case 'D':
1312 : // Match non LATIN1-digits
1313 0 : masm.computeEffectiveAddress(Address(current_character, -'0'), temp0);
1314 0 : masm.branch32(Assembler::BelowOrEqual, temp0, Imm32('9' - '0'), branch);
1315 0 : return true;
1316 : case '.': {
1317 : // Match non-newlines (not 0x0a('\n'), 0x0d('\r'), 0x2028 and 0x2029)
1318 11 : masm.move32(current_character, temp0);
1319 11 : masm.xor32(Imm32(0x01), temp0);
1320 :
1321 : // See if current character is '\n'^1 or '\r'^1, i.e., 0x0b or 0x0c
1322 11 : masm.sub32(Imm32(0x0b), temp0);
1323 11 : masm.branch32(Assembler::BelowOrEqual, temp0, Imm32(0x0c - 0x0b), branch);
1324 11 : if (mode_ == CHAR16) {
1325 : // Compare original value to 0x2028 and 0x2029, using the already
1326 : // computed (current_char ^ 0x01 - 0x0b). I.e., check for
1327 : // 0x201d (0x2028 - 0x0b) or 0x201e.
1328 0 : masm.sub32(Imm32(0x2028 - 0x0b), temp0);
1329 0 : masm.branch32(Assembler::BelowOrEqual, temp0, Imm32(0x2029 - 0x2028), branch);
1330 : }
1331 11 : return true;
1332 : }
1333 : case 'w': {
1334 1 : if (mode_ != LATIN1) {
1335 : // Table is 256 entries, so all LATIN1 characters can be tested.
1336 0 : masm.branch32(Assembler::Above, current_character, Imm32('z'), branch);
1337 : }
1338 1 : MOZ_ASSERT(0 == word_character_map[0]); // Character '\0' is not a word char.
1339 1 : masm.movePtr(ImmPtr(word_character_map), temp0);
1340 1 : masm.load8ZeroExtend(BaseIndex(temp0, current_character, TimesOne), temp0);
1341 1 : masm.branchTest32(Assembler::Zero, temp0, temp0, branch);
1342 1 : return true;
1343 : }
1344 : case 'W': {
1345 2 : Label done;
1346 1 : if (mode_ != LATIN1) {
1347 : // Table is 256 entries, so all LATIN1 characters can be tested.
1348 0 : masm.branch32(Assembler::Above, current_character, Imm32('z'), &done);
1349 : }
1350 1 : MOZ_ASSERT(0 == word_character_map[0]); // Character '\0' is not a word char.
1351 1 : masm.movePtr(ImmPtr(word_character_map), temp0);
1352 1 : masm.load8ZeroExtend(BaseIndex(temp0, current_character, TimesOne), temp0);
1353 1 : masm.branchTest32(Assembler::NonZero, temp0, temp0, branch);
1354 1 : if (mode_ != LATIN1)
1355 0 : masm.bind(&done);
1356 1 : return true;
1357 : }
1358 : // Non-standard classes (with no syntactic shorthand) used internally.
1359 : case '*':
1360 : // Match any character.
1361 0 : return true;
1362 : case 'n': {
1363 : // Match newlines (0x0a('\n'), 0x0d('\r'), 0x2028 or 0x2029).
1364 : // The opposite of '.'.
1365 0 : masm.move32(current_character, temp0);
1366 0 : masm.xor32(Imm32(0x01), temp0);
1367 :
1368 : // See if current character is '\n'^1 or '\r'^1, i.e., 0x0b or 0x0c
1369 0 : masm.sub32(Imm32(0x0b), temp0);
1370 :
1371 0 : if (mode_ == LATIN1) {
1372 0 : masm.branch32(Assembler::Above, temp0, Imm32(0x0c - 0x0b), branch);
1373 : } else {
1374 0 : Label done;
1375 0 : masm.branch32(Assembler::BelowOrEqual, temp0, Imm32(0x0c - 0x0b), &done);
1376 0 : MOZ_ASSERT(CHAR16 == mode_);
1377 :
1378 : // Compare original value to 0x2028 and 0x2029, using the already
1379 : // computed (current_char ^ 0x01 - 0x0b). I.e., check for
1380 : // 0x201d (0x2028 - 0x0b) or 0x201e.
1381 0 : masm.sub32(Imm32(0x2028 - 0x0b), temp0);
1382 0 : masm.branch32(Assembler::Above, temp0, Imm32(1), branch);
1383 :
1384 0 : masm.bind(&done);
1385 : }
1386 0 : return true;
1387 : }
1388 : // No custom implementation (yet):
1389 : default:
1390 0 : return false;
1391 : }
1392 : }
1393 :
1394 : bool
1395 173 : NativeRegExpMacroAssembler::CanReadUnaligned()
1396 : {
1397 : #if defined(JS_CODEGEN_ARM)
1398 : return !jit::HasAlignmentFault();
1399 : #elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
1400 : return false;
1401 : #else
1402 173 : return true;
1403 : #endif
1404 : }
1405 :
1406 : const uint8_t
1407 : NativeRegExpMacroAssembler::word_character_map[] =
1408 : {
1409 : 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
1410 : 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
1411 : 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
1412 : 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
1413 :
1414 : 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
1415 : 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
1416 : 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, // '0' - '7'
1417 : 0xffu, 0xffu, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, // '8' - '9'
1418 :
1419 : 0x00u, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, // 'A' - 'G'
1420 : 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, // 'H' - 'O'
1421 : 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, // 'P' - 'W'
1422 : 0xffu, 0xffu, 0xffu, 0x00u, 0x00u, 0x00u, 0x00u, 0xffu, // 'X' - 'Z', '_'
1423 :
1424 : 0x00u, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, // 'a' - 'g'
1425 : 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, // 'h' - 'o'
1426 : 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, // 'p' - 'w'
1427 : 0xffu, 0xffu, 0xffu, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, // 'x' - 'z'
1428 :
1429 : // Latin-1 range
1430 : 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
1431 : 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
1432 : 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
1433 : 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
1434 :
1435 : 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
1436 : 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
1437 : 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
1438 : 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
1439 :
1440 : 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
1441 : 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
1442 : 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
1443 : 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
1444 :
1445 : 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
1446 : 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
1447 : 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
1448 : 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
1449 : };
|