blob: 941cceaa5973eda95162267f97243beb1c64b0f4 [file] [log] [blame]
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001// Copyright 2013 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#if V8_TARGET_ARCH_ARM64
6
7#include "src/regexp/arm64/regexp-macro-assembler-arm64.h"
8
9#include "src/code-stubs.h"
10#include "src/log.h"
11#include "src/macro-assembler.h"
12#include "src/profiler/cpu-profiler.h"
13#include "src/regexp/regexp-macro-assembler.h"
14#include "src/regexp/regexp-stack.h"
15#include "src/unicode.h"
16
17namespace v8 {
18namespace internal {
19
20#ifndef V8_INTERPRETED_REGEXP
21/*
22 * This assembler uses the following register assignment convention:
23 * - w19 : Used to temporarely store a value before a call to C code.
24 * See CheckNotBackReferenceIgnoreCase.
25 * - x20 : Pointer to the current code object (Code*),
26 * it includes the heap object tag.
27 * - w21 : Current position in input, as negative offset from
28 * the end of the string. Please notice that this is
29 * the byte offset, not the character offset!
30 * - w22 : Currently loaded character. Must be loaded using
31 * LoadCurrentCharacter before using any of the dispatch methods.
32 * - x23 : Points to tip of backtrack stack.
33 * - w24 : Position of the first character minus one: non_position_value.
34 * Used to initialize capture registers.
35 * - x25 : Address at the end of the input string: input_end.
36 * Points to byte after last character in input.
37 * - x26 : Address at the start of the input string: input_start.
38 * - w27 : Where to start in the input string.
39 * - x28 : Output array pointer.
40 * - x29/fp : Frame pointer. Used to access arguments, local variables and
41 * RegExp registers.
42 * - x16/x17 : IP registers, used by assembler. Very volatile.
43 * - csp : Points to tip of C stack.
44 *
45 * - x0-x7 : Used as a cache to store 32 bit capture registers. These
46 * registers need to be retained every time a call to C code
47 * is done.
48 *
49 * The remaining registers are free for computations.
50 * Each call to a public method should retain this convention.
51 *
52 * The stack will have the following structure:
53 *
54 * Location Name Description
55 * (as referred to in
56 * the code)
57 *
58 * - fp[104] isolate Address of the current isolate.
59 * - fp[96] return_address Secondary link/return address
60 * used by an exit frame if this is a
61 * native call.
62 * ^^^ csp when called ^^^
63 * - fp[88] lr Return from the RegExp code.
64 * - fp[80] r29 Old frame pointer (CalleeSaved).
65 * - fp[0..72] r19-r28 Backup of CalleeSaved registers.
66 * - fp[-8] direct_call 1 => Direct call from JavaScript code.
67 * 0 => Call through the runtime system.
68 * - fp[-16] stack_base High end of the memory area to use as
69 * the backtracking stack.
70 * - fp[-24] output_size Output may fit multiple sets of matches.
71 * - fp[-32] input Handle containing the input string.
72 * - fp[-40] success_counter
73 * ^^^^^^^^^^^^^ From here and downwards we store 32 bit values ^^^^^^^^^^^^^
74 * - fp[-44] register N Capture registers initialized with
75 * - fp[-48] register N + 1 non_position_value.
76 * ... The first kNumCachedRegisters (N) registers
77 * ... are cached in x0 to x7.
78 * ... Only positions must be stored in the first
79 * - ... num_saved_registers_ registers.
80 * - ...
81 * - register N + num_registers - 1
82 * ^^^^^^^^^ csp ^^^^^^^^^
83 *
84 * The first num_saved_registers_ registers are initialized to point to
85 * "character -1" in the string (i.e., char_size() bytes before the first
86 * character of the string). The remaining registers start out as garbage.
87 *
88 * The data up to the return address must be placed there by the calling
89 * code and the remaining arguments are passed in registers, e.g. by calling the
90 * code entry as cast to a function with the signature:
91 * int (*match)(String* input,
92 * int start_offset,
93 * Address input_start,
94 * Address input_end,
95 * int* output,
96 * int output_size,
97 * Address stack_base,
98 * bool direct_call = false,
99 * Address secondary_return_address, // Only used by native call.
100 * Isolate* isolate)
101 * The call is performed by NativeRegExpMacroAssembler::Execute()
102 * (in regexp-macro-assembler.cc) via the CALL_GENERATED_REGEXP_CODE macro
103 * in arm64/simulator-arm64.h.
104 * When calling as a non-direct call (i.e., from C++ code), the return address
105 * area is overwritten with the LR register by the RegExp code. When doing a
106 * direct call from generated code, the return address is placed there by
107 * the calling code, as in a normal exit frame.
108 */
109
110#define __ ACCESS_MASM(masm_)
111
112RegExpMacroAssemblerARM64::RegExpMacroAssemblerARM64(Isolate* isolate,
113 Zone* zone, Mode mode,
114 int registers_to_save)
115 : NativeRegExpMacroAssembler(isolate, zone),
116 masm_(new MacroAssembler(isolate, NULL, kRegExpCodeSize,
117 CodeObjectRequired::kYes)),
118 mode_(mode),
119 num_registers_(registers_to_save),
120 num_saved_registers_(registers_to_save),
121 entry_label_(),
122 start_label_(),
123 success_label_(),
124 backtrack_label_(),
125 exit_label_() {
126 __ SetStackPointer(csp);
127 DCHECK_EQ(0, registers_to_save % 2);
128 // We can cache at most 16 W registers in x0-x7.
129 STATIC_ASSERT(kNumCachedRegisters <= 16);
130 STATIC_ASSERT((kNumCachedRegisters % 2) == 0);
131 __ B(&entry_label_); // We'll write the entry code later.
132 __ Bind(&start_label_); // And then continue from here.
133}
134
135
136RegExpMacroAssemblerARM64::~RegExpMacroAssemblerARM64() {
137 delete masm_;
138 // Unuse labels in case we throw away the assembler without calling GetCode.
139 entry_label_.Unuse();
140 start_label_.Unuse();
141 success_label_.Unuse();
142 backtrack_label_.Unuse();
143 exit_label_.Unuse();
144 check_preempt_label_.Unuse();
145 stack_overflow_label_.Unuse();
146}
147
148int RegExpMacroAssemblerARM64::stack_limit_slack() {
149 return RegExpStack::kStackLimitSlack;
150}
151
152
153void RegExpMacroAssemblerARM64::AdvanceCurrentPosition(int by) {
154 if (by != 0) {
155 __ Add(current_input_offset(),
156 current_input_offset(), by * char_size());
157 }
158}
159
160
161void RegExpMacroAssemblerARM64::AdvanceRegister(int reg, int by) {
162 DCHECK((reg >= 0) && (reg < num_registers_));
163 if (by != 0) {
164 Register to_advance;
165 RegisterState register_state = GetRegisterState(reg);
166 switch (register_state) {
167 case STACKED:
168 __ Ldr(w10, register_location(reg));
169 __ Add(w10, w10, by);
170 __ Str(w10, register_location(reg));
171 break;
172 case CACHED_LSW:
173 to_advance = GetCachedRegister(reg);
174 __ Add(to_advance, to_advance, by);
175 break;
176 case CACHED_MSW:
177 to_advance = GetCachedRegister(reg);
178 __ Add(to_advance, to_advance,
179 static_cast<int64_t>(by) << kWRegSizeInBits);
180 break;
181 default:
182 UNREACHABLE();
183 break;
184 }
185 }
186}
187
188
189void RegExpMacroAssemblerARM64::Backtrack() {
190 CheckPreemption();
191 Pop(w10);
192 __ Add(x10, code_pointer(), Operand(w10, UXTW));
193 __ Br(x10);
194}
195
196
197void RegExpMacroAssemblerARM64::Bind(Label* label) {
198 __ Bind(label);
199}
200
201
202void RegExpMacroAssemblerARM64::CheckCharacter(uint32_t c, Label* on_equal) {
203 CompareAndBranchOrBacktrack(current_character(), c, eq, on_equal);
204}
205
206
207void RegExpMacroAssemblerARM64::CheckCharacterGT(uc16 limit,
208 Label* on_greater) {
209 CompareAndBranchOrBacktrack(current_character(), limit, hi, on_greater);
210}
211
212
213void RegExpMacroAssemblerARM64::CheckAtStart(Label* on_at_start) {
214 __ Add(w10, current_input_offset(), Operand(-char_size()));
215 __ Cmp(w10, string_start_minus_one());
216 BranchOrBacktrack(eq, on_at_start);
217}
218
219
220void RegExpMacroAssemblerARM64::CheckNotAtStart(int cp_offset,
221 Label* on_not_at_start) {
222 __ Add(w10, current_input_offset(),
223 Operand(-char_size() + cp_offset * char_size()));
224 __ Cmp(w10, string_start_minus_one());
225 BranchOrBacktrack(ne, on_not_at_start);
226}
227
228
229void RegExpMacroAssemblerARM64::CheckCharacterLT(uc16 limit, Label* on_less) {
230 CompareAndBranchOrBacktrack(current_character(), limit, lo, on_less);
231}
232
233
234void RegExpMacroAssemblerARM64::CheckCharacters(Vector<const uc16> str,
235 int cp_offset,
236 Label* on_failure,
237 bool check_end_of_string) {
238 // This method is only ever called from the cctests.
239
240 if (check_end_of_string) {
241 // Is last character of required match inside string.
242 CheckPosition(cp_offset + str.length() - 1, on_failure);
243 }
244
245 Register characters_address = x11;
246
247 __ Add(characters_address,
248 input_end(),
249 Operand(current_input_offset(), SXTW));
250 if (cp_offset != 0) {
251 __ Add(characters_address, characters_address, cp_offset * char_size());
252 }
253
254 for (int i = 0; i < str.length(); i++) {
255 if (mode_ == LATIN1) {
256 __ Ldrb(w10, MemOperand(characters_address, 1, PostIndex));
257 DCHECK(str[i] <= String::kMaxOneByteCharCode);
258 } else {
259 __ Ldrh(w10, MemOperand(characters_address, 2, PostIndex));
260 }
261 CompareAndBranchOrBacktrack(w10, str[i], ne, on_failure);
262 }
263}
264
265
266void RegExpMacroAssemblerARM64::CheckGreedyLoop(Label* on_equal) {
267 __ Ldr(w10, MemOperand(backtrack_stackpointer()));
268 __ Cmp(current_input_offset(), w10);
269 __ Cset(x11, eq);
270 __ Add(backtrack_stackpointer(),
271 backtrack_stackpointer(), Operand(x11, LSL, kWRegSizeLog2));
272 BranchOrBacktrack(eq, on_equal);
273}
274
275
276void RegExpMacroAssemblerARM64::CheckNotBackReferenceIgnoreCase(
Ben Murdoch097c5b22016-05-18 11:27:45 +0100277 int start_reg, bool read_backward, bool unicode, Label* on_no_match) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000278 Label fallthrough;
279
280 Register capture_start_offset = w10;
281 // Save the capture length in a callee-saved register so it will
282 // be preserved if we call a C helper.
283 Register capture_length = w19;
284 DCHECK(kCalleeSaved.IncludesAliasOf(capture_length));
285
286 // Find length of back-referenced capture.
287 DCHECK((start_reg % 2) == 0);
288 if (start_reg < kNumCachedRegisters) {
289 __ Mov(capture_start_offset.X(), GetCachedRegister(start_reg));
290 __ Lsr(x11, GetCachedRegister(start_reg), kWRegSizeInBits);
291 } else {
292 __ Ldp(w11, capture_start_offset, capture_location(start_reg, x10));
293 }
294 __ Sub(capture_length, w11, capture_start_offset); // Length to check.
295
296 // At this point, the capture registers are either both set or both cleared.
297 // If the capture length is zero, then the capture is either empty or cleared.
298 // Fall through in both cases.
299 __ CompareAndBranch(capture_length, Operand(0), eq, &fallthrough);
300
301 // Check that there are enough characters left in the input.
302 if (read_backward) {
303 __ Add(w12, string_start_minus_one(), capture_length);
304 __ Cmp(current_input_offset(), w12);
305 BranchOrBacktrack(le, on_no_match);
306 } else {
307 __ Cmn(capture_length, current_input_offset());
308 BranchOrBacktrack(gt, on_no_match);
309 }
310
311 if (mode_ == LATIN1) {
312 Label success;
313 Label fail;
314 Label loop_check;
315
316 Register capture_start_address = x12;
317 Register capture_end_addresss = x13;
318 Register current_position_address = x14;
319
320 __ Add(capture_start_address,
321 input_end(),
322 Operand(capture_start_offset, SXTW));
323 __ Add(capture_end_addresss,
324 capture_start_address,
325 Operand(capture_length, SXTW));
326 __ Add(current_position_address,
327 input_end(),
328 Operand(current_input_offset(), SXTW));
329 if (read_backward) {
330 // Offset by length when matching backwards.
331 __ Sub(current_position_address, current_position_address,
332 Operand(capture_length, SXTW));
333 }
334
335 Label loop;
336 __ Bind(&loop);
337 __ Ldrb(w10, MemOperand(capture_start_address, 1, PostIndex));
338 __ Ldrb(w11, MemOperand(current_position_address, 1, PostIndex));
339 __ Cmp(w10, w11);
340 __ B(eq, &loop_check);
341
342 // Mismatch, try case-insensitive match (converting letters to lower-case).
343 __ Orr(w10, w10, 0x20); // Convert capture character to lower-case.
344 __ Orr(w11, w11, 0x20); // Also convert input character.
345 __ Cmp(w11, w10);
346 __ B(ne, &fail);
347 __ Sub(w10, w10, 'a');
348 __ Cmp(w10, 'z' - 'a'); // Is w10 a lowercase letter?
349 __ B(ls, &loop_check); // In range 'a'-'z'.
350 // Latin-1: Check for values in range [224,254] but not 247.
351 __ Sub(w10, w10, 224 - 'a');
352 __ Cmp(w10, 254 - 224);
353 __ Ccmp(w10, 247 - 224, ZFlag, ls); // Check for 247.
354 __ B(eq, &fail); // Weren't Latin-1 letters.
355
356 __ Bind(&loop_check);
357 __ Cmp(capture_start_address, capture_end_addresss);
358 __ B(lt, &loop);
359 __ B(&success);
360
361 __ Bind(&fail);
362 BranchOrBacktrack(al, on_no_match);
363
364 __ Bind(&success);
365 // Compute new value of character position after the matched part.
366 __ Sub(current_input_offset().X(), current_position_address, input_end());
367 if (read_backward) {
368 __ Sub(current_input_offset().X(), current_input_offset().X(),
369 Operand(capture_length, SXTW));
370 }
371 if (masm_->emit_debug_code()) {
372 __ Cmp(current_input_offset().X(), Operand(current_input_offset(), SXTW));
373 __ Ccmp(current_input_offset(), 0, NoFlag, eq);
374 // The current input offset should be <= 0, and fit in a W register.
375 __ Check(le, kOffsetOutOfRange);
376 }
377 } else {
378 DCHECK(mode_ == UC16);
379 int argument_count = 4;
380
381 // The cached registers need to be retained.
382 CPURegList cached_registers(CPURegister::kRegister, kXRegSizeInBits, 0, 7);
383 DCHECK((cached_registers.Count() * 2) == kNumCachedRegisters);
384 __ PushCPURegList(cached_registers);
385
386 // Put arguments into arguments registers.
387 // Parameters are
388 // x0: Address byte_offset1 - Address captured substring's start.
389 // x1: Address byte_offset2 - Address of current character position.
390 // w2: size_t byte_length - length of capture in bytes(!)
Ben Murdoch097c5b22016-05-18 11:27:45 +0100391 // x3: Isolate* isolate or 0 if unicode flag
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000392
393 // Address of start of capture.
394 __ Add(x0, input_end(), Operand(capture_start_offset, SXTW));
395 // Length of capture.
396 __ Mov(w2, capture_length);
397 // Address of current input position.
398 __ Add(x1, input_end(), Operand(current_input_offset(), SXTW));
399 if (read_backward) {
400 __ Sub(x1, x1, Operand(capture_length, SXTW));
401 }
402 // Isolate.
Ben Murdoch097c5b22016-05-18 11:27:45 +0100403#ifdef V8_I18N_SUPPORT
404 if (unicode) {
405 __ Mov(x3, Operand(0));
406 } else // NOLINT
407#endif // V8_I18N_SUPPORT
408 {
409 __ Mov(x3, ExternalReference::isolate_address(isolate()));
410 }
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000411
412 {
413 AllowExternalCallThatCantCauseGC scope(masm_);
414 ExternalReference function =
415 ExternalReference::re_case_insensitive_compare_uc16(isolate());
416 __ CallCFunction(function, argument_count);
417 }
418
419 // Check if function returned non-zero for success or zero for failure.
420 // x0 is one of the registers used as a cache so it must be tested before
421 // the cache is restored.
422 __ Cmp(x0, 0);
423 __ PopCPURegList(cached_registers);
424 BranchOrBacktrack(eq, on_no_match);
425
426 // On success, advance position by length of capture.
427 if (read_backward) {
428 __ Sub(current_input_offset(), current_input_offset(), capture_length);
429 } else {
430 __ Add(current_input_offset(), current_input_offset(), capture_length);
431 }
432 }
433
434 __ Bind(&fallthrough);
435}
436
437void RegExpMacroAssemblerARM64::CheckNotBackReference(int start_reg,
438 bool read_backward,
439 Label* on_no_match) {
440 Label fallthrough;
441
442 Register capture_start_address = x12;
443 Register capture_end_address = x13;
444 Register current_position_address = x14;
445 Register capture_length = w15;
446
447 // Find length of back-referenced capture.
448 DCHECK((start_reg % 2) == 0);
449 if (start_reg < kNumCachedRegisters) {
450 __ Mov(x10, GetCachedRegister(start_reg));
451 __ Lsr(x11, GetCachedRegister(start_reg), kWRegSizeInBits);
452 } else {
453 __ Ldp(w11, w10, capture_location(start_reg, x10));
454 }
455 __ Sub(capture_length, w11, w10); // Length to check.
456
457 // At this point, the capture registers are either both set or both cleared.
458 // If the capture length is zero, then the capture is either empty or cleared.
459 // Fall through in both cases.
460 __ CompareAndBranch(capture_length, Operand(0), eq, &fallthrough);
461
462 // Check that there are enough characters left in the input.
463 if (read_backward) {
464 __ Add(w12, string_start_minus_one(), capture_length);
465 __ Cmp(current_input_offset(), w12);
466 BranchOrBacktrack(le, on_no_match);
467 } else {
468 __ Cmn(capture_length, current_input_offset());
469 BranchOrBacktrack(gt, on_no_match);
470 }
471
472 // Compute pointers to match string and capture string
473 __ Add(capture_start_address, input_end(), Operand(w10, SXTW));
474 __ Add(capture_end_address,
475 capture_start_address,
476 Operand(capture_length, SXTW));
477 __ Add(current_position_address,
478 input_end(),
479 Operand(current_input_offset(), SXTW));
480 if (read_backward) {
481 // Offset by length when matching backwards.
482 __ Sub(current_position_address, current_position_address,
483 Operand(capture_length, SXTW));
484 }
485
486 Label loop;
487 __ Bind(&loop);
488 if (mode_ == LATIN1) {
489 __ Ldrb(w10, MemOperand(capture_start_address, 1, PostIndex));
490 __ Ldrb(w11, MemOperand(current_position_address, 1, PostIndex));
491 } else {
492 DCHECK(mode_ == UC16);
493 __ Ldrh(w10, MemOperand(capture_start_address, 2, PostIndex));
494 __ Ldrh(w11, MemOperand(current_position_address, 2, PostIndex));
495 }
496 __ Cmp(w10, w11);
497 BranchOrBacktrack(ne, on_no_match);
498 __ Cmp(capture_start_address, capture_end_address);
499 __ B(lt, &loop);
500
501 // Move current character position to position after match.
502 __ Sub(current_input_offset().X(), current_position_address, input_end());
503 if (read_backward) {
504 __ Sub(current_input_offset().X(), current_input_offset().X(),
505 Operand(capture_length, SXTW));
506 }
507
508 if (masm_->emit_debug_code()) {
509 __ Cmp(current_input_offset().X(), Operand(current_input_offset(), SXTW));
510 __ Ccmp(current_input_offset(), 0, NoFlag, eq);
511 // The current input offset should be <= 0, and fit in a W register.
512 __ Check(le, kOffsetOutOfRange);
513 }
514 __ Bind(&fallthrough);
515}
516
517
518void RegExpMacroAssemblerARM64::CheckNotCharacter(unsigned c,
519 Label* on_not_equal) {
520 CompareAndBranchOrBacktrack(current_character(), c, ne, on_not_equal);
521}
522
523
524void RegExpMacroAssemblerARM64::CheckCharacterAfterAnd(uint32_t c,
525 uint32_t mask,
526 Label* on_equal) {
527 __ And(w10, current_character(), mask);
528 CompareAndBranchOrBacktrack(w10, c, eq, on_equal);
529}
530
531
532void RegExpMacroAssemblerARM64::CheckNotCharacterAfterAnd(unsigned c,
533 unsigned mask,
534 Label* on_not_equal) {
535 __ And(w10, current_character(), mask);
536 CompareAndBranchOrBacktrack(w10, c, ne, on_not_equal);
537}
538
539
540void RegExpMacroAssemblerARM64::CheckNotCharacterAfterMinusAnd(
541 uc16 c,
542 uc16 minus,
543 uc16 mask,
544 Label* on_not_equal) {
545 DCHECK(minus < String::kMaxUtf16CodeUnit);
546 __ Sub(w10, current_character(), minus);
547 __ And(w10, w10, mask);
548 CompareAndBranchOrBacktrack(w10, c, ne, on_not_equal);
549}
550
551
552void RegExpMacroAssemblerARM64::CheckCharacterInRange(
553 uc16 from,
554 uc16 to,
555 Label* on_in_range) {
556 __ Sub(w10, current_character(), from);
557 // Unsigned lower-or-same condition.
558 CompareAndBranchOrBacktrack(w10, to - from, ls, on_in_range);
559}
560
561
562void RegExpMacroAssemblerARM64::CheckCharacterNotInRange(
563 uc16 from,
564 uc16 to,
565 Label* on_not_in_range) {
566 __ Sub(w10, current_character(), from);
567 // Unsigned higher condition.
568 CompareAndBranchOrBacktrack(w10, to - from, hi, on_not_in_range);
569}
570
571
572void RegExpMacroAssemblerARM64::CheckBitInTable(
573 Handle<ByteArray> table,
574 Label* on_bit_set) {
575 __ Mov(x11, Operand(table));
576 if ((mode_ != LATIN1) || (kTableMask != String::kMaxOneByteCharCode)) {
577 __ And(w10, current_character(), kTableMask);
578 __ Add(w10, w10, ByteArray::kHeaderSize - kHeapObjectTag);
579 } else {
580 __ Add(w10, current_character(), ByteArray::kHeaderSize - kHeapObjectTag);
581 }
582 __ Ldrb(w11, MemOperand(x11, w10, UXTW));
583 CompareAndBranchOrBacktrack(w11, 0, ne, on_bit_set);
584}
585
586
587bool RegExpMacroAssemblerARM64::CheckSpecialCharacterClass(uc16 type,
588 Label* on_no_match) {
589 // Range checks (c in min..max) are generally implemented by an unsigned
590 // (c - min) <= (max - min) check
591 switch (type) {
592 case 's':
593 // Match space-characters
594 if (mode_ == LATIN1) {
595 // One byte space characters are '\t'..'\r', ' ' and \u00a0.
596 Label success;
597 // Check for ' ' or 0x00a0.
598 __ Cmp(current_character(), ' ');
599 __ Ccmp(current_character(), 0x00a0, ZFlag, ne);
600 __ B(eq, &success);
601 // Check range 0x09..0x0d.
602 __ Sub(w10, current_character(), '\t');
603 CompareAndBranchOrBacktrack(w10, '\r' - '\t', hi, on_no_match);
604 __ Bind(&success);
605 return true;
606 }
607 return false;
608 case 'S':
609 // The emitted code for generic character classes is good enough.
610 return false;
611 case 'd':
612 // Match ASCII digits ('0'..'9').
613 __ Sub(w10, current_character(), '0');
614 CompareAndBranchOrBacktrack(w10, '9' - '0', hi, on_no_match);
615 return true;
616 case 'D':
617 // Match ASCII non-digits.
618 __ Sub(w10, current_character(), '0');
619 CompareAndBranchOrBacktrack(w10, '9' - '0', ls, on_no_match);
620 return true;
621 case '.': {
622 // Match non-newlines (not 0x0a('\n'), 0x0d('\r'), 0x2028 and 0x2029)
623 // Here we emit the conditional branch only once at the end to make branch
624 // prediction more efficient, even though we could branch out of here
625 // as soon as a character matches.
626 __ Cmp(current_character(), 0x0a);
627 __ Ccmp(current_character(), 0x0d, ZFlag, ne);
628 if (mode_ == UC16) {
629 __ Sub(w10, current_character(), 0x2028);
630 // If the Z flag was set we clear the flags to force a branch.
631 __ Ccmp(w10, 0x2029 - 0x2028, NoFlag, ne);
632 // ls -> !((C==1) && (Z==0))
633 BranchOrBacktrack(ls, on_no_match);
634 } else {
635 BranchOrBacktrack(eq, on_no_match);
636 }
637 return true;
638 }
639 case 'n': {
640 // Match newlines (0x0a('\n'), 0x0d('\r'), 0x2028 and 0x2029)
641 // We have to check all 4 newline characters before emitting
642 // the conditional branch.
643 __ Cmp(current_character(), 0x0a);
644 __ Ccmp(current_character(), 0x0d, ZFlag, ne);
645 if (mode_ == UC16) {
646 __ Sub(w10, current_character(), 0x2028);
647 // If the Z flag was set we clear the flags to force a fall-through.
648 __ Ccmp(w10, 0x2029 - 0x2028, NoFlag, ne);
649 // hi -> (C==1) && (Z==0)
650 BranchOrBacktrack(hi, on_no_match);
651 } else {
652 BranchOrBacktrack(ne, on_no_match);
653 }
654 return true;
655 }
656 case 'w': {
657 if (mode_ != LATIN1) {
658 // Table is 256 entries, so all Latin1 characters can be tested.
659 CompareAndBranchOrBacktrack(current_character(), 'z', hi, on_no_match);
660 }
661 ExternalReference map = ExternalReference::re_word_character_map();
662 __ Mov(x10, map);
663 __ Ldrb(w10, MemOperand(x10, current_character(), UXTW));
664 CompareAndBranchOrBacktrack(w10, 0, eq, on_no_match);
665 return true;
666 }
667 case 'W': {
668 Label done;
669 if (mode_ != LATIN1) {
670 // Table is 256 entries, so all Latin1 characters can be tested.
671 __ Cmp(current_character(), 'z');
672 __ B(hi, &done);
673 }
674 ExternalReference map = ExternalReference::re_word_character_map();
675 __ Mov(x10, map);
676 __ Ldrb(w10, MemOperand(x10, current_character(), UXTW));
677 CompareAndBranchOrBacktrack(w10, 0, ne, on_no_match);
678 __ Bind(&done);
679 return true;
680 }
681 case '*':
682 // Match any character.
683 return true;
684 // No custom implementation (yet): s(UC16), S(UC16).
685 default:
686 return false;
687 }
688}
689
690
691void RegExpMacroAssemblerARM64::Fail() {
692 __ Mov(w0, FAILURE);
693 __ B(&exit_label_);
694}
695
696
697Handle<HeapObject> RegExpMacroAssemblerARM64::GetCode(Handle<String> source) {
698 Label return_w0;
699 // Finalize code - write the entry point code now we know how many
700 // registers we need.
701
702 // Entry code:
703 __ Bind(&entry_label_);
704
705 // Arguments on entry:
706 // x0: String* input
707 // x1: int start_offset
708 // x2: byte* input_start
709 // x3: byte* input_end
710 // x4: int* output array
711 // x5: int output array size
712 // x6: Address stack_base
713 // x7: int direct_call
714
715 // The stack pointer should be csp on entry.
716 // csp[8]: address of the current isolate
717 // csp[0]: secondary link/return address used by native call
718
719 // Tell the system that we have a stack frame. Because the type is MANUAL, no
720 // code is generated.
721 FrameScope scope(masm_, StackFrame::MANUAL);
722
723 // Push registers on the stack, only push the argument registers that we need.
724 CPURegList argument_registers(x0, x5, x6, x7);
725
726 CPURegList registers_to_retain = kCalleeSaved;
727 DCHECK(kCalleeSaved.Count() == 11);
728 registers_to_retain.Combine(lr);
729
730 DCHECK(csp.Is(__ StackPointer()));
731 __ PushCPURegList(registers_to_retain);
732 __ PushCPURegList(argument_registers);
733
734 // Set frame pointer in place.
735 __ Add(frame_pointer(), csp, argument_registers.Count() * kPointerSize);
736
737 // Initialize callee-saved registers.
738 __ Mov(start_offset(), w1);
739 __ Mov(input_start(), x2);
740 __ Mov(input_end(), x3);
741 __ Mov(output_array(), x4);
742
743 // Set the number of registers we will need to allocate, that is:
744 // - success_counter (X register)
745 // - (num_registers_ - kNumCachedRegisters) (W registers)
746 int num_wreg_to_allocate = num_registers_ - kNumCachedRegisters;
747 // Do not allocate registers on the stack if they can all be cached.
748 if (num_wreg_to_allocate < 0) { num_wreg_to_allocate = 0; }
749 // Make room for the success_counter.
750 num_wreg_to_allocate += 2;
751
752 // Make sure the stack alignment will be respected.
753 int alignment = masm_->ActivationFrameAlignment();
754 DCHECK_EQ(alignment % 16, 0);
755 int align_mask = (alignment / kWRegSize) - 1;
756 num_wreg_to_allocate = (num_wreg_to_allocate + align_mask) & ~align_mask;
757
758 // Check if we have space on the stack.
759 Label stack_limit_hit;
760 Label stack_ok;
761
762 ExternalReference stack_limit =
763 ExternalReference::address_of_stack_limit(isolate());
764 __ Mov(x10, stack_limit);
765 __ Ldr(x10, MemOperand(x10));
766 __ Subs(x10, csp, x10);
767
768 // Handle it if the stack pointer is already below the stack limit.
769 __ B(ls, &stack_limit_hit);
770
771 // Check if there is room for the variable number of registers above
772 // the stack limit.
773 __ Cmp(x10, num_wreg_to_allocate * kWRegSize);
774 __ B(hs, &stack_ok);
775
776 // Exit with OutOfMemory exception. There is not enough space on the stack
777 // for our working registers.
778 __ Mov(w0, EXCEPTION);
779 __ B(&return_w0);
780
781 __ Bind(&stack_limit_hit);
782 CallCheckStackGuardState(x10);
783 // If returned value is non-zero, we exit with the returned value as result.
784 __ Cbnz(w0, &return_w0);
785
786 __ Bind(&stack_ok);
787
788 // Allocate space on stack.
789 __ Claim(num_wreg_to_allocate, kWRegSize);
790
791 // Initialize success_counter with 0.
792 __ Str(wzr, MemOperand(frame_pointer(), kSuccessCounter));
793
794 // Find negative length (offset of start relative to end).
795 __ Sub(x10, input_start(), input_end());
796 if (masm_->emit_debug_code()) {
797 // Check that the input string length is < 2^30.
798 __ Neg(x11, x10);
799 __ Cmp(x11, (1<<30) - 1);
800 __ Check(ls, kInputStringTooLong);
801 }
802 __ Mov(current_input_offset(), w10);
803
804 // The non-position value is used as a clearing value for the
805 // capture registers, it corresponds to the position of the first character
806 // minus one.
807 __ Sub(string_start_minus_one(), current_input_offset(), char_size());
808 __ Sub(string_start_minus_one(), string_start_minus_one(),
809 Operand(start_offset(), LSL, (mode_ == UC16) ? 1 : 0));
810 // We can store this value twice in an X register for initializing
811 // on-stack registers later.
812 __ Orr(twice_non_position_value(), string_start_minus_one().X(),
813 Operand(string_start_minus_one().X(), LSL, kWRegSizeInBits));
814
815 // Initialize code pointer register.
816 __ Mov(code_pointer(), Operand(masm_->CodeObject()));
817
818 Label load_char_start_regexp, start_regexp;
819 // Load newline if index is at start, previous character otherwise.
820 __ Cbnz(start_offset(), &load_char_start_regexp);
821 __ Mov(current_character(), '\n');
822 __ B(&start_regexp);
823
824 // Global regexp restarts matching here.
825 __ Bind(&load_char_start_regexp);
826 // Load previous char as initial value of current character register.
827 LoadCurrentCharacterUnchecked(-1, 1);
828 __ Bind(&start_regexp);
829 // Initialize on-stack registers.
830 if (num_saved_registers_ > 0) {
831 ClearRegisters(0, num_saved_registers_ - 1);
832 }
833
834 // Initialize backtrack stack pointer.
835 __ Ldr(backtrack_stackpointer(), MemOperand(frame_pointer(), kStackBase));
836
837 // Execute
838 __ B(&start_label_);
839
840 if (backtrack_label_.is_linked()) {
841 __ Bind(&backtrack_label_);
842 Backtrack();
843 }
844
845 if (success_label_.is_linked()) {
846 Register first_capture_start = w15;
847
848 // Save captures when successful.
849 __ Bind(&success_label_);
850
851 if (num_saved_registers_ > 0) {
852 // V8 expects the output to be an int32_t array.
853 Register capture_start = w12;
854 Register capture_end = w13;
855 Register input_length = w14;
856
857 // Copy captures to output.
858
859 // Get string length.
860 __ Sub(x10, input_end(), input_start());
861 if (masm_->emit_debug_code()) {
862 // Check that the input string length is < 2^30.
863 __ Cmp(x10, (1<<30) - 1);
864 __ Check(ls, kInputStringTooLong);
865 }
866 // input_start has a start_offset offset on entry. We need to include
867 // it when computing the length of the whole string.
868 if (mode_ == UC16) {
869 __ Add(input_length, start_offset(), Operand(w10, LSR, 1));
870 } else {
871 __ Add(input_length, start_offset(), w10);
872 }
873
874 // Copy the results to the output array from the cached registers first.
875 for (int i = 0;
876 (i < num_saved_registers_) && (i < kNumCachedRegisters);
877 i += 2) {
878 __ Mov(capture_start.X(), GetCachedRegister(i));
879 __ Lsr(capture_end.X(), capture_start.X(), kWRegSizeInBits);
880 if ((i == 0) && global_with_zero_length_check()) {
881 // Keep capture start for the zero-length check later.
882 __ Mov(first_capture_start, capture_start);
883 }
884 // Offsets need to be relative to the start of the string.
885 if (mode_ == UC16) {
886 __ Add(capture_start, input_length, Operand(capture_start, ASR, 1));
887 __ Add(capture_end, input_length, Operand(capture_end, ASR, 1));
888 } else {
889 __ Add(capture_start, input_length, capture_start);
890 __ Add(capture_end, input_length, capture_end);
891 }
892 // The output pointer advances for a possible global match.
893 __ Stp(capture_start,
894 capture_end,
895 MemOperand(output_array(), kPointerSize, PostIndex));
896 }
897
898 // Only carry on if there are more than kNumCachedRegisters capture
899 // registers.
900 int num_registers_left_on_stack =
901 num_saved_registers_ - kNumCachedRegisters;
902 if (num_registers_left_on_stack > 0) {
903 Register base = x10;
904 // There are always an even number of capture registers. A couple of
905 // registers determine one match with two offsets.
906 DCHECK_EQ(0, num_registers_left_on_stack % 2);
907 __ Add(base, frame_pointer(), kFirstCaptureOnStack);
908
909 // We can unroll the loop here, we should not unroll for less than 2
910 // registers.
911 STATIC_ASSERT(kNumRegistersToUnroll > 2);
912 if (num_registers_left_on_stack <= kNumRegistersToUnroll) {
913 for (int i = 0; i < num_registers_left_on_stack / 2; i++) {
914 __ Ldp(capture_end,
915 capture_start,
916 MemOperand(base, -kPointerSize, PostIndex));
917 if ((i == 0) && global_with_zero_length_check()) {
918 // Keep capture start for the zero-length check later.
919 __ Mov(first_capture_start, capture_start);
920 }
921 // Offsets need to be relative to the start of the string.
922 if (mode_ == UC16) {
923 __ Add(capture_start,
924 input_length,
925 Operand(capture_start, ASR, 1));
926 __ Add(capture_end, input_length, Operand(capture_end, ASR, 1));
927 } else {
928 __ Add(capture_start, input_length, capture_start);
929 __ Add(capture_end, input_length, capture_end);
930 }
931 // The output pointer advances for a possible global match.
932 __ Stp(capture_start,
933 capture_end,
934 MemOperand(output_array(), kPointerSize, PostIndex));
935 }
936 } else {
937 Label loop, start;
938 __ Mov(x11, num_registers_left_on_stack);
939
940 __ Ldp(capture_end,
941 capture_start,
942 MemOperand(base, -kPointerSize, PostIndex));
943 if (global_with_zero_length_check()) {
944 __ Mov(first_capture_start, capture_start);
945 }
946 __ B(&start);
947
948 __ Bind(&loop);
949 __ Ldp(capture_end,
950 capture_start,
951 MemOperand(base, -kPointerSize, PostIndex));
952 __ Bind(&start);
953 if (mode_ == UC16) {
954 __ Add(capture_start, input_length, Operand(capture_start, ASR, 1));
955 __ Add(capture_end, input_length, Operand(capture_end, ASR, 1));
956 } else {
957 __ Add(capture_start, input_length, capture_start);
958 __ Add(capture_end, input_length, capture_end);
959 }
960 // The output pointer advances for a possible global match.
961 __ Stp(capture_start,
962 capture_end,
963 MemOperand(output_array(), kPointerSize, PostIndex));
964 __ Sub(x11, x11, 2);
965 __ Cbnz(x11, &loop);
966 }
967 }
968 }
969
970 if (global()) {
971 Register success_counter = w0;
972 Register output_size = x10;
973 // Restart matching if the regular expression is flagged as global.
974
975 // Increment success counter.
976 __ Ldr(success_counter, MemOperand(frame_pointer(), kSuccessCounter));
977 __ Add(success_counter, success_counter, 1);
978 __ Str(success_counter, MemOperand(frame_pointer(), kSuccessCounter));
979
980 // Capture results have been stored, so the number of remaining global
981 // output registers is reduced by the number of stored captures.
982 __ Ldr(output_size, MemOperand(frame_pointer(), kOutputSize));
983 __ Sub(output_size, output_size, num_saved_registers_);
984 // Check whether we have enough room for another set of capture results.
985 __ Cmp(output_size, num_saved_registers_);
986 __ B(lt, &return_w0);
987
988 // The output pointer is already set to the next field in the output
989 // array.
990 // Update output size on the frame before we restart matching.
991 __ Str(output_size, MemOperand(frame_pointer(), kOutputSize));
992
993 if (global_with_zero_length_check()) {
994 // Special case for zero-length matches.
995 __ Cmp(current_input_offset(), first_capture_start);
996 // Not a zero-length match, restart.
997 __ B(ne, &load_char_start_regexp);
998 // Offset from the end is zero if we already reached the end.
999 __ Cbz(current_input_offset(), &return_w0);
1000 // Advance current position after a zero-length match.
Ben Murdoch097c5b22016-05-18 11:27:45 +01001001 Label advance;
1002 __ bind(&advance);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001003 __ Add(current_input_offset(),
1004 current_input_offset(),
1005 Operand((mode_ == UC16) ? 2 : 1));
Ben Murdoch097c5b22016-05-18 11:27:45 +01001006 if (global_unicode()) CheckNotInSurrogatePair(0, &advance);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001007 }
1008
1009 __ B(&load_char_start_regexp);
1010 } else {
1011 __ Mov(w0, SUCCESS);
1012 }
1013 }
1014
1015 if (exit_label_.is_linked()) {
1016 // Exit and return w0
1017 __ Bind(&exit_label_);
1018 if (global()) {
1019 __ Ldr(w0, MemOperand(frame_pointer(), kSuccessCounter));
1020 }
1021 }
1022
1023 __ Bind(&return_w0);
1024
1025 // Set stack pointer back to first register to retain
1026 DCHECK(csp.Is(__ StackPointer()));
1027 __ Mov(csp, fp);
1028 __ AssertStackConsistency();
1029
1030 // Restore registers.
1031 __ PopCPURegList(registers_to_retain);
1032
1033 __ Ret();
1034
1035 Label exit_with_exception;
1036 // Registers x0 to x7 are used to store the first captures, they need to be
1037 // retained over calls to C++ code.
1038 CPURegList cached_registers(CPURegister::kRegister, kXRegSizeInBits, 0, 7);
1039 DCHECK((cached_registers.Count() * 2) == kNumCachedRegisters);
1040
1041 if (check_preempt_label_.is_linked()) {
1042 __ Bind(&check_preempt_label_);
1043 SaveLinkRegister();
1044 // The cached registers need to be retained.
1045 __ PushCPURegList(cached_registers);
1046 CallCheckStackGuardState(x10);
1047 // Returning from the regexp code restores the stack (csp <- fp)
1048 // so we don't need to drop the link register from it before exiting.
1049 __ Cbnz(w0, &return_w0);
1050 // Reset the cached registers.
1051 __ PopCPURegList(cached_registers);
1052 RestoreLinkRegister();
1053 __ Ret();
1054 }
1055
1056 if (stack_overflow_label_.is_linked()) {
1057 __ Bind(&stack_overflow_label_);
1058 SaveLinkRegister();
1059 // The cached registers need to be retained.
1060 __ PushCPURegList(cached_registers);
1061 // Call GrowStack(backtrack_stackpointer(), &stack_base)
1062 __ Mov(x2, ExternalReference::isolate_address(isolate()));
1063 __ Add(x1, frame_pointer(), kStackBase);
1064 __ Mov(x0, backtrack_stackpointer());
1065 ExternalReference grow_stack =
1066 ExternalReference::re_grow_stack(isolate());
1067 __ CallCFunction(grow_stack, 3);
1068 // If return NULL, we have failed to grow the stack, and
1069 // must exit with a stack-overflow exception.
1070 // Returning from the regexp code restores the stack (csp <- fp)
1071 // so we don't need to drop the link register from it before exiting.
1072 __ Cbz(w0, &exit_with_exception);
1073 // Otherwise use return value as new stack pointer.
1074 __ Mov(backtrack_stackpointer(), x0);
1075 // Reset the cached registers.
1076 __ PopCPURegList(cached_registers);
1077 RestoreLinkRegister();
1078 __ Ret();
1079 }
1080
1081 if (exit_with_exception.is_linked()) {
1082 __ Bind(&exit_with_exception);
1083 __ Mov(w0, EXCEPTION);
1084 __ B(&return_w0);
1085 }
1086
1087 CodeDesc code_desc;
1088 masm_->GetCode(&code_desc);
1089 Handle<Code> code = isolate()->factory()->NewCode(
1090 code_desc, Code::ComputeFlags(Code::REGEXP), masm_->CodeObject());
1091 PROFILE(masm_->isolate(), RegExpCodeCreateEvent(*code, *source));
1092 return Handle<HeapObject>::cast(code);
1093}
1094
1095
1096void RegExpMacroAssemblerARM64::GoTo(Label* to) {
1097 BranchOrBacktrack(al, to);
1098}
1099
1100void RegExpMacroAssemblerARM64::IfRegisterGE(int reg, int comparand,
1101 Label* if_ge) {
1102 Register to_compare = GetRegister(reg, w10);
1103 CompareAndBranchOrBacktrack(to_compare, comparand, ge, if_ge);
1104}
1105
1106
1107void RegExpMacroAssemblerARM64::IfRegisterLT(int reg, int comparand,
1108 Label* if_lt) {
1109 Register to_compare = GetRegister(reg, w10);
1110 CompareAndBranchOrBacktrack(to_compare, comparand, lt, if_lt);
1111}
1112
1113
1114void RegExpMacroAssemblerARM64::IfRegisterEqPos(int reg, Label* if_eq) {
1115 Register to_compare = GetRegister(reg, w10);
1116 __ Cmp(to_compare, current_input_offset());
1117 BranchOrBacktrack(eq, if_eq);
1118}
1119
1120RegExpMacroAssembler::IrregexpImplementation
1121 RegExpMacroAssemblerARM64::Implementation() {
1122 return kARM64Implementation;
1123}
1124
1125
1126void RegExpMacroAssemblerARM64::LoadCurrentCharacter(int cp_offset,
1127 Label* on_end_of_input,
1128 bool check_bounds,
1129 int characters) {
1130 // TODO(pielan): Make sure long strings are caught before this, and not
1131 // just asserted in debug mode.
1132 // Be sane! (And ensure that an int32_t can be used to index the string)
1133 DCHECK(cp_offset < (1<<30));
1134 if (check_bounds) {
1135 if (cp_offset >= 0) {
1136 CheckPosition(cp_offset + characters - 1, on_end_of_input);
1137 } else {
1138 CheckPosition(cp_offset, on_end_of_input);
1139 }
1140 }
1141 LoadCurrentCharacterUnchecked(cp_offset, characters);
1142}
1143
1144
1145void RegExpMacroAssemblerARM64::PopCurrentPosition() {
1146 Pop(current_input_offset());
1147}
1148
1149
1150void RegExpMacroAssemblerARM64::PopRegister(int register_index) {
1151 Pop(w10);
1152 StoreRegister(register_index, w10);
1153}
1154
1155
1156void RegExpMacroAssemblerARM64::PushBacktrack(Label* label) {
1157 if (label->is_bound()) {
1158 int target = label->pos();
1159 __ Mov(w10, target + Code::kHeaderSize - kHeapObjectTag);
1160 } else {
1161 __ Adr(x10, label, MacroAssembler::kAdrFar);
1162 __ Sub(x10, x10, code_pointer());
1163 if (masm_->emit_debug_code()) {
1164 __ Cmp(x10, kWRegMask);
1165 // The code offset has to fit in a W register.
1166 __ Check(ls, kOffsetOutOfRange);
1167 }
1168 }
1169 Push(w10);
1170 CheckStackLimit();
1171}
1172
1173
1174void RegExpMacroAssemblerARM64::PushCurrentPosition() {
1175 Push(current_input_offset());
1176}
1177
1178
1179void RegExpMacroAssemblerARM64::PushRegister(int register_index,
1180 StackCheckFlag check_stack_limit) {
1181 Register to_push = GetRegister(register_index, w10);
1182 Push(to_push);
1183 if (check_stack_limit) CheckStackLimit();
1184}
1185
1186
1187void RegExpMacroAssemblerARM64::ReadCurrentPositionFromRegister(int reg) {
1188 Register cached_register;
1189 RegisterState register_state = GetRegisterState(reg);
1190 switch (register_state) {
1191 case STACKED:
1192 __ Ldr(current_input_offset(), register_location(reg));
1193 break;
1194 case CACHED_LSW:
1195 cached_register = GetCachedRegister(reg);
1196 __ Mov(current_input_offset(), cached_register.W());
1197 break;
1198 case CACHED_MSW:
1199 cached_register = GetCachedRegister(reg);
1200 __ Lsr(current_input_offset().X(), cached_register, kWRegSizeInBits);
1201 break;
1202 default:
1203 UNREACHABLE();
1204 break;
1205 }
1206}
1207
1208
1209void RegExpMacroAssemblerARM64::ReadStackPointerFromRegister(int reg) {
1210 Register read_from = GetRegister(reg, w10);
1211 __ Ldr(x11, MemOperand(frame_pointer(), kStackBase));
1212 __ Add(backtrack_stackpointer(), x11, Operand(read_from, SXTW));
1213}
1214
1215
1216void RegExpMacroAssemblerARM64::SetCurrentPositionFromEnd(int by) {
1217 Label after_position;
1218 __ Cmp(current_input_offset(), -by * char_size());
1219 __ B(ge, &after_position);
1220 __ Mov(current_input_offset(), -by * char_size());
1221 // On RegExp code entry (where this operation is used), the character before
1222 // the current position is expected to be already loaded.
1223 // We have advanced the position, so it's safe to read backwards.
1224 LoadCurrentCharacterUnchecked(-1, 1);
1225 __ Bind(&after_position);
1226}
1227
1228
1229void RegExpMacroAssemblerARM64::SetRegister(int register_index, int to) {
1230 DCHECK(register_index >= num_saved_registers_); // Reserved for positions!
1231 Register set_to = wzr;
1232 if (to != 0) {
1233 set_to = w10;
1234 __ Mov(set_to, to);
1235 }
1236 StoreRegister(register_index, set_to);
1237}
1238
1239
1240bool RegExpMacroAssemblerARM64::Succeed() {
1241 __ B(&success_label_);
1242 return global();
1243}
1244
1245
1246void RegExpMacroAssemblerARM64::WriteCurrentPositionToRegister(int reg,
1247 int cp_offset) {
1248 Register position = current_input_offset();
1249 if (cp_offset != 0) {
1250 position = w10;
1251 __ Add(position, current_input_offset(), cp_offset * char_size());
1252 }
1253 StoreRegister(reg, position);
1254}
1255
1256
1257void RegExpMacroAssemblerARM64::ClearRegisters(int reg_from, int reg_to) {
1258 DCHECK(reg_from <= reg_to);
1259 int num_registers = reg_to - reg_from + 1;
1260
1261 // If the first capture register is cached in a hardware register but not
1262 // aligned on a 64-bit one, we need to clear the first one specifically.
1263 if ((reg_from < kNumCachedRegisters) && ((reg_from % 2) != 0)) {
1264 StoreRegister(reg_from, string_start_minus_one());
1265 num_registers--;
1266 reg_from++;
1267 }
1268
1269 // Clear cached registers in pairs as far as possible.
1270 while ((num_registers >= 2) && (reg_from < kNumCachedRegisters)) {
1271 DCHECK(GetRegisterState(reg_from) == CACHED_LSW);
1272 __ Mov(GetCachedRegister(reg_from), twice_non_position_value());
1273 reg_from += 2;
1274 num_registers -= 2;
1275 }
1276
1277 if ((num_registers % 2) == 1) {
1278 StoreRegister(reg_from, string_start_minus_one());
1279 num_registers--;
1280 reg_from++;
1281 }
1282
1283 if (num_registers > 0) {
1284 // If there are some remaining registers, they are stored on the stack.
1285 DCHECK(reg_from >= kNumCachedRegisters);
1286
1287 // Move down the indexes of the registers on stack to get the correct offset
1288 // in memory.
1289 reg_from -= kNumCachedRegisters;
1290 reg_to -= kNumCachedRegisters;
1291 // We should not unroll the loop for less than 2 registers.
1292 STATIC_ASSERT(kNumRegistersToUnroll > 2);
1293 // We position the base pointer to (reg_from + 1).
1294 int base_offset = kFirstRegisterOnStack -
1295 kWRegSize - (kWRegSize * reg_from);
1296 if (num_registers > kNumRegistersToUnroll) {
1297 Register base = x10;
1298 __ Add(base, frame_pointer(), base_offset);
1299
1300 Label loop;
1301 __ Mov(x11, num_registers);
1302 __ Bind(&loop);
1303 __ Str(twice_non_position_value(),
1304 MemOperand(base, -kPointerSize, PostIndex));
1305 __ Sub(x11, x11, 2);
1306 __ Cbnz(x11, &loop);
1307 } else {
1308 for (int i = reg_from; i <= reg_to; i += 2) {
1309 __ Str(twice_non_position_value(),
1310 MemOperand(frame_pointer(), base_offset));
1311 base_offset -= kWRegSize * 2;
1312 }
1313 }
1314 }
1315}
1316
1317
1318void RegExpMacroAssemblerARM64::WriteStackPointerToRegister(int reg) {
1319 __ Ldr(x10, MemOperand(frame_pointer(), kStackBase));
1320 __ Sub(x10, backtrack_stackpointer(), x10);
1321 if (masm_->emit_debug_code()) {
1322 __ Cmp(x10, Operand(w10, SXTW));
1323 // The stack offset needs to fit in a W register.
1324 __ Check(eq, kOffsetOutOfRange);
1325 }
1326 StoreRegister(reg, w10);
1327}
1328
1329
1330// Helper function for reading a value out of a stack frame.
1331template <typename T>
1332static T& frame_entry(Address re_frame, int frame_offset) {
1333 return *reinterpret_cast<T*>(re_frame + frame_offset);
1334}
1335
1336
1337template <typename T>
1338static T* frame_entry_address(Address re_frame, int frame_offset) {
1339 return reinterpret_cast<T*>(re_frame + frame_offset);
1340}
1341
1342
1343int RegExpMacroAssemblerARM64::CheckStackGuardState(
1344 Address* return_address, Code* re_code, Address re_frame, int start_index,
1345 const byte** input_start, const byte** input_end) {
1346 return NativeRegExpMacroAssembler::CheckStackGuardState(
1347 frame_entry<Isolate*>(re_frame, kIsolate), start_index,
1348 frame_entry<int>(re_frame, kDirectCall) == 1, return_address, re_code,
1349 frame_entry_address<String*>(re_frame, kInput), input_start, input_end);
1350}
1351
1352
1353void RegExpMacroAssemblerARM64::CheckPosition(int cp_offset,
1354 Label* on_outside_input) {
1355 if (cp_offset >= 0) {
1356 CompareAndBranchOrBacktrack(current_input_offset(),
1357 -cp_offset * char_size(), ge, on_outside_input);
1358 } else {
1359 __ Add(w12, current_input_offset(), Operand(cp_offset * char_size()));
1360 __ Cmp(w12, string_start_minus_one());
1361 BranchOrBacktrack(le, on_outside_input);
1362 }
1363}
1364
1365
1366bool RegExpMacroAssemblerARM64::CanReadUnaligned() {
1367 // TODO(pielan): See whether or not we should disable unaligned accesses.
1368 return !slow_safe();
1369}
1370
1371
1372// Private methods:
1373
1374void RegExpMacroAssemblerARM64::CallCheckStackGuardState(Register scratch) {
1375 // Allocate space on the stack to store the return address. The
1376 // CheckStackGuardState C++ function will override it if the code
1377 // moved. Allocate extra space for 2 arguments passed by pointers.
1378 // AAPCS64 requires the stack to be 16 byte aligned.
1379 int alignment = masm_->ActivationFrameAlignment();
1380 DCHECK_EQ(alignment % 16, 0);
1381 int align_mask = (alignment / kXRegSize) - 1;
1382 int xreg_to_claim = (3 + align_mask) & ~align_mask;
1383
1384 DCHECK(csp.Is(__ StackPointer()));
1385 __ Claim(xreg_to_claim);
1386
1387 // CheckStackGuardState needs the end and start addresses of the input string.
1388 __ Poke(input_end(), 2 * kPointerSize);
1389 __ Add(x5, csp, 2 * kPointerSize);
1390 __ Poke(input_start(), kPointerSize);
1391 __ Add(x4, csp, kPointerSize);
1392
1393 __ Mov(w3, start_offset());
1394 // RegExp code frame pointer.
1395 __ Mov(x2, frame_pointer());
1396 // Code* of self.
1397 __ Mov(x1, Operand(masm_->CodeObject()));
1398
1399 // We need to pass a pointer to the return address as first argument.
1400 // The DirectCEntry stub will place the return address on the stack before
1401 // calling so the stack pointer will point to it.
1402 __ Mov(x0, csp);
1403
1404 ExternalReference check_stack_guard_state =
1405 ExternalReference::re_check_stack_guard_state(isolate());
1406 __ Mov(scratch, check_stack_guard_state);
1407 DirectCEntryStub stub(isolate());
1408 stub.GenerateCall(masm_, scratch);
1409
1410 // The input string may have been moved in memory, we need to reload it.
1411 __ Peek(input_start(), kPointerSize);
1412 __ Peek(input_end(), 2 * kPointerSize);
1413
1414 DCHECK(csp.Is(__ StackPointer()));
1415 __ Drop(xreg_to_claim);
1416
1417 // Reload the Code pointer.
1418 __ Mov(code_pointer(), Operand(masm_->CodeObject()));
1419}
1420
1421void RegExpMacroAssemblerARM64::BranchOrBacktrack(Condition condition,
1422 Label* to) {
1423 if (condition == al) { // Unconditional.
1424 if (to == NULL) {
1425 Backtrack();
1426 return;
1427 }
1428 __ B(to);
1429 return;
1430 }
1431 if (to == NULL) {
1432 to = &backtrack_label_;
1433 }
1434 __ B(condition, to);
1435}
1436
1437void RegExpMacroAssemblerARM64::CompareAndBranchOrBacktrack(Register reg,
1438 int immediate,
1439 Condition condition,
1440 Label* to) {
1441 if ((immediate == 0) && ((condition == eq) || (condition == ne))) {
1442 if (to == NULL) {
1443 to = &backtrack_label_;
1444 }
1445 if (condition == eq) {
1446 __ Cbz(reg, to);
1447 } else {
1448 __ Cbnz(reg, to);
1449 }
1450 } else {
1451 __ Cmp(reg, immediate);
1452 BranchOrBacktrack(condition, to);
1453 }
1454}
1455
1456
1457void RegExpMacroAssemblerARM64::CheckPreemption() {
1458 // Check for preemption.
1459 ExternalReference stack_limit =
1460 ExternalReference::address_of_stack_limit(isolate());
1461 __ Mov(x10, stack_limit);
1462 __ Ldr(x10, MemOperand(x10));
1463 DCHECK(csp.Is(__ StackPointer()));
1464 __ Cmp(csp, x10);
1465 CallIf(&check_preempt_label_, ls);
1466}
1467
1468
1469void RegExpMacroAssemblerARM64::CheckStackLimit() {
1470 ExternalReference stack_limit =
1471 ExternalReference::address_of_regexp_stack_limit(isolate());
1472 __ Mov(x10, stack_limit);
1473 __ Ldr(x10, MemOperand(x10));
1474 __ Cmp(backtrack_stackpointer(), x10);
1475 CallIf(&stack_overflow_label_, ls);
1476}
1477
1478
1479void RegExpMacroAssemblerARM64::Push(Register source) {
1480 DCHECK(source.Is32Bits());
1481 DCHECK(!source.is(backtrack_stackpointer()));
1482 __ Str(source,
1483 MemOperand(backtrack_stackpointer(),
1484 -static_cast<int>(kWRegSize),
1485 PreIndex));
1486}
1487
1488
1489void RegExpMacroAssemblerARM64::Pop(Register target) {
1490 DCHECK(target.Is32Bits());
1491 DCHECK(!target.is(backtrack_stackpointer()));
1492 __ Ldr(target,
1493 MemOperand(backtrack_stackpointer(), kWRegSize, PostIndex));
1494}
1495
1496
1497Register RegExpMacroAssemblerARM64::GetCachedRegister(int register_index) {
1498 DCHECK(register_index < kNumCachedRegisters);
1499 return Register::Create(register_index / 2, kXRegSizeInBits);
1500}
1501
1502
1503Register RegExpMacroAssemblerARM64::GetRegister(int register_index,
1504 Register maybe_result) {
1505 DCHECK(maybe_result.Is32Bits());
1506 DCHECK(register_index >= 0);
1507 if (num_registers_ <= register_index) {
1508 num_registers_ = register_index + 1;
1509 }
1510 Register result;
1511 RegisterState register_state = GetRegisterState(register_index);
1512 switch (register_state) {
1513 case STACKED:
1514 __ Ldr(maybe_result, register_location(register_index));
1515 result = maybe_result;
1516 break;
1517 case CACHED_LSW:
1518 result = GetCachedRegister(register_index).W();
1519 break;
1520 case CACHED_MSW:
1521 __ Lsr(maybe_result.X(), GetCachedRegister(register_index),
1522 kWRegSizeInBits);
1523 result = maybe_result;
1524 break;
1525 default:
1526 UNREACHABLE();
1527 break;
1528 }
1529 DCHECK(result.Is32Bits());
1530 return result;
1531}
1532
1533
1534void RegExpMacroAssemblerARM64::StoreRegister(int register_index,
1535 Register source) {
1536 DCHECK(source.Is32Bits());
1537 DCHECK(register_index >= 0);
1538 if (num_registers_ <= register_index) {
1539 num_registers_ = register_index + 1;
1540 }
1541
1542 Register cached_register;
1543 RegisterState register_state = GetRegisterState(register_index);
1544 switch (register_state) {
1545 case STACKED:
1546 __ Str(source, register_location(register_index));
1547 break;
1548 case CACHED_LSW:
1549 cached_register = GetCachedRegister(register_index);
1550 if (!source.Is(cached_register.W())) {
1551 __ Bfi(cached_register, source.X(), 0, kWRegSizeInBits);
1552 }
1553 break;
1554 case CACHED_MSW:
1555 cached_register = GetCachedRegister(register_index);
1556 __ Bfi(cached_register, source.X(), kWRegSizeInBits, kWRegSizeInBits);
1557 break;
1558 default:
1559 UNREACHABLE();
1560 break;
1561 }
1562}
1563
1564
1565void RegExpMacroAssemblerARM64::CallIf(Label* to, Condition condition) {
1566 Label skip_call;
1567 if (condition != al) __ B(&skip_call, NegateCondition(condition));
1568 __ Bl(to);
1569 __ Bind(&skip_call);
1570}
1571
1572
1573void RegExpMacroAssemblerARM64::RestoreLinkRegister() {
1574 DCHECK(csp.Is(__ StackPointer()));
1575 __ Pop(lr, xzr);
1576 __ Add(lr, lr, Operand(masm_->CodeObject()));
1577}
1578
1579
1580void RegExpMacroAssemblerARM64::SaveLinkRegister() {
1581 DCHECK(csp.Is(__ StackPointer()));
1582 __ Sub(lr, lr, Operand(masm_->CodeObject()));
1583 __ Push(xzr, lr);
1584}
1585
1586
1587MemOperand RegExpMacroAssemblerARM64::register_location(int register_index) {
1588 DCHECK(register_index < (1<<30));
1589 DCHECK(register_index >= kNumCachedRegisters);
1590 if (num_registers_ <= register_index) {
1591 num_registers_ = register_index + 1;
1592 }
1593 register_index -= kNumCachedRegisters;
1594 int offset = kFirstRegisterOnStack - register_index * kWRegSize;
1595 return MemOperand(frame_pointer(), offset);
1596}
1597
1598MemOperand RegExpMacroAssemblerARM64::capture_location(int register_index,
1599 Register scratch) {
1600 DCHECK(register_index < (1<<30));
1601 DCHECK(register_index < num_saved_registers_);
1602 DCHECK(register_index >= kNumCachedRegisters);
1603 DCHECK_EQ(register_index % 2, 0);
1604 register_index -= kNumCachedRegisters;
1605 int offset = kFirstCaptureOnStack - register_index * kWRegSize;
1606 // capture_location is used with Stp instructions to load/store 2 registers.
1607 // The immediate field in the encoding is limited to 7 bits (signed).
1608 if (is_int7(offset)) {
1609 return MemOperand(frame_pointer(), offset);
1610 } else {
1611 __ Add(scratch, frame_pointer(), offset);
1612 return MemOperand(scratch);
1613 }
1614}
1615
1616void RegExpMacroAssemblerARM64::LoadCurrentCharacterUnchecked(int cp_offset,
1617 int characters) {
1618 Register offset = current_input_offset();
1619
1620 // The ldr, str, ldrh, strh instructions can do unaligned accesses, if the CPU
1621 // and the operating system running on the target allow it.
1622 // If unaligned load/stores are not supported then this function must only
1623 // be used to load a single character at a time.
1624
1625 // ARMv8 supports unaligned accesses but V8 or the kernel can decide to
1626 // disable it.
1627 // TODO(pielan): See whether or not we should disable unaligned accesses.
1628 if (!CanReadUnaligned()) {
1629 DCHECK(characters == 1);
1630 }
1631
1632 if (cp_offset != 0) {
1633 if (masm_->emit_debug_code()) {
1634 __ Mov(x10, cp_offset * char_size());
1635 __ Add(x10, x10, Operand(current_input_offset(), SXTW));
1636 __ Cmp(x10, Operand(w10, SXTW));
1637 // The offset needs to fit in a W register.
1638 __ Check(eq, kOffsetOutOfRange);
1639 } else {
1640 __ Add(w10, current_input_offset(), cp_offset * char_size());
1641 }
1642 offset = w10;
1643 }
1644
1645 if (mode_ == LATIN1) {
1646 if (characters == 4) {
1647 __ Ldr(current_character(), MemOperand(input_end(), offset, SXTW));
1648 } else if (characters == 2) {
1649 __ Ldrh(current_character(), MemOperand(input_end(), offset, SXTW));
1650 } else {
1651 DCHECK(characters == 1);
1652 __ Ldrb(current_character(), MemOperand(input_end(), offset, SXTW));
1653 }
1654 } else {
1655 DCHECK(mode_ == UC16);
1656 if (characters == 2) {
1657 __ Ldr(current_character(), MemOperand(input_end(), offset, SXTW));
1658 } else {
1659 DCHECK(characters == 1);
1660 __ Ldrh(current_character(), MemOperand(input_end(), offset, SXTW));
1661 }
1662 }
1663}
1664
1665#endif // V8_INTERPRETED_REGEXP
1666
1667} // namespace internal
1668} // namespace v8
1669
1670#endif // V8_TARGET_ARCH_ARM64