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