blob: 96d0c252a9b7df47d2cdb604ab50821522212b01 [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"
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000012#include "src/regexp/regexp-macro-assembler.h"
13#include "src/regexp/regexp-stack.h"
14#include "src/unicode.h"
15
16namespace v8 {
17namespace internal {
18
19#ifndef V8_INTERPRETED_REGEXP
20/*
21 * This assembler uses the following register assignment convention:
22 * - w19 : Used to temporarely store a value before a call to C code.
23 * See CheckNotBackReferenceIgnoreCase.
24 * - x20 : Pointer to the current code object (Code*),
25 * it includes the heap object tag.
26 * - w21 : Current position in input, as negative offset from
27 * the end of the string. Please notice that this is
28 * the byte offset, not the character offset!
29 * - w22 : Currently loaded character. Must be loaded using
30 * LoadCurrentCharacter before using any of the dispatch methods.
31 * - x23 : Points to tip of backtrack stack.
32 * - w24 : Position of the first character minus one: non_position_value.
33 * Used to initialize capture registers.
34 * - x25 : Address at the end of the input string: input_end.
35 * Points to byte after last character in input.
36 * - x26 : Address at the start of the input string: input_start.
37 * - w27 : Where to start in the input string.
38 * - x28 : Output array pointer.
39 * - x29/fp : Frame pointer. Used to access arguments, local variables and
40 * RegExp registers.
41 * - x16/x17 : IP registers, used by assembler. Very volatile.
42 * - csp : Points to tip of C stack.
43 *
44 * - x0-x7 : Used as a cache to store 32 bit capture registers. These
45 * registers need to be retained every time a call to C code
46 * is done.
47 *
48 * The remaining registers are free for computations.
49 * Each call to a public method should retain this convention.
50 *
51 * The stack will have the following structure:
52 *
53 * Location Name Description
54 * (as referred to in
55 * the code)
56 *
57 * - fp[104] isolate Address of the current isolate.
58 * - fp[96] return_address Secondary link/return address
59 * used by an exit frame if this is a
60 * native call.
61 * ^^^ csp when called ^^^
62 * - fp[88] lr Return from the RegExp code.
63 * - fp[80] r29 Old frame pointer (CalleeSaved).
64 * - fp[0..72] r19-r28 Backup of CalleeSaved registers.
65 * - fp[-8] direct_call 1 => Direct call from JavaScript code.
66 * 0 => Call through the runtime system.
67 * - fp[-16] stack_base High end of the memory area to use as
68 * the backtracking stack.
69 * - fp[-24] output_size Output may fit multiple sets of matches.
70 * - fp[-32] input Handle containing the input string.
71 * - fp[-40] success_counter
72 * ^^^^^^^^^^^^^ From here and downwards we store 32 bit values ^^^^^^^^^^^^^
73 * - fp[-44] register N Capture registers initialized with
74 * - fp[-48] register N + 1 non_position_value.
75 * ... The first kNumCachedRegisters (N) registers
76 * ... are cached in x0 to x7.
77 * ... Only positions must be stored in the first
78 * - ... num_saved_registers_ registers.
79 * - ...
80 * - register N + num_registers - 1
81 * ^^^^^^^^^ csp ^^^^^^^^^
82 *
83 * The first num_saved_registers_ registers are initialized to point to
84 * "character -1" in the string (i.e., char_size() bytes before the first
85 * character of the string). The remaining registers start out as garbage.
86 *
87 * The data up to the return address must be placed there by the calling
88 * code and the remaining arguments are passed in registers, e.g. by calling the
89 * code entry as cast to a function with the signature:
90 * int (*match)(String* input,
91 * int start_offset,
92 * Address input_start,
93 * Address input_end,
94 * int* output,
95 * int output_size,
96 * Address stack_base,
97 * bool direct_call = false,
98 * Address secondary_return_address, // Only used by native call.
99 * Isolate* isolate)
100 * The call is performed by NativeRegExpMacroAssembler::Execute()
101 * (in regexp-macro-assembler.cc) via the CALL_GENERATED_REGEXP_CODE macro
102 * in arm64/simulator-arm64.h.
103 * When calling as a non-direct call (i.e., from C++ code), the return address
104 * area is overwritten with the LR register by the RegExp code. When doing a
105 * direct call from generated code, the return address is placed there by
106 * the calling code, as in a normal exit frame.
107 */
108
109#define __ ACCESS_MASM(masm_)
110
111RegExpMacroAssemblerARM64::RegExpMacroAssemblerARM64(Isolate* isolate,
112 Zone* zone, Mode mode,
113 int registers_to_save)
114 : NativeRegExpMacroAssembler(isolate, zone),
115 masm_(new MacroAssembler(isolate, NULL, kRegExpCodeSize,
116 CodeObjectRequired::kYes)),
117 mode_(mode),
118 num_registers_(registers_to_save),
119 num_saved_registers_(registers_to_save),
120 entry_label_(),
121 start_label_(),
122 success_label_(),
123 backtrack_label_(),
124 exit_label_() {
125 __ SetStackPointer(csp);
126 DCHECK_EQ(0, registers_to_save % 2);
127 // We can cache at most 16 W registers in x0-x7.
128 STATIC_ASSERT(kNumCachedRegisters <= 16);
129 STATIC_ASSERT((kNumCachedRegisters % 2) == 0);
130 __ B(&entry_label_); // We'll write the entry code later.
131 __ Bind(&start_label_); // And then continue from here.
132}
133
134
135RegExpMacroAssemblerARM64::~RegExpMacroAssemblerARM64() {
136 delete masm_;
137 // Unuse labels in case we throw away the assembler without calling GetCode.
138 entry_label_.Unuse();
139 start_label_.Unuse();
140 success_label_.Unuse();
141 backtrack_label_.Unuse();
142 exit_label_.Unuse();
143 check_preempt_label_.Unuse();
144 stack_overflow_label_.Unuse();
145}
146
147int RegExpMacroAssemblerARM64::stack_limit_slack() {
148 return RegExpStack::kStackLimitSlack;
149}
150
151
152void RegExpMacroAssemblerARM64::AdvanceCurrentPosition(int by) {
153 if (by != 0) {
154 __ Add(current_input_offset(),
155 current_input_offset(), by * char_size());
156 }
157}
158
159
160void RegExpMacroAssemblerARM64::AdvanceRegister(int reg, int by) {
161 DCHECK((reg >= 0) && (reg < num_registers_));
162 if (by != 0) {
163 Register to_advance;
164 RegisterState register_state = GetRegisterState(reg);
165 switch (register_state) {
166 case STACKED:
167 __ Ldr(w10, register_location(reg));
168 __ Add(w10, w10, by);
169 __ Str(w10, register_location(reg));
170 break;
171 case CACHED_LSW:
172 to_advance = GetCachedRegister(reg);
173 __ Add(to_advance, to_advance, by);
174 break;
175 case CACHED_MSW:
176 to_advance = GetCachedRegister(reg);
177 __ Add(to_advance, to_advance,
178 static_cast<int64_t>(by) << kWRegSizeInBits);
179 break;
180 default:
181 UNREACHABLE();
182 break;
183 }
184 }
185}
186
187
188void RegExpMacroAssemblerARM64::Backtrack() {
189 CheckPreemption();
190 Pop(w10);
191 __ Add(x10, code_pointer(), Operand(w10, UXTW));
192 __ Br(x10);
193}
194
195
196void RegExpMacroAssemblerARM64::Bind(Label* label) {
197 __ Bind(label);
198}
199
200
201void RegExpMacroAssemblerARM64::CheckCharacter(uint32_t c, Label* on_equal) {
202 CompareAndBranchOrBacktrack(current_character(), c, eq, on_equal);
203}
204
205
206void RegExpMacroAssemblerARM64::CheckCharacterGT(uc16 limit,
207 Label* on_greater) {
208 CompareAndBranchOrBacktrack(current_character(), limit, hi, on_greater);
209}
210
211
212void RegExpMacroAssemblerARM64::CheckAtStart(Label* on_at_start) {
213 __ Add(w10, current_input_offset(), Operand(-char_size()));
214 __ Cmp(w10, string_start_minus_one());
215 BranchOrBacktrack(eq, on_at_start);
216}
217
218
219void RegExpMacroAssemblerARM64::CheckNotAtStart(int cp_offset,
220 Label* on_not_at_start) {
221 __ Add(w10, current_input_offset(),
222 Operand(-char_size() + cp_offset * char_size()));
223 __ Cmp(w10, string_start_minus_one());
224 BranchOrBacktrack(ne, on_not_at_start);
225}
226
227
228void RegExpMacroAssemblerARM64::CheckCharacterLT(uc16 limit, Label* on_less) {
229 CompareAndBranchOrBacktrack(current_character(), limit, lo, on_less);
230}
231
232
233void RegExpMacroAssemblerARM64::CheckCharacters(Vector<const uc16> str,
234 int cp_offset,
235 Label* on_failure,
236 bool check_end_of_string) {
237 // This method is only ever called from the cctests.
238
239 if (check_end_of_string) {
240 // Is last character of required match inside string.
241 CheckPosition(cp_offset + str.length() - 1, on_failure);
242 }
243
244 Register characters_address = x11;
245
246 __ Add(characters_address,
247 input_end(),
248 Operand(current_input_offset(), SXTW));
249 if (cp_offset != 0) {
250 __ Add(characters_address, characters_address, cp_offset * char_size());
251 }
252
253 for (int i = 0; i < str.length(); i++) {
254 if (mode_ == LATIN1) {
255 __ Ldrb(w10, MemOperand(characters_address, 1, PostIndex));
256 DCHECK(str[i] <= String::kMaxOneByteCharCode);
257 } else {
258 __ Ldrh(w10, MemOperand(characters_address, 2, PostIndex));
259 }
260 CompareAndBranchOrBacktrack(w10, str[i], ne, on_failure);
261 }
262}
263
264
265void RegExpMacroAssemblerARM64::CheckGreedyLoop(Label* on_equal) {
266 __ Ldr(w10, MemOperand(backtrack_stackpointer()));
267 __ Cmp(current_input_offset(), w10);
268 __ Cset(x11, eq);
269 __ Add(backtrack_stackpointer(),
270 backtrack_stackpointer(), Operand(x11, LSL, kWRegSizeLog2));
271 BranchOrBacktrack(eq, on_equal);
272}
273
274
275void RegExpMacroAssemblerARM64::CheckNotBackReferenceIgnoreCase(
Ben Murdoch097c5b22016-05-18 11:27:45 +0100276 int start_reg, bool read_backward, bool unicode, Label* on_no_match) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000277 Label fallthrough;
278
279 Register capture_start_offset = w10;
280 // Save the capture length in a callee-saved register so it will
281 // be preserved if we call a C helper.
282 Register capture_length = w19;
283 DCHECK(kCalleeSaved.IncludesAliasOf(capture_length));
284
285 // Find length of back-referenced capture.
286 DCHECK((start_reg % 2) == 0);
287 if (start_reg < kNumCachedRegisters) {
288 __ Mov(capture_start_offset.X(), GetCachedRegister(start_reg));
289 __ Lsr(x11, GetCachedRegister(start_reg), kWRegSizeInBits);
290 } else {
291 __ Ldp(w11, capture_start_offset, capture_location(start_reg, x10));
292 }
293 __ Sub(capture_length, w11, capture_start_offset); // Length to check.
294
295 // At this point, the capture registers are either both set or both cleared.
296 // If the capture length is zero, then the capture is either empty or cleared.
297 // Fall through in both cases.
298 __ CompareAndBranch(capture_length, Operand(0), eq, &fallthrough);
299
300 // Check that there are enough characters left in the input.
301 if (read_backward) {
302 __ Add(w12, string_start_minus_one(), capture_length);
303 __ Cmp(current_input_offset(), w12);
304 BranchOrBacktrack(le, on_no_match);
305 } else {
306 __ Cmn(capture_length, current_input_offset());
307 BranchOrBacktrack(gt, on_no_match);
308 }
309
310 if (mode_ == LATIN1) {
311 Label success;
312 Label fail;
313 Label loop_check;
314
315 Register capture_start_address = x12;
316 Register capture_end_addresss = x13;
317 Register current_position_address = x14;
318
319 __ Add(capture_start_address,
320 input_end(),
321 Operand(capture_start_offset, SXTW));
322 __ Add(capture_end_addresss,
323 capture_start_address,
324 Operand(capture_length, SXTW));
325 __ Add(current_position_address,
326 input_end(),
327 Operand(current_input_offset(), SXTW));
328 if (read_backward) {
329 // Offset by length when matching backwards.
330 __ Sub(current_position_address, current_position_address,
331 Operand(capture_length, SXTW));
332 }
333
334 Label loop;
335 __ Bind(&loop);
336 __ Ldrb(w10, MemOperand(capture_start_address, 1, PostIndex));
337 __ Ldrb(w11, MemOperand(current_position_address, 1, PostIndex));
338 __ Cmp(w10, w11);
339 __ B(eq, &loop_check);
340
341 // Mismatch, try case-insensitive match (converting letters to lower-case).
342 __ Orr(w10, w10, 0x20); // Convert capture character to lower-case.
343 __ Orr(w11, w11, 0x20); // Also convert input character.
344 __ Cmp(w11, w10);
345 __ B(ne, &fail);
346 __ Sub(w10, w10, 'a');
347 __ Cmp(w10, 'z' - 'a'); // Is w10 a lowercase letter?
348 __ B(ls, &loop_check); // In range 'a'-'z'.
349 // Latin-1: Check for values in range [224,254] but not 247.
350 __ Sub(w10, w10, 224 - 'a');
351 __ Cmp(w10, 254 - 224);
352 __ Ccmp(w10, 247 - 224, ZFlag, ls); // Check for 247.
353 __ B(eq, &fail); // Weren't Latin-1 letters.
354
355 __ Bind(&loop_check);
356 __ Cmp(capture_start_address, capture_end_addresss);
357 __ B(lt, &loop);
358 __ B(&success);
359
360 __ Bind(&fail);
361 BranchOrBacktrack(al, on_no_match);
362
363 __ Bind(&success);
364 // Compute new value of character position after the matched part.
365 __ Sub(current_input_offset().X(), current_position_address, input_end());
366 if (read_backward) {
367 __ Sub(current_input_offset().X(), current_input_offset().X(),
368 Operand(capture_length, SXTW));
369 }
370 if (masm_->emit_debug_code()) {
371 __ Cmp(current_input_offset().X(), Operand(current_input_offset(), SXTW));
372 __ Ccmp(current_input_offset(), 0, NoFlag, eq);
373 // The current input offset should be <= 0, and fit in a W register.
374 __ Check(le, kOffsetOutOfRange);
375 }
376 } else {
377 DCHECK(mode_ == UC16);
378 int argument_count = 4;
379
380 // The cached registers need to be retained.
381 CPURegList cached_registers(CPURegister::kRegister, kXRegSizeInBits, 0, 7);
382 DCHECK((cached_registers.Count() * 2) == kNumCachedRegisters);
383 __ PushCPURegList(cached_registers);
384
385 // Put arguments into arguments registers.
386 // Parameters are
387 // x0: Address byte_offset1 - Address captured substring's start.
388 // x1: Address byte_offset2 - Address of current character position.
389 // w2: size_t byte_length - length of capture in bytes(!)
Ben Murdoch097c5b22016-05-18 11:27:45 +0100390 // x3: Isolate* isolate or 0 if unicode flag
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000391
392 // Address of start of capture.
393 __ Add(x0, input_end(), Operand(capture_start_offset, SXTW));
394 // Length of capture.
395 __ Mov(w2, capture_length);
396 // Address of current input position.
397 __ Add(x1, input_end(), Operand(current_input_offset(), SXTW));
398 if (read_backward) {
399 __ Sub(x1, x1, Operand(capture_length, SXTW));
400 }
401 // Isolate.
Ben Murdoch097c5b22016-05-18 11:27:45 +0100402#ifdef V8_I18N_SUPPORT
403 if (unicode) {
404 __ Mov(x3, Operand(0));
405 } else // NOLINT
406#endif // V8_I18N_SUPPORT
407 {
408 __ Mov(x3, ExternalReference::isolate_address(isolate()));
409 }
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000410
411 {
412 AllowExternalCallThatCantCauseGC scope(masm_);
413 ExternalReference function =
414 ExternalReference::re_case_insensitive_compare_uc16(isolate());
415 __ CallCFunction(function, argument_count);
416 }
417
418 // Check if function returned non-zero for success or zero for failure.
419 // x0 is one of the registers used as a cache so it must be tested before
420 // the cache is restored.
421 __ Cmp(x0, 0);
422 __ PopCPURegList(cached_registers);
423 BranchOrBacktrack(eq, on_no_match);
424
425 // On success, advance position by length of capture.
426 if (read_backward) {
427 __ Sub(current_input_offset(), current_input_offset(), capture_length);
428 } else {
429 __ Add(current_input_offset(), current_input_offset(), capture_length);
430 }
431 }
432
433 __ Bind(&fallthrough);
434}
435
436void RegExpMacroAssemblerARM64::CheckNotBackReference(int start_reg,
437 bool read_backward,
438 Label* on_no_match) {
439 Label fallthrough;
440
441 Register capture_start_address = x12;
442 Register capture_end_address = x13;
443 Register current_position_address = x14;
444 Register capture_length = w15;
445
446 // Find length of back-referenced capture.
447 DCHECK((start_reg % 2) == 0);
448 if (start_reg < kNumCachedRegisters) {
449 __ Mov(x10, GetCachedRegister(start_reg));
450 __ Lsr(x11, GetCachedRegister(start_reg), kWRegSizeInBits);
451 } else {
452 __ Ldp(w11, w10, capture_location(start_reg, x10));
453 }
454 __ Sub(capture_length, w11, w10); // Length to check.
455
456 // At this point, the capture registers are either both set or both cleared.
457 // If the capture length is zero, then the capture is either empty or cleared.
458 // Fall through in both cases.
459 __ CompareAndBranch(capture_length, Operand(0), eq, &fallthrough);
460
461 // Check that there are enough characters left in the input.
462 if (read_backward) {
463 __ Add(w12, string_start_minus_one(), capture_length);
464 __ Cmp(current_input_offset(), w12);
465 BranchOrBacktrack(le, on_no_match);
466 } else {
467 __ Cmn(capture_length, current_input_offset());
468 BranchOrBacktrack(gt, on_no_match);
469 }
470
471 // Compute pointers to match string and capture string
472 __ Add(capture_start_address, input_end(), Operand(w10, SXTW));
473 __ Add(capture_end_address,
474 capture_start_address,
475 Operand(capture_length, SXTW));
476 __ Add(current_position_address,
477 input_end(),
478 Operand(current_input_offset(), SXTW));
479 if (read_backward) {
480 // Offset by length when matching backwards.
481 __ Sub(current_position_address, current_position_address,
482 Operand(capture_length, SXTW));
483 }
484
485 Label loop;
486 __ Bind(&loop);
487 if (mode_ == LATIN1) {
488 __ Ldrb(w10, MemOperand(capture_start_address, 1, PostIndex));
489 __ Ldrb(w11, MemOperand(current_position_address, 1, PostIndex));
490 } else {
491 DCHECK(mode_ == UC16);
492 __ Ldrh(w10, MemOperand(capture_start_address, 2, PostIndex));
493 __ Ldrh(w11, MemOperand(current_position_address, 2, PostIndex));
494 }
495 __ Cmp(w10, w11);
496 BranchOrBacktrack(ne, on_no_match);
497 __ Cmp(capture_start_address, capture_end_address);
498 __ B(lt, &loop);
499
500 // Move current character position to position after match.
501 __ Sub(current_input_offset().X(), current_position_address, input_end());
502 if (read_backward) {
503 __ Sub(current_input_offset().X(), current_input_offset().X(),
504 Operand(capture_length, SXTW));
505 }
506
507 if (masm_->emit_debug_code()) {
508 __ Cmp(current_input_offset().X(), Operand(current_input_offset(), SXTW));
509 __ Ccmp(current_input_offset(), 0, NoFlag, eq);
510 // The current input offset should be <= 0, and fit in a W register.
511 __ Check(le, kOffsetOutOfRange);
512 }
513 __ Bind(&fallthrough);
514}
515
516
517void RegExpMacroAssemblerARM64::CheckNotCharacter(unsigned c,
518 Label* on_not_equal) {
519 CompareAndBranchOrBacktrack(current_character(), c, ne, on_not_equal);
520}
521
522
523void RegExpMacroAssemblerARM64::CheckCharacterAfterAnd(uint32_t c,
524 uint32_t mask,
525 Label* on_equal) {
526 __ And(w10, current_character(), mask);
527 CompareAndBranchOrBacktrack(w10, c, eq, on_equal);
528}
529
530
531void RegExpMacroAssemblerARM64::CheckNotCharacterAfterAnd(unsigned c,
532 unsigned mask,
533 Label* on_not_equal) {
534 __ And(w10, current_character(), mask);
535 CompareAndBranchOrBacktrack(w10, c, ne, on_not_equal);
536}
537
538
539void RegExpMacroAssemblerARM64::CheckNotCharacterAfterMinusAnd(
540 uc16 c,
541 uc16 minus,
542 uc16 mask,
543 Label* on_not_equal) {
544 DCHECK(minus < String::kMaxUtf16CodeUnit);
545 __ Sub(w10, current_character(), minus);
546 __ And(w10, w10, mask);
547 CompareAndBranchOrBacktrack(w10, c, ne, on_not_equal);
548}
549
550
551void RegExpMacroAssemblerARM64::CheckCharacterInRange(
552 uc16 from,
553 uc16 to,
554 Label* on_in_range) {
555 __ Sub(w10, current_character(), from);
556 // Unsigned lower-or-same condition.
557 CompareAndBranchOrBacktrack(w10, to - from, ls, on_in_range);
558}
559
560
561void RegExpMacroAssemblerARM64::CheckCharacterNotInRange(
562 uc16 from,
563 uc16 to,
564 Label* on_not_in_range) {
565 __ Sub(w10, current_character(), from);
566 // Unsigned higher condition.
567 CompareAndBranchOrBacktrack(w10, to - from, hi, on_not_in_range);
568}
569
570
571void RegExpMacroAssemblerARM64::CheckBitInTable(
572 Handle<ByteArray> table,
573 Label* on_bit_set) {
574 __ Mov(x11, Operand(table));
575 if ((mode_ != LATIN1) || (kTableMask != String::kMaxOneByteCharCode)) {
576 __ And(w10, current_character(), kTableMask);
577 __ Add(w10, w10, ByteArray::kHeaderSize - kHeapObjectTag);
578 } else {
579 __ Add(w10, current_character(), ByteArray::kHeaderSize - kHeapObjectTag);
580 }
581 __ Ldrb(w11, MemOperand(x11, w10, UXTW));
582 CompareAndBranchOrBacktrack(w11, 0, ne, on_bit_set);
583}
584
585
586bool RegExpMacroAssemblerARM64::CheckSpecialCharacterClass(uc16 type,
587 Label* on_no_match) {
588 // Range checks (c in min..max) are generally implemented by an unsigned
589 // (c - min) <= (max - min) check
590 switch (type) {
591 case 's':
592 // Match space-characters
593 if (mode_ == LATIN1) {
594 // One byte space characters are '\t'..'\r', ' ' and \u00a0.
595 Label success;
596 // Check for ' ' or 0x00a0.
597 __ Cmp(current_character(), ' ');
598 __ Ccmp(current_character(), 0x00a0, ZFlag, ne);
599 __ B(eq, &success);
600 // Check range 0x09..0x0d.
601 __ Sub(w10, current_character(), '\t');
602 CompareAndBranchOrBacktrack(w10, '\r' - '\t', hi, on_no_match);
603 __ Bind(&success);
604 return true;
605 }
606 return false;
607 case 'S':
608 // The emitted code for generic character classes is good enough.
609 return false;
610 case 'd':
611 // Match ASCII digits ('0'..'9').
612 __ Sub(w10, current_character(), '0');
613 CompareAndBranchOrBacktrack(w10, '9' - '0', hi, on_no_match);
614 return true;
615 case 'D':
616 // Match ASCII non-digits.
617 __ Sub(w10, current_character(), '0');
618 CompareAndBranchOrBacktrack(w10, '9' - '0', ls, on_no_match);
619 return true;
620 case '.': {
621 // Match non-newlines (not 0x0a('\n'), 0x0d('\r'), 0x2028 and 0x2029)
622 // Here we emit the conditional branch only once at the end to make branch
623 // prediction more efficient, even though we could branch out of here
624 // as soon as a character matches.
625 __ Cmp(current_character(), 0x0a);
626 __ Ccmp(current_character(), 0x0d, ZFlag, ne);
627 if (mode_ == UC16) {
628 __ Sub(w10, current_character(), 0x2028);
629 // If the Z flag was set we clear the flags to force a branch.
630 __ Ccmp(w10, 0x2029 - 0x2028, NoFlag, ne);
631 // ls -> !((C==1) && (Z==0))
632 BranchOrBacktrack(ls, on_no_match);
633 } else {
634 BranchOrBacktrack(eq, on_no_match);
635 }
636 return true;
637 }
638 case 'n': {
639 // Match newlines (0x0a('\n'), 0x0d('\r'), 0x2028 and 0x2029)
640 // We have to check all 4 newline characters before emitting
641 // the conditional branch.
642 __ Cmp(current_character(), 0x0a);
643 __ Ccmp(current_character(), 0x0d, ZFlag, ne);
644 if (mode_ == UC16) {
645 __ Sub(w10, current_character(), 0x2028);
646 // If the Z flag was set we clear the flags to force a fall-through.
647 __ Ccmp(w10, 0x2029 - 0x2028, NoFlag, ne);
648 // hi -> (C==1) && (Z==0)
649 BranchOrBacktrack(hi, on_no_match);
650 } else {
651 BranchOrBacktrack(ne, on_no_match);
652 }
653 return true;
654 }
655 case 'w': {
656 if (mode_ != LATIN1) {
657 // Table is 256 entries, so all Latin1 characters can be tested.
658 CompareAndBranchOrBacktrack(current_character(), 'z', hi, on_no_match);
659 }
660 ExternalReference map = ExternalReference::re_word_character_map();
661 __ Mov(x10, map);
662 __ Ldrb(w10, MemOperand(x10, current_character(), UXTW));
663 CompareAndBranchOrBacktrack(w10, 0, eq, on_no_match);
664 return true;
665 }
666 case 'W': {
667 Label done;
668 if (mode_ != LATIN1) {
669 // Table is 256 entries, so all Latin1 characters can be tested.
670 __ Cmp(current_character(), 'z');
671 __ B(hi, &done);
672 }
673 ExternalReference map = ExternalReference::re_word_character_map();
674 __ Mov(x10, map);
675 __ Ldrb(w10, MemOperand(x10, current_character(), UXTW));
676 CompareAndBranchOrBacktrack(w10, 0, ne, on_no_match);
677 __ Bind(&done);
678 return true;
679 }
680 case '*':
681 // Match any character.
682 return true;
683 // No custom implementation (yet): s(UC16), S(UC16).
684 default:
685 return false;
686 }
687}
688
689
690void RegExpMacroAssemblerARM64::Fail() {
691 __ Mov(w0, FAILURE);
692 __ B(&exit_label_);
693}
694
695
696Handle<HeapObject> RegExpMacroAssemblerARM64::GetCode(Handle<String> source) {
697 Label return_w0;
698 // Finalize code - write the entry point code now we know how many
699 // registers we need.
700
701 // Entry code:
702 __ Bind(&entry_label_);
703
704 // Arguments on entry:
705 // x0: String* input
706 // x1: int start_offset
707 // x2: byte* input_start
708 // x3: byte* input_end
709 // x4: int* output array
710 // x5: int output array size
711 // x6: Address stack_base
712 // x7: int direct_call
713
714 // The stack pointer should be csp on entry.
715 // csp[8]: address of the current isolate
716 // csp[0]: secondary link/return address used by native call
717
718 // Tell the system that we have a stack frame. Because the type is MANUAL, no
719 // code is generated.
720 FrameScope scope(masm_, StackFrame::MANUAL);
721
722 // Push registers on the stack, only push the argument registers that we need.
723 CPURegList argument_registers(x0, x5, x6, x7);
724
725 CPURegList registers_to_retain = kCalleeSaved;
726 DCHECK(kCalleeSaved.Count() == 11);
727 registers_to_retain.Combine(lr);
728
729 DCHECK(csp.Is(__ StackPointer()));
730 __ PushCPURegList(registers_to_retain);
731 __ PushCPURegList(argument_registers);
732
733 // Set frame pointer in place.
734 __ Add(frame_pointer(), csp, argument_registers.Count() * kPointerSize);
735
736 // Initialize callee-saved registers.
737 __ Mov(start_offset(), w1);
738 __ Mov(input_start(), x2);
739 __ Mov(input_end(), x3);
740 __ Mov(output_array(), x4);
741
742 // Set the number of registers we will need to allocate, that is:
743 // - success_counter (X register)
744 // - (num_registers_ - kNumCachedRegisters) (W registers)
745 int num_wreg_to_allocate = num_registers_ - kNumCachedRegisters;
746 // Do not allocate registers on the stack if they can all be cached.
747 if (num_wreg_to_allocate < 0) { num_wreg_to_allocate = 0; }
748 // Make room for the success_counter.
749 num_wreg_to_allocate += 2;
750
751 // Make sure the stack alignment will be respected.
752 int alignment = masm_->ActivationFrameAlignment();
753 DCHECK_EQ(alignment % 16, 0);
754 int align_mask = (alignment / kWRegSize) - 1;
755 num_wreg_to_allocate = (num_wreg_to_allocate + align_mask) & ~align_mask;
756
757 // Check if we have space on the stack.
758 Label stack_limit_hit;
759 Label stack_ok;
760
761 ExternalReference stack_limit =
762 ExternalReference::address_of_stack_limit(isolate());
763 __ Mov(x10, stack_limit);
764 __ Ldr(x10, MemOperand(x10));
765 __ Subs(x10, csp, x10);
766
767 // Handle it if the stack pointer is already below the stack limit.
768 __ B(ls, &stack_limit_hit);
769
770 // Check if there is room for the variable number of registers above
771 // the stack limit.
772 __ Cmp(x10, num_wreg_to_allocate * kWRegSize);
773 __ B(hs, &stack_ok);
774
775 // Exit with OutOfMemory exception. There is not enough space on the stack
776 // for our working registers.
777 __ Mov(w0, EXCEPTION);
778 __ B(&return_w0);
779
780 __ Bind(&stack_limit_hit);
781 CallCheckStackGuardState(x10);
782 // If returned value is non-zero, we exit with the returned value as result.
783 __ Cbnz(w0, &return_w0);
784
785 __ Bind(&stack_ok);
786
787 // Allocate space on stack.
788 __ Claim(num_wreg_to_allocate, kWRegSize);
789
790 // Initialize success_counter with 0.
791 __ Str(wzr, MemOperand(frame_pointer(), kSuccessCounter));
792
793 // Find negative length (offset of start relative to end).
794 __ Sub(x10, input_start(), input_end());
795 if (masm_->emit_debug_code()) {
796 // Check that the input string length is < 2^30.
797 __ Neg(x11, x10);
798 __ Cmp(x11, (1<<30) - 1);
799 __ Check(ls, kInputStringTooLong);
800 }
801 __ Mov(current_input_offset(), w10);
802
803 // The non-position value is used as a clearing value for the
804 // capture registers, it corresponds to the position of the first character
805 // minus one.
806 __ Sub(string_start_minus_one(), current_input_offset(), char_size());
807 __ Sub(string_start_minus_one(), string_start_minus_one(),
808 Operand(start_offset(), LSL, (mode_ == UC16) ? 1 : 0));
809 // We can store this value twice in an X register for initializing
810 // on-stack registers later.
811 __ Orr(twice_non_position_value(), string_start_minus_one().X(),
812 Operand(string_start_minus_one().X(), LSL, kWRegSizeInBits));
813
814 // Initialize code pointer register.
815 __ Mov(code_pointer(), Operand(masm_->CodeObject()));
816
817 Label load_char_start_regexp, start_regexp;
818 // Load newline if index is at start, previous character otherwise.
819 __ Cbnz(start_offset(), &load_char_start_regexp);
820 __ Mov(current_character(), '\n');
821 __ B(&start_regexp);
822
823 // Global regexp restarts matching here.
824 __ Bind(&load_char_start_regexp);
825 // Load previous char as initial value of current character register.
826 LoadCurrentCharacterUnchecked(-1, 1);
827 __ Bind(&start_regexp);
828 // Initialize on-stack registers.
829 if (num_saved_registers_ > 0) {
830 ClearRegisters(0, num_saved_registers_ - 1);
831 }
832
833 // Initialize backtrack stack pointer.
834 __ Ldr(backtrack_stackpointer(), MemOperand(frame_pointer(), kStackBase));
835
836 // Execute
837 __ B(&start_label_);
838
839 if (backtrack_label_.is_linked()) {
840 __ Bind(&backtrack_label_);
841 Backtrack();
842 }
843
844 if (success_label_.is_linked()) {
845 Register first_capture_start = w15;
846
847 // Save captures when successful.
848 __ Bind(&success_label_);
849
850 if (num_saved_registers_ > 0) {
851 // V8 expects the output to be an int32_t array.
852 Register capture_start = w12;
853 Register capture_end = w13;
854 Register input_length = w14;
855
856 // Copy captures to output.
857
858 // Get string length.
859 __ Sub(x10, input_end(), input_start());
860 if (masm_->emit_debug_code()) {
861 // Check that the input string length is < 2^30.
862 __ Cmp(x10, (1<<30) - 1);
863 __ Check(ls, kInputStringTooLong);
864 }
865 // input_start has a start_offset offset on entry. We need to include
866 // it when computing the length of the whole string.
867 if (mode_ == UC16) {
868 __ Add(input_length, start_offset(), Operand(w10, LSR, 1));
869 } else {
870 __ Add(input_length, start_offset(), w10);
871 }
872
873 // Copy the results to the output array from the cached registers first.
874 for (int i = 0;
875 (i < num_saved_registers_) && (i < kNumCachedRegisters);
876 i += 2) {
877 __ Mov(capture_start.X(), GetCachedRegister(i));
878 __ Lsr(capture_end.X(), capture_start.X(), kWRegSizeInBits);
879 if ((i == 0) && global_with_zero_length_check()) {
880 // Keep capture start for the zero-length check later.
881 __ Mov(first_capture_start, capture_start);
882 }
883 // Offsets need to be relative to the start of the string.
884 if (mode_ == UC16) {
885 __ Add(capture_start, input_length, Operand(capture_start, ASR, 1));
886 __ Add(capture_end, input_length, Operand(capture_end, ASR, 1));
887 } else {
888 __ Add(capture_start, input_length, capture_start);
889 __ Add(capture_end, input_length, capture_end);
890 }
891 // The output pointer advances for a possible global match.
892 __ Stp(capture_start,
893 capture_end,
894 MemOperand(output_array(), kPointerSize, PostIndex));
895 }
896
897 // Only carry on if there are more than kNumCachedRegisters capture
898 // registers.
899 int num_registers_left_on_stack =
900 num_saved_registers_ - kNumCachedRegisters;
901 if (num_registers_left_on_stack > 0) {
902 Register base = x10;
903 // There are always an even number of capture registers. A couple of
904 // registers determine one match with two offsets.
905 DCHECK_EQ(0, num_registers_left_on_stack % 2);
906 __ Add(base, frame_pointer(), kFirstCaptureOnStack);
907
908 // We can unroll the loop here, we should not unroll for less than 2
909 // registers.
910 STATIC_ASSERT(kNumRegistersToUnroll > 2);
911 if (num_registers_left_on_stack <= kNumRegistersToUnroll) {
912 for (int i = 0; i < num_registers_left_on_stack / 2; i++) {
913 __ Ldp(capture_end,
914 capture_start,
915 MemOperand(base, -kPointerSize, PostIndex));
916 if ((i == 0) && global_with_zero_length_check()) {
917 // Keep capture start for the zero-length check later.
918 __ Mov(first_capture_start, capture_start);
919 }
920 // Offsets need to be relative to the start of the string.
921 if (mode_ == UC16) {
922 __ Add(capture_start,
923 input_length,
924 Operand(capture_start, ASR, 1));
925 __ Add(capture_end, input_length, Operand(capture_end, ASR, 1));
926 } else {
927 __ Add(capture_start, input_length, capture_start);
928 __ Add(capture_end, input_length, capture_end);
929 }
930 // The output pointer advances for a possible global match.
931 __ Stp(capture_start,
932 capture_end,
933 MemOperand(output_array(), kPointerSize, PostIndex));
934 }
935 } else {
936 Label loop, start;
937 __ Mov(x11, num_registers_left_on_stack);
938
939 __ Ldp(capture_end,
940 capture_start,
941 MemOperand(base, -kPointerSize, PostIndex));
942 if (global_with_zero_length_check()) {
943 __ Mov(first_capture_start, capture_start);
944 }
945 __ B(&start);
946
947 __ Bind(&loop);
948 __ Ldp(capture_end,
949 capture_start,
950 MemOperand(base, -kPointerSize, PostIndex));
951 __ Bind(&start);
952 if (mode_ == UC16) {
953 __ Add(capture_start, input_length, Operand(capture_start, ASR, 1));
954 __ Add(capture_end, input_length, Operand(capture_end, ASR, 1));
955 } else {
956 __ Add(capture_start, input_length, capture_start);
957 __ Add(capture_end, input_length, capture_end);
958 }
959 // The output pointer advances for a possible global match.
960 __ Stp(capture_start,
961 capture_end,
962 MemOperand(output_array(), kPointerSize, PostIndex));
963 __ Sub(x11, x11, 2);
964 __ Cbnz(x11, &loop);
965 }
966 }
967 }
968
969 if (global()) {
970 Register success_counter = w0;
971 Register output_size = x10;
972 // Restart matching if the regular expression is flagged as global.
973
974 // Increment success counter.
975 __ Ldr(success_counter, MemOperand(frame_pointer(), kSuccessCounter));
976 __ Add(success_counter, success_counter, 1);
977 __ Str(success_counter, MemOperand(frame_pointer(), kSuccessCounter));
978
979 // Capture results have been stored, so the number of remaining global
980 // output registers is reduced by the number of stored captures.
981 __ Ldr(output_size, MemOperand(frame_pointer(), kOutputSize));
982 __ Sub(output_size, output_size, num_saved_registers_);
983 // Check whether we have enough room for another set of capture results.
984 __ Cmp(output_size, num_saved_registers_);
985 __ B(lt, &return_w0);
986
987 // The output pointer is already set to the next field in the output
988 // array.
989 // Update output size on the frame before we restart matching.
990 __ Str(output_size, MemOperand(frame_pointer(), kOutputSize));
991
992 if (global_with_zero_length_check()) {
993 // Special case for zero-length matches.
994 __ Cmp(current_input_offset(), first_capture_start);
995 // Not a zero-length match, restart.
996 __ B(ne, &load_char_start_regexp);
997 // Offset from the end is zero if we already reached the end.
998 __ Cbz(current_input_offset(), &return_w0);
999 // Advance current position after a zero-length match.
Ben Murdoch097c5b22016-05-18 11:27:45 +01001000 Label advance;
1001 __ bind(&advance);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001002 __ Add(current_input_offset(),
1003 current_input_offset(),
1004 Operand((mode_ == UC16) ? 2 : 1));
Ben Murdoch097c5b22016-05-18 11:27:45 +01001005 if (global_unicode()) CheckNotInSurrogatePair(0, &advance);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001006 }
1007
1008 __ B(&load_char_start_regexp);
1009 } else {
1010 __ Mov(w0, SUCCESS);
1011 }
1012 }
1013
1014 if (exit_label_.is_linked()) {
1015 // Exit and return w0
1016 __ Bind(&exit_label_);
1017 if (global()) {
1018 __ Ldr(w0, MemOperand(frame_pointer(), kSuccessCounter));
1019 }
1020 }
1021
1022 __ Bind(&return_w0);
1023
1024 // Set stack pointer back to first register to retain
1025 DCHECK(csp.Is(__ StackPointer()));
1026 __ Mov(csp, fp);
1027 __ AssertStackConsistency();
1028
1029 // Restore registers.
1030 __ PopCPURegList(registers_to_retain);
1031
1032 __ Ret();
1033
1034 Label exit_with_exception;
1035 // Registers x0 to x7 are used to store the first captures, they need to be
1036 // retained over calls to C++ code.
1037 CPURegList cached_registers(CPURegister::kRegister, kXRegSizeInBits, 0, 7);
1038 DCHECK((cached_registers.Count() * 2) == kNumCachedRegisters);
1039
1040 if (check_preempt_label_.is_linked()) {
1041 __ Bind(&check_preempt_label_);
1042 SaveLinkRegister();
1043 // The cached registers need to be retained.
1044 __ PushCPURegList(cached_registers);
1045 CallCheckStackGuardState(x10);
1046 // Returning from the regexp code restores the stack (csp <- fp)
1047 // so we don't need to drop the link register from it before exiting.
1048 __ Cbnz(w0, &return_w0);
1049 // Reset the cached registers.
1050 __ PopCPURegList(cached_registers);
1051 RestoreLinkRegister();
1052 __ Ret();
1053 }
1054
1055 if (stack_overflow_label_.is_linked()) {
1056 __ Bind(&stack_overflow_label_);
1057 SaveLinkRegister();
1058 // The cached registers need to be retained.
1059 __ PushCPURegList(cached_registers);
1060 // Call GrowStack(backtrack_stackpointer(), &stack_base)
1061 __ Mov(x2, ExternalReference::isolate_address(isolate()));
1062 __ Add(x1, frame_pointer(), kStackBase);
1063 __ Mov(x0, backtrack_stackpointer());
1064 ExternalReference grow_stack =
1065 ExternalReference::re_grow_stack(isolate());
1066 __ CallCFunction(grow_stack, 3);
1067 // If return NULL, we have failed to grow the stack, and
1068 // must exit with a stack-overflow exception.
1069 // Returning from the regexp code restores the stack (csp <- fp)
1070 // so we don't need to drop the link register from it before exiting.
1071 __ Cbz(w0, &exit_with_exception);
1072 // Otherwise use return value as new stack pointer.
1073 __ Mov(backtrack_stackpointer(), x0);
1074 // Reset the cached registers.
1075 __ PopCPURegList(cached_registers);
1076 RestoreLinkRegister();
1077 __ Ret();
1078 }
1079
1080 if (exit_with_exception.is_linked()) {
1081 __ Bind(&exit_with_exception);
1082 __ Mov(w0, EXCEPTION);
1083 __ B(&return_w0);
1084 }
1085
1086 CodeDesc code_desc;
1087 masm_->GetCode(&code_desc);
1088 Handle<Code> code = isolate()->factory()->NewCode(
1089 code_desc, Code::ComputeFlags(Code::REGEXP), masm_->CodeObject());
Ben Murdochda12d292016-06-02 14:46:10 +01001090 PROFILE(masm_->isolate(),
1091 RegExpCodeCreateEvent(AbstractCode::cast(*code), *source));
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001092 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