v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
regexp-macro-assembler-arm64.cc
Go to the documentation of this file.
1// 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
8
11#include "src/logging/log.h"
16#include "src/strings/unicode.h"
17
18namespace v8 {
19namespace internal {
20
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 InstructionStream object,
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 * - sp : 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
56 * in the code)
57 *
58 * - fp[104] Address regexp Address of the JSRegExp object. Unused in
59 * native code, passed to match signature of
60 * the interpreter.
61 * - fp[96] isolate Address of the current isolate.
62 * ^^^^^^^^^ sp when called ^^^^^^^^^
63 * - fp[16..88] r19-r28 Backup of CalleeSaved registers.
64 * - fp[8] lr Return from the RegExp code.
65 * - fp[0] fp Old frame pointer.
66 * ^^^^^^^^^ fp ^^^^^^^^^
67 * - fp[-8] frame marker
68 * - fp[-16] isolate
69 * - fp[-24] direct_call 1 => Direct call from JavaScript code.
70 * 0 => Call through the runtime system.
71 * - fp[-32] output_size Output may fit multiple sets of matches.
72 * - fp[-40] input Handle containing the input string.
73 * - fp[-48] success_counter
74 * ^^^^^^^^^^^^^ From here and downwards we store 32 bit values ^^^^^^^^^^^^^
75 * - fp[-56] register N Capture registers initialized with
76 * - fp[-60] register N + 1 non_position_value.
77 * ... The first kNumCachedRegisters (N) registers
78 * ... are cached in x0 to x7.
79 * ... Only positions must be stored in the first
80 * - ... num_saved_registers_ registers.
81 * - ...
82 * - register N + num_registers - 1
83 * ^^^^^^^^^ sp ^^^^^^^^^
84 *
85 * The first num_saved_registers_ registers are initialized to point to
86 * "character -1" in the string (i.e., char_size() bytes before the first
87 * character of the string). The remaining registers start out as garbage.
88 *
89 * The data up to the return address must be placed there by the calling
90 * code and the remaining arguments are passed in registers, e.g. by calling the
91 * code entry as cast to a function with the signature:
92 * int (*match)(String input_string,
93 * int start_index,
94 * Address start,
95 * Address end,
96 * int* capture_output_array,
97 * int num_capture_registers,
98 * bool direct_call = false,
99 * Isolate* isolate,
100 * Address regexp);
101 * The call is performed by NativeRegExpMacroAssembler::Execute()
102 * (in regexp-macro-assembler.cc) via the GeneratedCode wrapper.
103 */
104
105#define __ ACCESS_MASM(masm_)
106
108 Zone* zone, Mode mode,
109 int registers_to_save)
110 : NativeRegExpMacroAssembler(isolate, zone),
111 masm_(std::make_unique<MacroAssembler>(
112 isolate, zone, CodeObjectRequired::kYes,
113 NewAssemblerBuffer(kInitialBufferSize))),
114 no_root_array_scope_(masm_.get()),
115 mode_(mode),
116 num_registers_(registers_to_save),
117 num_saved_registers_(registers_to_save),
118 entry_label_(),
119 start_label_(),
120 success_label_(),
121 backtrack_label_(),
122 exit_label_() {
123 DCHECK_EQ(0, registers_to_save % 2);
124 // We can cache at most 16 W registers in x0-x7.
125 static_assert(kNumCachedRegisters <= 16);
126 static_assert((kNumCachedRegisters % 2) == 0);
127 __ CallTarget();
128
129 __ B(&entry_label_); // We'll write the entry code later.
130 __ Bind(&start_label_); // And then continue from here.
131}
132
133RegExpMacroAssemblerARM64::~RegExpMacroAssemblerARM64() = default;
134
135void RegExpMacroAssemblerARM64::AbortedCodeGeneration() {
136 masm_->AbortedCodeGeneration();
137 entry_label_.Unuse();
138 start_label_.Unuse();
139 success_label_.Unuse();
140 backtrack_label_.Unuse();
141 exit_label_.Unuse();
142 check_preempt_label_.Unuse();
143 stack_overflow_label_.Unuse();
144 fallback_label_.Unuse();
145}
146
147int RegExpMacroAssemblerARM64::stack_limit_slack_slot_count() {
148 return RegExpStack::kStackLimitSlackSlotCount;
149}
150
151void RegExpMacroAssemblerARM64::AdvanceCurrentPosition(int by) {
152 if (by != 0) {
153 __ Add(current_input_offset(),
154 current_input_offset(), by * char_size());
155 }
156}
157
158
159void RegExpMacroAssemblerARM64::AdvanceRegister(int reg, int by) {
160 DCHECK((reg >= 0) && (reg < num_registers_));
161 if (by != 0) {
162 RegisterState register_state = GetRegisterState(reg);
163 switch (register_state) {
164 case STACKED:
165 __ Ldr(w10, register_location(reg));
166 __ Add(w10, w10, by);
167 __ Str(w10, register_location(reg));
168 break;
169 case CACHED_LSW: {
170 Register to_advance = GetCachedRegister(reg);
171 __ Add(to_advance, to_advance, by);
172 break;
173 }
174 case CACHED_MSW: {
175 Register to_advance = GetCachedRegister(reg);
176 // Sign-extend to int64, shift as uint64, cast back to int64.
177 __ Add(
178 to_advance, to_advance,
179 static_cast<int64_t>(static_cast<uint64_t>(static_cast<int64_t>(by))
180 << kWRegSizeInBits));
181 break;
182 }
183 default:
184 UNREACHABLE();
185 }
186 }
187}
188
189
190void RegExpMacroAssemblerARM64::Backtrack() {
191 CheckPreemption();
192 if (has_backtrack_limit()) {
193 Label next;
194 UseScratchRegisterScope temps(masm_.get());
195 Register scratch = temps.AcquireW();
196 __ Ldr(scratch, MemOperand(frame_pointer(), kBacktrackCountOffset));
197 __ Add(scratch, scratch, 1);
198 __ Str(scratch, MemOperand(frame_pointer(), kBacktrackCountOffset));
199 __ Cmp(scratch, Operand(backtrack_limit()));
200 __ B(ne, &next);
201
202 // Backtrack limit exceeded.
203 if (can_fallback()) {
204 __ B(&fallback_label_);
205 } else {
206 // Can't fallback, so we treat it as a failed match.
207 Fail();
208 }
209
210 __ bind(&next);
211 }
212 Pop(w10);
213 __ Add(x10, code_pointer(), Operand(w10, UXTW));
214 __ Br(x10);
215}
216
217
218void RegExpMacroAssemblerARM64::Bind(Label* label) {
219 __ Bind(label);
220}
221
222void RegExpMacroAssemblerARM64::BindJumpTarget(Label* label) {
223 __ BindJumpTarget(label);
224}
225
226void RegExpMacroAssemblerARM64::CheckCharacter(uint32_t c, Label* on_equal) {
227 CompareAndBranchOrBacktrack(current_character(), c, eq, on_equal);
228}
229
230void RegExpMacroAssemblerARM64::CheckCharacterGT(base::uc16 limit,
231 Label* on_greater) {
232 CompareAndBranchOrBacktrack(current_character(), limit, hi, on_greater);
233}
234
235void RegExpMacroAssemblerARM64::CheckAtStart(int cp_offset,
236 Label* on_at_start) {
237 __ Add(w10, current_input_offset(),
238 Operand(-char_size() + cp_offset * char_size()));
239 __ Cmp(w10, string_start_minus_one());
240 BranchOrBacktrack(eq, on_at_start);
241}
242
243void RegExpMacroAssemblerARM64::CheckNotAtStart(int cp_offset,
244 Label* on_not_at_start) {
245 __ Add(w10, current_input_offset(),
246 Operand(-char_size() + cp_offset * char_size()));
247 __ Cmp(w10, string_start_minus_one());
248 BranchOrBacktrack(ne, on_not_at_start);
249}
250
251void RegExpMacroAssemblerARM64::CheckCharacterLT(base::uc16 limit,
252 Label* on_less) {
253 CompareAndBranchOrBacktrack(current_character(), limit, lo, on_less);
254}
255
256void RegExpMacroAssemblerARM64::CheckCharacters(
257 base::Vector<const base::uc16> str, int cp_offset, Label* on_failure,
258 bool check_end_of_string) {
259 // This method is only ever called from the cctests.
260
261 if (check_end_of_string) {
262 // Is last character of required match inside string.
263 CheckPosition(cp_offset + str.length() - 1, on_failure);
264 }
265
266 Register characters_address = x11;
267
268 __ Add(characters_address,
269 input_end(),
270 Operand(current_input_offset(), SXTW));
271 if (cp_offset != 0) {
272 __ Add(characters_address, characters_address, cp_offset * char_size());
273 }
274
275 for (int i = 0; i < str.length(); i++) {
276 if (mode_ == LATIN1) {
277 __ Ldrb(w10, MemOperand(characters_address, 1, PostIndex));
278 DCHECK_GE(String::kMaxOneByteCharCode, str[i]);
279 } else {
280 __ Ldrh(w10, MemOperand(characters_address, 2, PostIndex));
281 }
282 CompareAndBranchOrBacktrack(w10, str[i], ne, on_failure);
283 }
284}
285
286void RegExpMacroAssemblerARM64::CheckGreedyLoop(Label* on_equal) {
287 __ Ldr(w10, MemOperand(backtrack_stackpointer()));
288 __ Cmp(current_input_offset(), w10);
289 __ Cset(x11, eq);
290 __ Add(backtrack_stackpointer(),
291 backtrack_stackpointer(), Operand(x11, LSL, kWRegSizeLog2));
292 BranchOrBacktrack(eq, on_equal);
293}
294
295void RegExpMacroAssemblerARM64::PushCachedRegisters() {
296 CPURegList cached_registers(CPURegister::kRegister, kXRegSizeInBits, 0, 7);
297 DCHECK_EQ(kNumCachedRegisters, cached_registers.Count() * 2);
298 __ PushCPURegList(cached_registers);
299}
300
301void RegExpMacroAssemblerARM64::PopCachedRegisters() {
302 CPURegList cached_registers(CPURegister::kRegister, kXRegSizeInBits, 0, 7);
303 DCHECK_EQ(kNumCachedRegisters, cached_registers.Count() * 2);
304 __ PopCPURegList(cached_registers);
305}
306
307void RegExpMacroAssemblerARM64::CheckNotBackReferenceIgnoreCase(
308 int start_reg, bool read_backward, bool unicode, Label* on_no_match) {
309 Label fallthrough;
310
311 Register capture_start_offset = w10;
312 // Save the capture length in a callee-saved register so it will
313 // be preserved if we call a C helper.
314 Register capture_length = w19;
315 DCHECK(kCalleeSaved.IncludesAliasOf(capture_length));
316
317 // Find length of back-referenced capture.
318 DCHECK_EQ(0, start_reg % 2);
319 if (start_reg < kNumCachedRegisters) {
320 __ Mov(capture_start_offset.X(), GetCachedRegister(start_reg));
321 __ Lsr(x11, GetCachedRegister(start_reg), kWRegSizeInBits);
322 } else {
323 __ Ldp(w11, capture_start_offset, capture_location(start_reg, x10));
324 }
325 __ Sub(capture_length, w11, capture_start_offset); // Length to check.
326
327 // At this point, the capture registers are either both set or both cleared.
328 // If the capture length is zero, then the capture is either empty or cleared.
329 // Fall through in both cases.
330 __ CompareAndBranch(capture_length, Operand(0), eq, &fallthrough);
331
332 // Check that there are enough characters left in the input.
333 if (read_backward) {
334 __ Add(w12, string_start_minus_one(), capture_length);
335 __ Cmp(current_input_offset(), w12);
336 BranchOrBacktrack(le, on_no_match);
337 } else {
338 __ Cmn(capture_length, current_input_offset());
339 BranchOrBacktrack(gt, on_no_match);
340 }
341
342 if (mode_ == LATIN1) {
343 Label success;
344 Label fail;
345 Label loop_check;
346
347 Register capture_start_address = x12;
348 Register capture_end_address = x13;
349 Register current_position_address = x14;
350
351 __ Add(capture_start_address,
352 input_end(),
353 Operand(capture_start_offset, SXTW));
354 __ Add(capture_end_address, capture_start_address,
355 Operand(capture_length, SXTW));
356 __ Add(current_position_address,
357 input_end(),
358 Operand(current_input_offset(), SXTW));
359 if (read_backward) {
360 // Offset by length when matching backwards.
361 __ Sub(current_position_address, current_position_address,
362 Operand(capture_length, SXTW));
363 }
364
365 Label loop;
366 __ Bind(&loop);
367 __ Ldrb(w10, MemOperand(capture_start_address, 1, PostIndex));
368 __ Ldrb(w11, MemOperand(current_position_address, 1, PostIndex));
369 __ Cmp(w10, w11);
370 __ B(eq, &loop_check);
371
372 // Mismatch, try case-insensitive match (converting letters to lower-case).
373 __ Orr(w10, w10, 0x20); // Convert capture character to lower-case.
374 __ Orr(w11, w11, 0x20); // Also convert input character.
375 __ Cmp(w11, w10);
376 __ B(ne, &fail);
377 __ Sub(w10, w10, 'a');
378 __ Cmp(w10, 'z' - 'a'); // Is w10 a lowercase letter?
379 __ B(ls, &loop_check); // In range 'a'-'z'.
380 // Latin-1: Check for values in range [224,254] but not 247.
381 __ Sub(w10, w10, 224 - 'a');
382 __ Cmp(w10, 254 - 224);
383 __ Ccmp(w10, 247 - 224, ZFlag, ls); // Check for 247.
384 __ B(eq, &fail); // Weren't Latin-1 letters.
385
386 __ Bind(&loop_check);
387 __ Cmp(capture_start_address, capture_end_address);
388 __ B(lt, &loop);
389 __ B(&success);
390
391 __ Bind(&fail);
392 BranchOrBacktrack(al, on_no_match);
393
394 __ Bind(&success);
395 // Compute new value of character position after the matched part.
396 __ Sub(current_input_offset().X(), current_position_address, input_end());
397 if (read_backward) {
398 __ Sub(current_input_offset().X(), current_input_offset().X(),
399 Operand(capture_length, SXTW));
400 }
401 if (v8_flags.debug_code) {
402 __ Cmp(current_input_offset().X(), Operand(current_input_offset(), SXTW));
403 __ Ccmp(current_input_offset(), 0, NoFlag, eq);
404 // The current input offset should be <= 0, and fit in a W register.
405 __ Check(le, AbortReason::kOffsetOutOfRange);
406 }
407 } else {
408 DCHECK(mode_ == UC16);
409 int argument_count = 4;
410
411 PushCachedRegisters();
412
413 // Put arguments into arguments registers.
414 // Parameters are
415 // x0: Address byte_offset1 - Address captured substring's start.
416 // x1: Address byte_offset2 - Address of current character position.
417 // w2: size_t byte_length - length of capture in bytes(!)
418 // x3: Isolate* isolate.
419
420 // Address of start of capture.
421 __ Add(x0, input_end(), Operand(capture_start_offset, SXTW));
422 // Length of capture.
423 __ Mov(w2, capture_length);
424 // Address of current input position.
425 __ Add(x1, input_end(), Operand(current_input_offset(), SXTW));
426 if (read_backward) {
427 __ Sub(x1, x1, Operand(capture_length, SXTW));
428 }
429 // Isolate.
430 __ Mov(x3, ExternalReference::isolate_address(isolate()));
431
432 {
433 AllowExternalCallThatCantCauseGC scope(masm_.get());
434 ExternalReference function =
435 unicode
436 ? ExternalReference::re_case_insensitive_compare_unicode()
437 : ExternalReference::re_case_insensitive_compare_non_unicode();
438 CallCFunctionFromIrregexpCode(function, argument_count);
439 }
440
441 // Check if function returned non-zero for success or zero for failure.
442 // x0 is one of the registers used as a cache so it must be tested before
443 // the cache is restored.
444 __ Cmp(x0, 0);
445 PopCachedRegisters();
446 BranchOrBacktrack(eq, on_no_match);
447
448 // On success, advance position by length of capture.
449 if (read_backward) {
450 __ Sub(current_input_offset(), current_input_offset(), capture_length);
451 } else {
452 __ Add(current_input_offset(), current_input_offset(), capture_length);
453 }
454 }
455
456 __ Bind(&fallthrough);
457}
458
459void RegExpMacroAssemblerARM64::CheckNotBackReference(int start_reg,
460 bool read_backward,
461 Label* on_no_match) {
462 Label fallthrough;
463
464 Register capture_start_address = x12;
465 Register capture_end_address = x13;
466 Register current_position_address = x14;
467 Register capture_length = w15;
468
469 // Find length of back-referenced capture.
470 DCHECK_EQ(0, start_reg % 2);
471 if (start_reg < kNumCachedRegisters) {
472 __ Mov(x10, GetCachedRegister(start_reg));
473 __ Lsr(x11, GetCachedRegister(start_reg), kWRegSizeInBits);
474 } else {
475 __ Ldp(w11, w10, capture_location(start_reg, x10));
476 }
477 __ Sub(capture_length, w11, w10); // Length to check.
478
479 // At this point, the capture registers are either both set or both cleared.
480 // If the capture length is zero, then the capture is either empty or cleared.
481 // Fall through in both cases.
482 __ CompareAndBranch(capture_length, Operand(0), eq, &fallthrough);
483
484 // Check that there are enough characters left in the input.
485 if (read_backward) {
486 __ Add(w12, string_start_minus_one(), capture_length);
487 __ Cmp(current_input_offset(), w12);
488 BranchOrBacktrack(le, on_no_match);
489 } else {
490 __ Cmn(capture_length, current_input_offset());
491 BranchOrBacktrack(gt, on_no_match);
492 }
493
494 // Compute pointers to match string and capture string
495 __ Add(capture_start_address, input_end(), Operand(w10, SXTW));
496 __ Add(capture_end_address,
497 capture_start_address,
498 Operand(capture_length, SXTW));
499 __ Add(current_position_address,
500 input_end(),
501 Operand(current_input_offset(), SXTW));
502 if (read_backward) {
503 // Offset by length when matching backwards.
504 __ Sub(current_position_address, current_position_address,
505 Operand(capture_length, SXTW));
506 }
507
508 Label loop;
509 __ Bind(&loop);
510 if (mode_ == LATIN1) {
511 __ Ldrb(w10, MemOperand(capture_start_address, 1, PostIndex));
512 __ Ldrb(w11, MemOperand(current_position_address, 1, PostIndex));
513 } else {
514 DCHECK(mode_ == UC16);
515 __ Ldrh(w10, MemOperand(capture_start_address, 2, PostIndex));
516 __ Ldrh(w11, MemOperand(current_position_address, 2, PostIndex));
517 }
518 __ Cmp(w10, w11);
519 BranchOrBacktrack(ne, on_no_match);
520 __ Cmp(capture_start_address, capture_end_address);
521 __ B(lt, &loop);
522
523 // Move current character position to position after match.
524 __ Sub(current_input_offset().X(), current_position_address, input_end());
525 if (read_backward) {
526 __ Sub(current_input_offset().X(), current_input_offset().X(),
527 Operand(capture_length, SXTW));
528 }
529
530 if (v8_flags.debug_code) {
531 __ Cmp(current_input_offset().X(), Operand(current_input_offset(), SXTW));
532 __ Ccmp(current_input_offset(), 0, NoFlag, eq);
533 // The current input offset should be <= 0, and fit in a W register.
534 __ Check(le, AbortReason::kOffsetOutOfRange);
535 }
536 __ Bind(&fallthrough);
537}
538
539
540void RegExpMacroAssemblerARM64::CheckNotCharacter(unsigned c,
541 Label* on_not_equal) {
542 CompareAndBranchOrBacktrack(current_character(), c, ne, on_not_equal);
543}
544
545
546void RegExpMacroAssemblerARM64::CheckCharacterAfterAnd(uint32_t c,
547 uint32_t mask,
548 Label* on_equal) {
549 __ And(w10, current_character(), mask);
550 CompareAndBranchOrBacktrack(w10, c, eq, on_equal);
551}
552
553
554void RegExpMacroAssemblerARM64::CheckNotCharacterAfterAnd(unsigned c,
555 unsigned mask,
556 Label* on_not_equal) {
557 __ And(w10, current_character(), mask);
558 CompareAndBranchOrBacktrack(w10, c, ne, on_not_equal);
559}
560
561void RegExpMacroAssemblerARM64::CheckNotCharacterAfterMinusAnd(
562 base::uc16 c, base::uc16 minus, base::uc16 mask, Label* on_not_equal) {
563 DCHECK_GT(String::kMaxUtf16CodeUnit, minus);
564 __ Sub(w10, current_character(), minus);
565 __ And(w10, w10, mask);
566 CompareAndBranchOrBacktrack(w10, c, ne, on_not_equal);
567}
568
569void RegExpMacroAssemblerARM64::CheckCharacterInRange(base::uc16 from,
570 base::uc16 to,
571 Label* on_in_range) {
572 __ Sub(w10, current_character(), from);
573 // Unsigned lower-or-same condition.
574 CompareAndBranchOrBacktrack(w10, to - from, ls, on_in_range);
575}
576
577void RegExpMacroAssemblerARM64::CheckCharacterNotInRange(
578 base::uc16 from, base::uc16 to, Label* on_not_in_range) {
579 __ Sub(w10, current_character(), from);
580 // Unsigned higher condition.
581 CompareAndBranchOrBacktrack(w10, to - from, hi, on_not_in_range);
582}
583
584void RegExpMacroAssemblerARM64::CallIsCharacterInRangeArray(
585 const ZoneList<CharacterRange>* ranges) {
586 static const int kNumArguments = 2;
587 __ Mov(w0, current_character());
588 __ Mov(x1, GetOrAddRangeArray(ranges));
589
590 {
591 // We have a frame (set up in GetCode), but the assembler doesn't know.
592 FrameScope scope(masm_.get(), StackFrame::MANUAL);
593 CallCFunctionFromIrregexpCode(
594 ExternalReference::re_is_character_in_range_array(), kNumArguments);
595 }
596
597 __ Mov(code_pointer(), Operand(masm_->CodeObject()));
598}
599
600bool RegExpMacroAssemblerARM64::CheckCharacterInRangeArray(
601 const ZoneList<CharacterRange>* ranges, Label* on_in_range) {
602 // Note: due to the arm64 oddity of x0 being a 'cached register',
603 // pushing/popping registers must happen outside of CallIsCharacterInRange
604 // s.t. we can compare the return value to 0 before popping x0.
605 PushCachedRegisters();
606 CallIsCharacterInRangeArray(ranges);
607 __ Cmp(x0, 0);
608 PopCachedRegisters();
609 BranchOrBacktrack(ne, on_in_range);
610 return true;
611}
612
613bool RegExpMacroAssemblerARM64::CheckCharacterNotInRangeArray(
614 const ZoneList<CharacterRange>* ranges, Label* on_not_in_range) {
615 // Note: due to the arm64 oddity of x0 being a 'cached register',
616 // pushing/popping registers must happen outside of CallIsCharacterInRange
617 // s.t. we can compare the return value to 0 before popping x0.
618 PushCachedRegisters();
619 CallIsCharacterInRangeArray(ranges);
620 __ Cmp(x0, 0);
621 PopCachedRegisters();
622 BranchOrBacktrack(eq, on_not_in_range);
623 return true;
624}
625
626void RegExpMacroAssemblerARM64::CheckBitInTable(
627 Handle<ByteArray> table,
628 Label* on_bit_set) {
629 __ Mov(x11, Operand(table));
630 if ((mode_ != LATIN1) || (kTableMask != String::kMaxOneByteCharCode)) {
631 __ And(w10, current_character(), kTableMask);
632 __ Add(w10, w10, OFFSET_OF_DATA_START(ByteArray) - kHeapObjectTag);
633 } else {
634 __ Add(w10, current_character(),
635 OFFSET_OF_DATA_START(ByteArray) - kHeapObjectTag);
636 }
637 __ Ldrb(w11, MemOperand(x11, w10, UXTW));
638 CompareAndBranchOrBacktrack(w11, 0, ne, on_bit_set);
639}
640
641void RegExpMacroAssemblerARM64::SkipUntilBitInTable(
642 int cp_offset, Handle<ByteArray> table,
643 Handle<ByteArray> nibble_table_array, int advance_by) {
644 Label cont, scalar_repeat;
645
646 const bool use_simd = SkipUntilBitInTableUseSimd(advance_by);
647 if (use_simd) {
648 DCHECK(!nibble_table_array.is_null());
649 Label simd_repeat, found, scalar;
650 static constexpr int kVectorSize = 16;
651 const int kCharsPerVector = kVectorSize / char_size();
652
653 // Fallback to scalar version if there are less than kCharsPerVector chars
654 // left in the subject.
655 // We subtract 1 because CheckPosition assumes we are reading 1 character
656 // plus cp_offset. So the -1 is the the character that is assumed to be
657 // read by default.
658 CheckPosition(cp_offset + kCharsPerVector - 1, &scalar);
659
660 // Load table and mask constants.
661 // For a description of the table layout, check the comment on
662 // BoyerMooreLookahead::GetSkipTable in regexp-compiler.cc.
663 VRegister nibble_table = v0;
664 __ Mov(x8, Operand(nibble_table_array));
665 __ Add(x8, x8, OFFSET_OF_DATA_START(ByteArray) - kHeapObjectTag);
666 __ Ld1(nibble_table.V16B(), MemOperand(x8));
667 VRegister nibble_mask = v1;
668 const uint64_t nibble_mask_imm = 0x0f0f0f0f'0f0f0f0f;
669 __ Movi(nibble_mask.V16B(), nibble_mask_imm, nibble_mask_imm);
670 VRegister hi_nibble_lookup_mask = v2;
671 const uint64_t hi_nibble_mask_imm = 0x80402010'08040201;
672 __ Movi(hi_nibble_lookup_mask.V16B(), hi_nibble_mask_imm,
673 hi_nibble_mask_imm);
674
675 Bind(&simd_repeat);
676 // Load next characters into vector.
677 VRegister input_vec = v3;
678 __ Add(x8, input_end(), Operand(current_input_offset(), SXTW));
679 __ Add(x8, x8, cp_offset * char_size());
680 __ Ld1(input_vec.V16B(), MemOperand(x8));
681
682 // Extract low nibbles.
683 // lo_nibbles = input & 0x0f
684 VRegister lo_nibbles = v4;
685 __ And(lo_nibbles.V16B(), nibble_mask.V16B(), input_vec.V16B());
686 // Extract high nibbles.
687 // hi_nibbles = (input >> 4) & 0x0f
688 VRegister hi_nibbles = v5;
689 __ Ushr(hi_nibbles.V16B(), input_vec.V16B(), 4);
690 __ And(hi_nibbles.V16B(), hi_nibbles.V16B(), nibble_mask.V16B());
691
692 // Get rows of nibbles table based on low nibbles.
693 // row = nibble_table[lo_nibbles]
694 VRegister row = v6;
695 __ Tbl(row.V16B(), nibble_table.V16B(), lo_nibbles.V16B());
696
697 // Check if high nibble is set in row.
698 // bitmask = 1 << (hi_nibbles & 0x7)
699 // = hi_nibbles_lookup_mask[hi_nibbles] & 0x7
700 // Note: The hi_nibbles & 0x7 part is implicitly executed, as tbl sets
701 // the result byte to zero if the lookup index is out of range.
702 VRegister bitmask = v7;
703 __ Tbl(bitmask.V16B(), hi_nibble_lookup_mask.V16B(), hi_nibbles.V16B());
704
705 // result = row & bitmask != 0
706 VRegister result = ReassignRegister(lo_nibbles);
707 __ Cmtst(result.V16B(), row.V16B(), bitmask.V16B());
708
709 // Narrow the result to 64 bit.
710 __ Shrn(result.V8B(), result.V8H(), 4);
711 __ Umov(x8, result.V1D(), 0);
712 __ Cbnz(x8, &found);
713
714 // The maximum lookahead for boyer moore is less than vector size, so we can
715 // ignore advance_by in the vectorized version.
716 AdvanceCurrentPosition(kCharsPerVector);
717 CheckPosition(cp_offset + kCharsPerVector - 1, &scalar);
718 __ B(&simd_repeat);
719
720 Bind(&found);
721 // Extract position.
722 __ Rbit(x8, x8);
723 __ Clz(x8, x8);
724 __ Lsr(x8, x8, 2);
725 if (mode_ == UC16) {
726 // Make sure that we skip an even number of bytes in 2-byte subjects.
727 // Odd skips can happen if the higher byte produced a match.
728 // False positives should be rare and are no problem in general, as the
729 // following instructions will check for an exact match.
730 __ And(x8, x8, Immediate(0xfffe));
731 }
732 __ Add(current_input_offset(), current_input_offset(), w8);
733 __ B(&cont);
734 Bind(&scalar);
735 }
736
737 // Scalar version.
738 Register table_reg = x9;
739 __ Mov(table_reg, Operand(table));
740
741 Bind(&scalar_repeat);
742 CheckPosition(cp_offset, &cont);
743 LoadCurrentCharacterUnchecked(cp_offset, 1);
744 Register index = w10;
745 if ((mode_ != LATIN1) || (kTableMask != String::kMaxOneByteCharCode)) {
746 __ And(index, current_character(), kTableMask);
747 __ Add(index, index, OFFSET_OF_DATA_START(ByteArray) - kHeapObjectTag);
748 } else {
749 __ Add(index, current_character(),
750 OFFSET_OF_DATA_START(ByteArray) - kHeapObjectTag);
751 }
752 Register found_in_table = w11;
753 __ Ldrb(found_in_table, MemOperand(table_reg, index, UXTW));
754 __ Cbnz(found_in_table, &cont);
755 AdvanceCurrentPosition(advance_by);
756 __ B(&scalar_repeat);
757
758 Bind(&cont);
759}
760
761bool RegExpMacroAssemblerARM64::SkipUntilBitInTableUseSimd(int advance_by) {
762 // We only use SIMD instead of the scalar version if we advance by 1 byte
763 // in each iteration. For higher values the scalar version performs better.
764 return v8_flags.regexp_simd && advance_by * char_size() == 1;
765}
766
767bool RegExpMacroAssemblerARM64::CheckSpecialClassRanges(
768 StandardCharacterSet type, Label* on_no_match) {
769 // Range checks (c in min..max) are generally implemented by an unsigned
770 // (c - min) <= (max - min) check
771 // TODO(jgruber): No custom implementation (yet): s(UC16), S(UC16).
772 switch (type) {
773 case StandardCharacterSet::kWhitespace:
774 // Match space-characters.
775 if (mode_ == LATIN1) {
776 // One byte space characters are '\t'..'\r', ' ' and \u00a0.
777 Label success;
778 // Check for ' ' or 0x00A0.
779 __ Cmp(current_character(), ' ');
780 __ Ccmp(current_character(), 0x00A0, ZFlag, ne);
781 __ B(eq, &success);
782 // Check range 0x09..0x0D.
783 __ Sub(w10, current_character(), '\t');
784 CompareAndBranchOrBacktrack(w10, '\r' - '\t', hi, on_no_match);
785 __ Bind(&success);
786 return true;
787 }
788 return false;
789 case StandardCharacterSet::kNotWhitespace:
790 // The emitted code for generic character classes is good enough.
791 return false;
792 case StandardCharacterSet::kDigit:
793 // Match ASCII digits ('0'..'9').
794 __ Sub(w10, current_character(), '0');
795 CompareAndBranchOrBacktrack(w10, '9' - '0', hi, on_no_match);
796 return true;
797 case StandardCharacterSet::kNotDigit:
798 // Match ASCII non-digits.
799 __ Sub(w10, current_character(), '0');
800 CompareAndBranchOrBacktrack(w10, '9' - '0', ls, on_no_match);
801 return true;
802 case StandardCharacterSet::kNotLineTerminator: {
803 // Match non-newlines (not 0x0A('\n'), 0x0D('\r'), 0x2028 and 0x2029)
804 // Here we emit the conditional branch only once at the end to make branch
805 // prediction more efficient, even though we could branch out of here
806 // as soon as a character matches.
807 __ Cmp(current_character(), 0x0A);
808 __ Ccmp(current_character(), 0x0D, ZFlag, ne);
809 if (mode_ == UC16) {
810 __ Sub(w10, current_character(), 0x2028);
811 // If the Z flag was set we clear the flags to force a branch.
812 __ Ccmp(w10, 0x2029 - 0x2028, NoFlag, ne);
813 // ls -> !((C==1) && (Z==0))
814 BranchOrBacktrack(ls, on_no_match);
815 } else {
816 BranchOrBacktrack(eq, on_no_match);
817 }
818 return true;
819 }
820 case StandardCharacterSet::kLineTerminator: {
821 // Match newlines (0x0A('\n'), 0x0D('\r'), 0x2028 and 0x2029)
822 // We have to check all 4 newline characters before emitting
823 // the conditional branch.
824 __ Cmp(current_character(), 0x0A);
825 __ Ccmp(current_character(), 0x0D, ZFlag, ne);
826 if (mode_ == UC16) {
827 __ Sub(w10, current_character(), 0x2028);
828 // If the Z flag was set we clear the flags to force a fall-through.
829 __ Ccmp(w10, 0x2029 - 0x2028, NoFlag, ne);
830 // hi -> (C==1) && (Z==0)
831 BranchOrBacktrack(hi, on_no_match);
832 } else {
833 BranchOrBacktrack(ne, on_no_match);
834 }
835 return true;
836 }
837 case StandardCharacterSet::kWord: {
838 if (mode_ != LATIN1) {
839 // Table is 256 entries, so all Latin1 characters can be tested.
840 CompareAndBranchOrBacktrack(current_character(), 'z', hi, on_no_match);
841 }
842 ExternalReference map = ExternalReference::re_word_character_map();
843 __ Mov(x10, map);
844 __ Ldrb(w10, MemOperand(x10, current_character(), UXTW));
845 CompareAndBranchOrBacktrack(w10, 0, eq, on_no_match);
846 return true;
847 }
848 case StandardCharacterSet::kNotWord: {
849 Label done;
850 if (mode_ != LATIN1) {
851 // Table is 256 entries, so all Latin1 characters can be tested.
852 __ Cmp(current_character(), 'z');
853 __ B(hi, &done);
854 }
855 ExternalReference map = ExternalReference::re_word_character_map();
856 __ Mov(x10, map);
857 __ Ldrb(w10, MemOperand(x10, current_character(), UXTW));
858 CompareAndBranchOrBacktrack(w10, 0, ne, on_no_match);
859 __ Bind(&done);
860 return true;
861 }
862 case StandardCharacterSet::kEverything:
863 // Match any character.
864 return true;
865 }
866}
867
868void RegExpMacroAssemblerARM64::Fail() {
869 __ Mov(w0, FAILURE);
870 __ B(&exit_label_);
871}
872
873void RegExpMacroAssemblerARM64::LoadRegExpStackPointerFromMemory(Register dst) {
874 ExternalReference ref =
875 ExternalReference::address_of_regexp_stack_stack_pointer(isolate());
876 __ Mov(dst, ref);
877 __ Ldr(dst, MemOperand(dst));
878}
879
880void RegExpMacroAssemblerARM64::StoreRegExpStackPointerToMemory(
881 Register src, Register scratch) {
882 ExternalReference ref =
883 ExternalReference::address_of_regexp_stack_stack_pointer(isolate());
884 __ Mov(scratch, ref);
885 __ Str(src, MemOperand(scratch));
886}
887
888void RegExpMacroAssemblerARM64::PushRegExpBasePointer(Register stack_pointer,
889 Register scratch) {
890 ExternalReference ref =
891 ExternalReference::address_of_regexp_stack_memory_top_address(isolate());
892 __ Mov(scratch, ref);
893 __ Ldr(scratch, MemOperand(scratch));
894 __ Sub(scratch, stack_pointer, scratch);
895 __ Str(scratch, MemOperand(frame_pointer(), kRegExpStackBasePointerOffset));
896}
897
898void RegExpMacroAssemblerARM64::PopRegExpBasePointer(Register stack_pointer_out,
899 Register scratch) {
900 ExternalReference ref =
901 ExternalReference::address_of_regexp_stack_memory_top_address(isolate());
902 __ Ldr(stack_pointer_out,
903 MemOperand(frame_pointer(), kRegExpStackBasePointerOffset));
904 __ Mov(scratch, ref);
905 __ Ldr(scratch, MemOperand(scratch));
906 __ Add(stack_pointer_out, stack_pointer_out, scratch);
907 StoreRegExpStackPointerToMemory(stack_pointer_out, scratch);
908}
909
910DirectHandle<HeapObject> RegExpMacroAssemblerARM64::GetCode(
911 DirectHandle<String> source, RegExpFlags flags) {
912 Label return_w0;
913 // Finalize code - write the entry point code now we know how many
914 // registers we need.
915
916 // Entry code:
917 __ Bind(&entry_label_);
918
919 // Arguments on entry:
920 // x0: String input
921 // x1: int start_offset
922 // x2: uint8_t* input_start
923 // x3: uint8_t* input_end
924 // x4: int* output array
925 // x5: int output array size
926 // x6: int direct_call
927 // x7: Isolate* isolate
928 //
929 // sp[0]: secondary link/return address used by native call
930
931 // Tell the system that we have a stack frame. Because the type is MANUAL, no
932 // code is generated.
933 FrameScope scope(masm_.get(), StackFrame::MANUAL);
934
935 // Stack frame setup.
936 // Push callee-saved registers.
937 const CPURegList registers_to_retain = kCalleeSaved;
938 DCHECK_EQ(registers_to_retain.Count(), kNumCalleeSavedRegisters);
939 __ PushCPURegList(registers_to_retain);
940 static_assert(kFrameTypeOffset == kFramePointerOffset - kSystemPointerSize);
941 __ EnterFrame(StackFrame::IRREGEXP);
942 // Only push the argument registers that we need.
943 static_assert(kIsolateOffset ==
944 kFrameTypeOffset - kPaddingAfterFrameType - kSystemPointerSize);
945 static_assert(kDirectCallOffset == kIsolateOffset - kSystemPointerSize);
946 static_assert(kNumOutputRegistersOffset ==
947 kDirectCallOffset - kSystemPointerSize);
948 static_assert(kInputStringOffset ==
949 kNumOutputRegistersOffset - kSystemPointerSize);
950 __ PushCPURegList(CPURegList{x0, x5, x6, x7});
951
952 // Initialize callee-saved registers.
953 __ Mov(start_offset(), w1);
954 __ Mov(input_start(), x2);
955 __ Mov(input_end(), x3);
956 __ Mov(output_array(), x4);
957
958 // Make sure the stack alignment will be respected.
959 const int alignment = masm_->ActivationFrameAlignment();
960 DCHECK_EQ(alignment % 16, 0);
961 const int align_mask = (alignment / kWRegSize) - 1;
962
963 // Make room for stack locals.
964 static constexpr int kWRegPerXReg = kXRegSize / kWRegSize;
965 DCHECK_EQ(kNumberOfStackLocals * kWRegPerXReg,
966 ((kNumberOfStackLocals * kWRegPerXReg) + align_mask) & ~align_mask);
967 __ Claim(kNumberOfStackLocals * kWRegPerXReg);
968
969 // Initialize backtrack stack pointer. It must not be clobbered from here on.
970 // Note the backtrack_stackpointer is callee-saved.
971 static_assert(backtrack_stackpointer() == x23);
972 LoadRegExpStackPointerFromMemory(backtrack_stackpointer());
973
974 // Store the regexp base pointer - we'll later restore it / write it to
975 // memory when returning from this irregexp code object.
976 PushRegExpBasePointer(backtrack_stackpointer(), x11);
977
978 // Set the number of registers we will need to allocate, that is:
979 // - (num_registers_ - kNumCachedRegisters) (W registers)
980 const int num_stack_registers =
981 std::max(0, num_registers_ - kNumCachedRegisters);
982 const int num_wreg_to_allocate =
983 (num_stack_registers + align_mask) & ~align_mask;
984
985 {
986 // Check if we have space on the stack.
987 Label stack_limit_hit, stack_ok;
988
989 ExternalReference stack_limit =
990 ExternalReference::address_of_jslimit(isolate());
991 __ Mov(x10, stack_limit);
992 __ Ldr(x10, MemOperand(x10));
993 __ Subs(x10, sp, x10);
994 Operand extra_space_for_variables(num_wreg_to_allocate * kWRegSize);
995
996 // Handle it if the stack pointer is already below the stack limit.
997 __ B(ls, &stack_limit_hit);
998
999 // Check if there is room for the variable number of registers above
1000 // the stack limit.
1001 __ Cmp(x10, extra_space_for_variables);
1002 __ B(hs, &stack_ok);
1003
1004 // Exit with OutOfMemory exception. There is not enough space on the stack
1005 // for our working registers.
1006 __ Mov(w0, EXCEPTION);
1007 __ B(&return_w0);
1008
1009 __ Bind(&stack_limit_hit);
1010 CallCheckStackGuardState(x10, extra_space_for_variables);
1011 // If returned value is non-zero, we exit with the returned value as result.
1012 __ Cbnz(w0, &return_w0);
1013
1014 __ Bind(&stack_ok);
1015 }
1016
1017 // Allocate space on stack.
1018 __ Claim(num_wreg_to_allocate, kWRegSize);
1019
1020 // Initialize success_counter and kBacktrackCountOffset with 0.
1021 __ Str(wzr, MemOperand(frame_pointer(), kSuccessfulCapturesOffset));
1022 __ Str(wzr, MemOperand(frame_pointer(), kBacktrackCountOffset));
1023
1024 // Find negative length (offset of start relative to end).
1025 __ Sub(x10, input_start(), input_end());
1026 if (v8_flags.debug_code) {
1027 // Check that the size of the input string chars is in range.
1028 __ Neg(x11, x10);
1029 __ Cmp(x11, SeqTwoByteString::kMaxCharsSize);
1030 __ Check(ls, AbortReason::kInputStringTooLong);
1031 }
1032 __ Mov(current_input_offset(), w10);
1033
1034 // The non-position value is used as a clearing value for the
1035 // capture registers, it corresponds to the position of the first character
1036 // minus one.
1037 __ Sub(string_start_minus_one(), current_input_offset(), char_size());
1038 __ Sub(string_start_minus_one(), string_start_minus_one(),
1039 Operand(start_offset(), LSL, (mode_ == UC16) ? 1 : 0));
1040 // We can store this value twice in an X register for initializing
1041 // on-stack registers later.
1042 __ Orr(twice_non_position_value(), string_start_minus_one().X(),
1043 Operand(string_start_minus_one().X(), LSL, kWRegSizeInBits));
1044
1045 // Initialize code pointer register.
1046 __ Mov(code_pointer(), Operand(masm_->CodeObject()));
1047
1048 Label load_char_start_regexp;
1049 {
1050 Label start_regexp;
1051 // Load newline if index is at start, previous character otherwise.
1052 __ Cbnz(start_offset(), &load_char_start_regexp);
1053 __ Mov(current_character(), '\n');
1054 __ B(&start_regexp);
1055
1056 // Global regexp restarts matching here.
1057 __ Bind(&load_char_start_regexp);
1058 // Load previous char as initial value of current character register.
1059 LoadCurrentCharacterUnchecked(-1, 1);
1060 __ Bind(&start_regexp);
1061 }
1062
1063 // Initialize on-stack registers.
1064 if (num_saved_registers_ > 0) {
1065 ClearRegisters(0, num_saved_registers_ - 1);
1066 }
1067
1068 // Execute.
1069 __ B(&start_label_);
1070
1071 if (backtrack_label_.is_linked()) {
1072 __ Bind(&backtrack_label_);
1073 Backtrack();
1074 }
1075
1076 if (success_label_.is_linked()) {
1077 Register first_capture_start = w15;
1078
1079 // Save captures when successful.
1080 __ Bind(&success_label_);
1081
1082 if (num_saved_registers_ > 0) {
1083 // V8 expects the output to be an int32_t array.
1084 Register capture_start = w12;
1085 Register capture_end = w13;
1086 Register input_length = w14;
1087
1088 // Copy captures to output.
1089
1090 // Get string length.
1091 __ Sub(x10, input_end(), input_start());
1092 if (v8_flags.debug_code) {
1093 // Check that the size of the input string chars is in range.
1094 __ Cmp(x10, SeqTwoByteString::kMaxCharsSize);
1095 __ Check(ls, AbortReason::kInputStringTooLong);
1096 }
1097 // input_start has a start_offset offset on entry. We need to include
1098 // it when computing the length of the whole string.
1099 if (mode_ == UC16) {
1100 __ Add(input_length, start_offset(), Operand(w10, LSR, 1));
1101 } else {
1102 __ Add(input_length, start_offset(), w10);
1103 }
1104
1105 // Copy the results to the output array from the cached registers first.
1106 for (int i = 0; (i < num_saved_registers_) && (i < kNumCachedRegisters);
1107 i += 2) {
1108 __ Mov(capture_start.X(), GetCachedRegister(i));
1109 __ Lsr(capture_end.X(), capture_start.X(), kWRegSizeInBits);
1110 if ((i == 0) && global_with_zero_length_check()) {
1111 // Keep capture start for the zero-length check later.
1112 // Note this only works when we have at least one cached register
1113 // pair (otherwise we'd never reach this branch).
1114 static_assert(kNumCachedRegisters > 0);
1115 __ Mov(first_capture_start, capture_start);
1116 }
1117 // Offsets need to be relative to the start of the string.
1118 if (mode_ == UC16) {
1119 __ Add(capture_start, input_length, Operand(capture_start, ASR, 1));
1120 __ Add(capture_end, input_length, Operand(capture_end, ASR, 1));
1121 } else {
1122 __ Add(capture_start, input_length, capture_start);
1123 __ Add(capture_end, input_length, capture_end);
1124 }
1125 // The output pointer advances for a possible global match.
1126 __ Stp(capture_start, capture_end,
1127 MemOperand(output_array(), kSystemPointerSize, PostIndex));
1128 }
1129
1130 // Only carry on if there are more than kNumCachedRegisters capture
1131 // registers.
1132 int num_registers_left_on_stack =
1133 num_saved_registers_ - kNumCachedRegisters;
1134 if (num_registers_left_on_stack > 0) {
1135 Register base = x10;
1136 // There are always an even number of capture registers. A couple of
1137 // registers determine one match with two offsets.
1138 DCHECK_EQ(0, num_registers_left_on_stack % 2);
1139 __ Add(base, frame_pointer(), kFirstCaptureOnStackOffset);
1140
1141 // We can unroll the loop here, we should not unroll for less than 2
1142 // registers.
1143 static_assert(kNumRegistersToUnroll > 2);
1144 if (num_registers_left_on_stack <= kNumRegistersToUnroll) {
1145 for (int i = 0; i < num_registers_left_on_stack / 2; i++) {
1146 __ Ldp(capture_end, capture_start,
1147 MemOperand(base, -kSystemPointerSize, PostIndex));
1148 // Offsets need to be relative to the start of the string.
1149 if (mode_ == UC16) {
1150 __ Add(capture_start, input_length,
1151 Operand(capture_start, ASR, 1));
1152 __ Add(capture_end, input_length, Operand(capture_end, ASR, 1));
1153 } else {
1154 __ Add(capture_start, input_length, capture_start);
1155 __ Add(capture_end, input_length, capture_end);
1156 }
1157 // The output pointer advances for a possible global match.
1158 __ Stp(capture_start, capture_end,
1159 MemOperand(output_array(), kSystemPointerSize, PostIndex));
1160 }
1161 } else {
1162 Label loop;
1163 __ Mov(x11, num_registers_left_on_stack);
1164
1165 __ Bind(&loop);
1166 __ Ldp(capture_end, capture_start,
1167 MemOperand(base, -kSystemPointerSize, PostIndex));
1168 if (mode_ == UC16) {
1169 __ Add(capture_start, input_length, Operand(capture_start, ASR, 1));
1170 __ Add(capture_end, input_length, Operand(capture_end, ASR, 1));
1171 } else {
1172 __ Add(capture_start, input_length, capture_start);
1173 __ Add(capture_end, input_length, capture_end);
1174 }
1175 // The output pointer advances for a possible global match.
1176 __ Stp(capture_start, capture_end,
1177 MemOperand(output_array(), kSystemPointerSize, PostIndex));
1178 __ Sub(x11, x11, 2);
1179 __ Cbnz(x11, &loop);
1180 }
1181 }
1182 }
1183
1184 if (global()) {
1185 Register success_counter = w0;
1186 Register output_size = x10;
1187 // Restart matching if the regular expression is flagged as global.
1188
1189 // Increment success counter.
1190 __ Ldr(success_counter,
1191 MemOperand(frame_pointer(), kSuccessfulCapturesOffset));
1192 __ Add(success_counter, success_counter, 1);
1193 __ Str(success_counter,
1194 MemOperand(frame_pointer(), kSuccessfulCapturesOffset));
1195
1196 // Capture results have been stored, so the number of remaining global
1197 // output registers is reduced by the number of stored captures.
1198 __ Ldr(output_size,
1199 MemOperand(frame_pointer(), kNumOutputRegistersOffset));
1200 __ Sub(output_size, output_size, num_saved_registers_);
1201 // Check whether we have enough room for another set of capture results.
1202 __ Cmp(output_size, num_saved_registers_);
1203 __ B(lt, &return_w0);
1204
1205 // The output pointer is already set to the next field in the output
1206 // array.
1207 // Update output size on the frame before we restart matching.
1208 __ Str(output_size,
1209 MemOperand(frame_pointer(), kNumOutputRegistersOffset));
1210
1211 // Restore the original regexp stack pointer value (effectively, pop the
1212 // stored base pointer).
1213 PopRegExpBasePointer(backtrack_stackpointer(), x11);
1214
1215 if (global_with_zero_length_check()) {
1216 // Special case for zero-length matches.
1217 __ Cmp(current_input_offset(), first_capture_start);
1218 // Not a zero-length match, restart.
1219 __ B(ne, &load_char_start_regexp);
1220 // Offset from the end is zero if we already reached the end.
1221 __ Cbz(current_input_offset(), &return_w0);
1222 // Advance current position after a zero-length match.
1223 Label advance;
1224 __ bind(&advance);
1225 __ Add(current_input_offset(), current_input_offset(),
1226 Operand((mode_ == UC16) ? 2 : 1));
1227 if (global_unicode()) CheckNotInSurrogatePair(0, &advance);
1228 }
1229
1230 __ B(&load_char_start_regexp);
1231 } else {
1232 __ Mov(w0, SUCCESS);
1233 }
1234 }
1235
1236 if (exit_label_.is_linked()) {
1237 // Exit and return w0.
1238 __ Bind(&exit_label_);
1239 if (global()) {
1240 __ Ldr(w0, MemOperand(frame_pointer(), kSuccessfulCapturesOffset));
1241 }
1242 }
1243
1244 __ Bind(&return_w0);
1245 // Restore the original regexp stack pointer value (effectively, pop the
1246 // stored base pointer).
1247 PopRegExpBasePointer(backtrack_stackpointer(), x11);
1248
1249 __ LeaveFrame(StackFrame::IRREGEXP);
1250 __ PopCPURegList(registers_to_retain);
1251 __ Ret();
1252
1253 Label exit_with_exception;
1254 if (check_preempt_label_.is_linked()) {
1255 __ Bind(&check_preempt_label_);
1256
1257 StoreRegExpStackPointerToMemory(backtrack_stackpointer(), x10);
1258
1259 SaveLinkRegister();
1260 PushCachedRegisters();
1261 CallCheckStackGuardState(x10);
1262 // Returning from the regexp code restores the stack (sp <- fp)
1263 // so we don't need to drop the link register from it before exiting.
1264 __ Cbnz(w0, &return_w0);
1265 // Reset the cached registers.
1266 PopCachedRegisters();
1267
1268 LoadRegExpStackPointerFromMemory(backtrack_stackpointer());
1269
1270 RestoreLinkRegister();
1271 __ Ret();
1272 }
1273
1274 if (stack_overflow_label_.is_linked()) {
1275 __ Bind(&stack_overflow_label_);
1276
1277 StoreRegExpStackPointerToMemory(backtrack_stackpointer(), x10);
1278
1279 SaveLinkRegister();
1280 PushCachedRegisters();
1281 // Call GrowStack(isolate).
1282 static constexpr int kNumArguments = 1;
1283 __ Mov(x0, ExternalReference::isolate_address(isolate()));
1284 CallCFunctionFromIrregexpCode(ExternalReference::re_grow_stack(),
1285 kNumArguments);
1286 // If return nullptr, we have failed to grow the stack, and must exit with
1287 // a stack-overflow exception. Returning from the regexp code restores the
1288 // stack (sp <- fp) so we don't need to drop the link register from it
1289 // before exiting.
1290 __ Cbz(w0, &exit_with_exception);
1291 // Otherwise use return value as new stack pointer.
1292 __ Mov(backtrack_stackpointer(), x0);
1293 PopCachedRegisters();
1294 RestoreLinkRegister();
1295 __ Ret();
1296 }
1297
1298 if (exit_with_exception.is_linked()) {
1299 __ Bind(&exit_with_exception);
1300 __ Mov(w0, EXCEPTION);
1301 __ B(&return_w0);
1302 }
1303
1304 if (fallback_label_.is_linked()) {
1305 __ Bind(&fallback_label_);
1306 __ Mov(w0, FALLBACK_TO_EXPERIMENTAL);
1307 __ B(&return_w0);
1308 }
1309
1310 CodeDesc code_desc;
1311 masm_->GetCode(isolate(), &code_desc);
1312 Handle<Code> code =
1313 Factory::CodeBuilder(isolate(), code_desc, CodeKind::REGEXP)
1314 .set_self_reference(masm_->CodeObject())
1315 .set_empty_source_position_table()
1316 .Build();
1317 PROFILE(masm_->isolate(),
1318 RegExpCodeCreateEvent(Cast<AbstractCode>(code), source, flags));
1319 return Cast<HeapObject>(code);
1320}
1321
1322void RegExpMacroAssemblerARM64::GoTo(Label* to) {
1323 BranchOrBacktrack(al, to);
1324}
1325
1326void RegExpMacroAssemblerARM64::IfRegisterGE(int reg, int comparand,
1327 Label* if_ge) {
1328 Register to_compare = GetRegister(reg, w10);
1329 CompareAndBranchOrBacktrack(to_compare, comparand, ge, if_ge);
1330}
1331
1332
1333void RegExpMacroAssemblerARM64::IfRegisterLT(int reg, int comparand,
1334 Label* if_lt) {
1335 Register to_compare = GetRegister(reg, w10);
1336 CompareAndBranchOrBacktrack(to_compare, comparand, lt, if_lt);
1337}
1338
1339
1340void RegExpMacroAssemblerARM64::IfRegisterEqPos(int reg, Label* if_eq) {
1341 Register to_compare = GetRegister(reg, w10);
1342 __ Cmp(to_compare, current_input_offset());
1343 BranchOrBacktrack(eq, if_eq);
1344}
1345
1346RegExpMacroAssembler::IrregexpImplementation
1347 RegExpMacroAssemblerARM64::Implementation() {
1348 return kARM64Implementation;
1349}
1350
1351
1352void RegExpMacroAssemblerARM64::PopCurrentPosition() {
1353 Pop(current_input_offset());
1354}
1355
1356
1357void RegExpMacroAssemblerARM64::PopRegister(int register_index) {
1358 Pop(w10);
1359 StoreRegister(register_index, w10);
1360}
1361
1362
1363void RegExpMacroAssemblerARM64::PushBacktrack(Label* label) {
1364 if (label->is_bound()) {
1365 int target = label->pos();
1366 __ Mov(w10, target + InstructionStream::kHeaderSize - kHeapObjectTag);
1367 } else {
1368 __ Adr(x10, label, MacroAssembler::kAdrFar);
1369 __ Sub(x10, x10, code_pointer());
1370 if (v8_flags.debug_code) {
1371 __ Cmp(x10, kWRegMask);
1372 // The code offset has to fit in a W register.
1373 __ Check(ls, AbortReason::kOffsetOutOfRange);
1374 }
1375 }
1376 Push(w10);
1377 CheckStackLimit();
1378}
1379
1380
1381void RegExpMacroAssemblerARM64::PushCurrentPosition() {
1382 Push(current_input_offset());
1383 CheckStackLimit();
1384}
1385
1386
1387void RegExpMacroAssemblerARM64::PushRegister(int register_index,
1388 StackCheckFlag check_stack_limit) {
1389 Register to_push = GetRegister(register_index, w10);
1390 Push(to_push);
1391 if (check_stack_limit) {
1392 CheckStackLimit();
1393 } else if (V8_UNLIKELY(v8_flags.slow_debug_code)) {
1394 AssertAboveStackLimitMinusSlack();
1395 }
1396}
1397
1398
1399void RegExpMacroAssemblerARM64::ReadCurrentPositionFromRegister(int reg) {
1400 RegisterState register_state = GetRegisterState(reg);
1401 switch (register_state) {
1402 case STACKED:
1403 __ Ldr(current_input_offset(), register_location(reg));
1404 break;
1405 case CACHED_LSW:
1406 __ Mov(current_input_offset(), GetCachedRegister(reg).W());
1407 break;
1408 case CACHED_MSW:
1409 __ Lsr(current_input_offset().X(), GetCachedRegister(reg),
1410 kWRegSizeInBits);
1411 break;
1412 default:
1413 UNREACHABLE();
1414 }
1415}
1416
1417void RegExpMacroAssemblerARM64::WriteStackPointerToRegister(int reg) {
1418 ExternalReference ref =
1419 ExternalReference::address_of_regexp_stack_memory_top_address(isolate());
1420 __ Mov(x10, ref);
1421 __ Ldr(x10, MemOperand(x10));
1422 __ Sub(x10, backtrack_stackpointer(), x10);
1423 if (v8_flags.debug_code) {
1424 __ Cmp(x10, Operand(w10, SXTW));
1425 // The stack offset needs to fit in a W register.
1426 __ Check(eq, AbortReason::kOffsetOutOfRange);
1427 }
1428 StoreRegister(reg, w10);
1429}
1430
1431void RegExpMacroAssemblerARM64::ReadStackPointerFromRegister(int reg) {
1432 ExternalReference ref =
1433 ExternalReference::address_of_regexp_stack_memory_top_address(isolate());
1434 Register read_from = GetRegister(reg, w10);
1435 __ Mov(x11, ref);
1436 __ Ldr(x11, MemOperand(x11));
1437 __ Add(backtrack_stackpointer(), x11, Operand(read_from, SXTW));
1438}
1439
1440void RegExpMacroAssemblerARM64::SetCurrentPositionFromEnd(int by) {
1441 Label after_position;
1442 __ Cmp(current_input_offset(), -by * char_size());
1443 __ B(ge, &after_position);
1444 __ Mov(current_input_offset(), -by * char_size());
1445 // On RegExp code entry (where this operation is used), the character before
1446 // the current position is expected to be already loaded.
1447 // We have advanced the position, so it's safe to read backwards.
1448 LoadCurrentCharacterUnchecked(-1, 1);
1449 __ Bind(&after_position);
1450}
1451
1452
1453void RegExpMacroAssemblerARM64::SetRegister(int register_index, int to) {
1454 DCHECK(register_index >= num_saved_registers_); // Reserved for positions!
1455 Register set_to = wzr;
1456 if (to != 0) {
1457 set_to = w10;
1458 __ Mov(set_to, to);
1459 }
1460 StoreRegister(register_index, set_to);
1461}
1462
1463
1464bool RegExpMacroAssemblerARM64::Succeed() {
1465 __ B(&success_label_);
1466 return global();
1467}
1468
1469
1470void RegExpMacroAssemblerARM64::WriteCurrentPositionToRegister(int reg,
1471 int cp_offset) {
1472 Register position = current_input_offset();
1473 if (cp_offset != 0) {
1474 position = w10;
1475 __ Add(position, current_input_offset(), cp_offset * char_size());
1476 }
1477 StoreRegister(reg, position);
1478}
1479
1480
1481void RegExpMacroAssemblerARM64::ClearRegisters(int reg_from, int reg_to) {
1482 DCHECK(reg_from <= reg_to);
1483 int num_registers = reg_to - reg_from + 1;
1484
1485 // If the first capture register is cached in a hardware register but not
1486 // aligned on a 64-bit one, we need to clear the first one specifically.
1487 if ((reg_from < kNumCachedRegisters) && ((reg_from % 2) != 0)) {
1488 StoreRegister(reg_from, string_start_minus_one());
1489 num_registers--;
1490 reg_from++;
1491 }
1492
1493 // Clear cached registers in pairs as far as possible.
1494 while ((num_registers >= 2) && (reg_from < kNumCachedRegisters)) {
1495 DCHECK(GetRegisterState(reg_from) == CACHED_LSW);
1496 __ Mov(GetCachedRegister(reg_from), twice_non_position_value());
1497 reg_from += 2;
1498 num_registers -= 2;
1499 }
1500
1501 if ((num_registers % 2) == 1) {
1502 StoreRegister(reg_from, string_start_minus_one());
1503 num_registers--;
1504 reg_from++;
1505 }
1506
1507 if (num_registers > 0) {
1508 // If there are some remaining registers, they are stored on the stack.
1509 DCHECK_LE(kNumCachedRegisters, reg_from);
1510
1511 // Move down the indexes of the registers on stack to get the correct offset
1512 // in memory.
1513 reg_from -= kNumCachedRegisters;
1514 reg_to -= kNumCachedRegisters;
1515 // We should not unroll the loop for less than 2 registers.
1516 static_assert(kNumRegistersToUnroll > 2);
1517 // We position the base pointer to (reg_from + 1).
1518 int base_offset =
1519 kFirstRegisterOnStackOffset - kWRegSize - (kWRegSize * reg_from);
1520 if (num_registers > kNumRegistersToUnroll) {
1521 Register base = x10;
1522 __ Add(base, frame_pointer(), base_offset);
1523
1524 Label loop;
1525 __ Mov(x11, num_registers);
1526 __ Bind(&loop);
1527 __ Str(twice_non_position_value(),
1528 MemOperand(base, -kSystemPointerSize, PostIndex));
1529 __ Sub(x11, x11, 2);
1530 __ Cbnz(x11, &loop);
1531 } else {
1532 for (int i = reg_from; i <= reg_to; i += 2) {
1533 __ Str(twice_non_position_value(),
1534 MemOperand(frame_pointer(), base_offset));
1535 base_offset -= kWRegSize * 2;
1536 }
1537 }
1538 }
1539}
1540
1541// Helper function for reading a value out of a stack frame.
1542template <typename T>
1543static T& frame_entry(Address re_frame, int frame_offset) {
1544 return *reinterpret_cast<T*>(re_frame + frame_offset);
1545}
1546
1547
1548template <typename T>
1549static T* frame_entry_address(Address re_frame, int frame_offset) {
1550 return reinterpret_cast<T*>(re_frame + frame_offset);
1551}
1552
1553int RegExpMacroAssemblerARM64::CheckStackGuardState(
1554 Address* return_address, Address raw_code, Address re_frame,
1555 int start_index, const uint8_t** input_start, const uint8_t** input_end,
1556 uintptr_t extra_space) {
1557 Tagged<InstructionStream> re_code =
1558 Cast<InstructionStream>(Tagged<Object>(raw_code));
1559 return NativeRegExpMacroAssembler::CheckStackGuardState(
1560 frame_entry<Isolate*>(re_frame, kIsolateOffset), start_index,
1561 static_cast<RegExp::CallOrigin>(
1562 frame_entry<int>(re_frame, kDirectCallOffset)),
1563 return_address, re_code,
1564 frame_entry_address<Address>(re_frame, kInputStringOffset), input_start,
1565 input_end, extra_space);
1566}
1567
1568void RegExpMacroAssemblerARM64::CheckPosition(int cp_offset,
1569 Label* on_outside_input) {
1570 if (cp_offset >= 0) {
1571 CompareAndBranchOrBacktrack(current_input_offset(),
1572 -cp_offset * char_size(), ge, on_outside_input);
1573 } else {
1574 __ Add(w12, current_input_offset(), Operand(cp_offset * char_size()));
1575 __ Cmp(w12, string_start_minus_one());
1576 BranchOrBacktrack(le, on_outside_input);
1577 }
1578}
1579
1580
1581// Private methods:
1582
1583void RegExpMacroAssemblerARM64::CallCheckStackGuardState(Register scratch,
1584 Operand extra_space) {
1585 DCHECK(!isolate()->IsGeneratingEmbeddedBuiltins());
1586 DCHECK(!masm_->options().isolate_independent_code);
1587
1588 // Allocate space on the stack to store the return address. The
1589 // CheckStackGuardState C++ function will override it if the code
1590 // moved. Allocate extra space for 2 arguments passed by pointers.
1591 // AAPCS64 requires the stack to be 16 byte aligned.
1592 int alignment = masm_->ActivationFrameAlignment();
1593 DCHECK_EQ(alignment % 16, 0);
1594 int align_mask = (alignment / kXRegSize) - 1;
1595 int xreg_to_claim = (3 + align_mask) & ~align_mask;
1596
1597 __ Claim(xreg_to_claim);
1598
1599 __ Mov(x6, extra_space);
1600 // CheckStackGuardState needs the end and start addresses of the input string.
1601 __ Poke(input_end(), 2 * kSystemPointerSize);
1602 __ Add(x5, sp, 2 * kSystemPointerSize);
1603 __ Poke(input_start(), kSystemPointerSize);
1604 __ Add(x4, sp, kSystemPointerSize);
1605
1606 __ Mov(w3, start_offset());
1607 // RegExp code frame pointer.
1608 __ Mov(x2, frame_pointer());
1609 // InstructionStream of self.
1610 __ Mov(x1, Operand(masm_->CodeObject()));
1611
1612 // We need to pass a pointer to the return address as first argument.
1613 // DirectCEntry will place the return address on the stack before calling so
1614 // the stack pointer will point to it.
1615 __ Mov(x0, sp);
1616
1617 DCHECK_EQ(scratch, x10);
1618 ExternalReference check_stack_guard_state =
1619 ExternalReference::re_check_stack_guard_state();
1620 __ Mov(scratch, check_stack_guard_state);
1621
1622 __ CallBuiltin(Builtin::kDirectCEntry);
1623
1624 // The input string may have been moved in memory, we need to reload it.
1625 __ Peek(input_start(), kSystemPointerSize);
1626 __ Peek(input_end(), 2 * kSystemPointerSize);
1627
1628 __ Drop(xreg_to_claim);
1629
1630 // Reload the InstructionStream pointer.
1631 __ Mov(code_pointer(), Operand(masm_->CodeObject()));
1632}
1633
1634void RegExpMacroAssemblerARM64::BranchOrBacktrack(Condition condition,
1635 Label* to) {
1636 if (condition == al) { // Unconditional.
1637 if (to == nullptr) {
1638 Backtrack();
1639 return;
1640 }
1641 __ B(to);
1642 return;
1643 }
1644 if (to == nullptr) {
1645 to = &backtrack_label_;
1646 }
1647 __ B(condition, to);
1648}
1649
1650void RegExpMacroAssemblerARM64::CompareAndBranchOrBacktrack(Register reg,
1651 int immediate,
1652 Condition condition,
1653 Label* to) {
1654 if ((immediate == 0) && ((condition == eq) || (condition == ne))) {
1655 if (to == nullptr) {
1656 to = &backtrack_label_;
1657 }
1658 if (condition == eq) {
1659 __ Cbz(reg, to);
1660 } else {
1661 __ Cbnz(reg, to);
1662 }
1663 } else {
1664 __ Cmp(reg, immediate);
1665 BranchOrBacktrack(condition, to);
1666 }
1667}
1668
1669void RegExpMacroAssemblerARM64::CallCFunctionFromIrregexpCode(
1670 ExternalReference function, int num_arguments) {
1671 // Irregexp code must not set fast_c_call_caller_fp and fast_c_call_caller_pc
1672 // since
1673 //
1674 // 1. it may itself have been called using CallCFunction and nested calls are
1675 // unsupported, and
1676 // 2. it may itself have been called directly from C where the frame pointer
1677 // might not be set (-fomit-frame-pointer), and thus frame iteration would
1678 // fail.
1679 //
1680 // See also: crbug.com/v8/12670#c17.
1681 __ CallCFunction(function, num_arguments, SetIsolateDataSlots::kNo);
1682}
1683
1684void RegExpMacroAssemblerARM64::CheckPreemption() {
1685 // Check for preemption.
1686 ExternalReference stack_limit =
1687 ExternalReference::address_of_jslimit(isolate());
1688 __ Mov(x10, stack_limit);
1689 __ Ldr(x10, MemOperand(x10));
1690 __ Cmp(sp, x10);
1691 CallIf(&check_preempt_label_, ls);
1692}
1693
1694
1695void RegExpMacroAssemblerARM64::CheckStackLimit() {
1696 ExternalReference stack_limit =
1697 ExternalReference::address_of_regexp_stack_limit_address(isolate());
1698 __ Mov(x10, stack_limit);
1699 __ Ldr(x10, MemOperand(x10));
1700 __ Cmp(backtrack_stackpointer(), x10);
1701 CallIf(&stack_overflow_label_, ls);
1702}
1703
1704void RegExpMacroAssemblerARM64::AssertAboveStackLimitMinusSlack() {
1705 DCHECK(v8_flags.slow_debug_code);
1706 Label no_stack_overflow;
1707 ASM_CODE_COMMENT_STRING(masm_.get(), "AssertAboveStackLimitMinusSlack");
1708 auto l = ExternalReference::address_of_regexp_stack_limit_address(isolate());
1709 __ Mov(x10, l);
1710 __ Ldr(x10, MemOperand(x10));
1711 __ Sub(x10, x10, RegExpStack::kStackLimitSlackSize);
1712 __ Cmp(backtrack_stackpointer(), x10);
1713 __ B(hi, &no_stack_overflow);
1714 __ DebugBreak();
1715 __ bind(&no_stack_overflow);
1716}
1717
1718void RegExpMacroAssemblerARM64::Push(Register source) {
1719 DCHECK(source.Is32Bits());
1720 DCHECK_NE(source, backtrack_stackpointer());
1721 __ Str(source,
1722 MemOperand(backtrack_stackpointer(),
1723 -static_cast<int>(kWRegSize),
1724 PreIndex));
1725}
1726
1727
1728void RegExpMacroAssemblerARM64::Pop(Register target) {
1729 DCHECK(target.Is32Bits());
1730 DCHECK_NE(target, backtrack_stackpointer());
1731 __ Ldr(target,
1732 MemOperand(backtrack_stackpointer(), kWRegSize, PostIndex));
1733}
1734
1735
1736Register RegExpMacroAssemblerARM64::GetCachedRegister(int register_index) {
1737 DCHECK_GT(kNumCachedRegisters, register_index);
1738 return Register::Create(register_index / 2, kXRegSizeInBits);
1739}
1740
1741
1742Register RegExpMacroAssemblerARM64::GetRegister(int register_index,
1743 Register maybe_result) {
1744 DCHECK(maybe_result.Is32Bits());
1745 DCHECK_LE(0, register_index);
1746 if (num_registers_ <= register_index) {
1747 num_registers_ = register_index + 1;
1748 }
1750 RegisterState register_state = GetRegisterState(register_index);
1751 switch (register_state) {
1752 case STACKED:
1753 __ Ldr(maybe_result, register_location(register_index));
1754 result = maybe_result;
1755 break;
1756 case CACHED_LSW:
1757 result = GetCachedRegister(register_index).W();
1758 break;
1759 case CACHED_MSW:
1760 __ Lsr(maybe_result.X(), GetCachedRegister(register_index),
1761 kWRegSizeInBits);
1762 result = maybe_result;
1763 break;
1764 default:
1765 UNREACHABLE();
1766 }
1767 DCHECK(result.Is32Bits());
1768 return result;
1769}
1770
1771
1772void RegExpMacroAssemblerARM64::StoreRegister(int register_index,
1773 Register source) {
1774 DCHECK(source.Is32Bits());
1775 DCHECK_LE(0, register_index);
1776 if (num_registers_ <= register_index) {
1777 num_registers_ = register_index + 1;
1778 }
1779
1780 RegisterState register_state = GetRegisterState(register_index);
1781 switch (register_state) {
1782 case STACKED:
1783 __ Str(source, register_location(register_index));
1784 break;
1785 case CACHED_LSW: {
1786 Register cached_register = GetCachedRegister(register_index);
1787 if (source != cached_register.W()) {
1788 __ Bfi(cached_register, source.X(), 0, kWRegSizeInBits);
1789 }
1790 break;
1791 }
1792 case CACHED_MSW: {
1793 Register cached_register = GetCachedRegister(register_index);
1794 __ Bfi(cached_register, source.X(), kWRegSizeInBits, kWRegSizeInBits);
1795 break;
1796 }
1797 default:
1798 UNREACHABLE();
1799 }
1800}
1801
1802
1803void RegExpMacroAssemblerARM64::CallIf(Label* to, Condition condition) {
1804 Label skip_call;
1805 if (condition != al) __ B(&skip_call, NegateCondition(condition));
1806 __ Bl(to);
1807 __ Bind(&skip_call);
1808}
1809
1810
1811void RegExpMacroAssemblerARM64::RestoreLinkRegister() {
1812 // TODO(v8:10026): Remove when we stop compacting for code objects that are
1813 // active on the call stack.
1814 __ Pop<MacroAssembler::kAuthLR>(padreg, lr);
1815 __ Add(lr, lr, Operand(masm_->CodeObject()));
1816}
1817
1818
1819void RegExpMacroAssemblerARM64::SaveLinkRegister() {
1820 __ Sub(lr, lr, Operand(masm_->CodeObject()));
1821 __ Push<MacroAssembler::kSignLR>(lr, padreg);
1822}
1823
1824
1825MemOperand RegExpMacroAssemblerARM64::register_location(int register_index) {
1826 DCHECK(register_index < (1<<30));
1827 DCHECK_LE(kNumCachedRegisters, register_index);
1828 if (num_registers_ <= register_index) {
1829 num_registers_ = register_index + 1;
1830 }
1831 register_index -= kNumCachedRegisters;
1832 int offset = kFirstRegisterOnStackOffset - register_index * kWRegSize;
1833 return MemOperand(frame_pointer(), offset);
1834}
1835
1836MemOperand RegExpMacroAssemblerARM64::capture_location(int register_index,
1837 Register scratch) {
1838 DCHECK(register_index < (1<<30));
1839 DCHECK(register_index < num_saved_registers_);
1840 DCHECK_LE(kNumCachedRegisters, register_index);
1841 DCHECK_EQ(register_index % 2, 0);
1842 register_index -= kNumCachedRegisters;
1843 int offset = kFirstCaptureOnStackOffset - register_index * kWRegSize;
1844 // capture_location is used with Stp instructions to load/store 2 registers.
1845 // The immediate field in the encoding is limited to 7 bits (signed).
1846 if (is_int7(offset)) {
1847 return MemOperand(frame_pointer(), offset);
1848 } else {
1849 __ Add(scratch, frame_pointer(), offset);
1850 return MemOperand(scratch);
1851 }
1852}
1853
1854void RegExpMacroAssemblerARM64::LoadCurrentCharacterUnchecked(int cp_offset,
1855 int characters) {
1856 Register offset = current_input_offset();
1857
1858 // The ldr, str, ldrh, strh instructions can do unaligned accesses, if the CPU
1859 // and the operating system running on the target allow it.
1860 // If unaligned load/stores are not supported then this function must only
1861 // be used to load a single character at a time.
1862
1863 // ARMv8 supports unaligned accesses but V8 or the kernel can decide to
1864 // disable it.
1865 // TODO(pielan): See whether or not we should disable unaligned accesses.
1866 if (!CanReadUnaligned()) {
1867 DCHECK_EQ(1, characters);
1868 }
1869
1870 if (cp_offset != 0) {
1871 if (v8_flags.debug_code) {
1872 __ Mov(x10, cp_offset * char_size());
1873 __ Add(x10, x10, Operand(current_input_offset(), SXTW));
1874 __ Cmp(x10, Operand(w10, SXTW));
1875 // The offset needs to fit in a W register.
1876 __ Check(eq, AbortReason::kOffsetOutOfRange);
1877 } else {
1878 __ Add(w10, current_input_offset(), cp_offset * char_size());
1879 }
1880 offset = w10;
1881 }
1882
1883 if (mode_ == LATIN1) {
1884 if (characters == 4) {
1885 __ Ldr(current_character(), MemOperand(input_end(), offset, SXTW));
1886 } else if (characters == 2) {
1887 __ Ldrh(current_character(), MemOperand(input_end(), offset, SXTW));
1888 } else {
1889 DCHECK_EQ(1, characters);
1890 __ Ldrb(current_character(), MemOperand(input_end(), offset, SXTW));
1891 }
1892 } else {
1893 DCHECK(mode_ == UC16);
1894 if (characters == 2) {
1895 __ Ldr(current_character(), MemOperand(input_end(), offset, SXTW));
1896 } else {
1897 DCHECK_EQ(1, characters);
1898 __ Ldrh(current_character(), MemOperand(input_end(), offset, SXTW));
1899 }
1900 }
1901}
1902
1903} // namespace internal
1904} // namespace v8
1905
1906#undef __
1907
1908#endif // V8_TARGET_ARCH_ARM64
friend Zone
Definition asm-types.cc:195
RegExpMacroAssemblerARM64(Isolate *isolate, Zone *zone, Mode mode, int registers_to_save)
#define PROFILE(the_isolate, Call)
Definition code-events.h:59
RecordWriteMode const mode_
const CodeDesc * code_desc
#define ASM_CODE_COMMENT_STRING(asm,...)
Definition assembler.h:618
Label label
too high values may cause the compiler to set high thresholds for inlining to as much as possible avoid inlined allocation of objects that cannot escape trace load stores from virtual maglev objects use TurboFan fast string builder analyze liveness of environment slots and zap dead values trace TurboFan load elimination emit data about basic block usage in builtins to this enable builtin reordering when run mksnapshot flag for emit warnings when applying builtin profile data verify register allocation in TurboFan randomly schedule instructions to stress dependency tracking enable store store elimination in TurboFan rewrite far to near simulate GC compiler thread race related to allow float parameters to be passed in simulator mode JS Wasm Run additional turbo_optimize_inlined_js_wasm_wrappers enable experimental feedback collection in generic lowering enable Turboshaft s WasmLoadElimination enable Turboshaft s low level load elimination for JS enable Turboshaft s escape analysis for string concatenation use enable Turbolev features that we want to ship in the not too far future trace individual Turboshaft reduction steps trace intermediate Turboshaft reduction steps invocation count threshold for early optimization Enables optimizations which favor memory size over execution speed Enables sampling allocation profiler with X as a sample interval min size of a semi the new space consists of two semi spaces max size of the Collect garbage after Collect garbage after keeps maps alive for< n > old space garbage collections print one detailed trace line in allocation gc speed threshold for starting incremental marking via a task in percent of available threshold for starting incremental marking immediately in percent of available Use a single schedule for determining a marking schedule between JS and C objects schedules the minor GC task with kUserVisible priority max worker number of concurrent for NumberOfWorkerThreads start background threads that allocate memory concurrent_array_buffer_sweeping use parallel threads to clear weak refs in the atomic pause trace progress of the incremental marking trace object counts and memory usage report a tick only when allocated zone memory changes by this amount TracingFlags::gc_stats TracingFlags::gc_stats track native contexts that are expected to be garbage collected verify heap pointers before and after GC memory reducer runs GC with ReduceMemoryFootprint flag Maximum number of memory reducer GCs scheduled Old gen GC speed is computed directly from gc tracer counters Perform compaction on full GCs based on V8 s default heuristics Perform compaction on every full GC Perform code space compaction when finalizing a full GC with stack Stress GC compaction to flush out bugs with moving objects flush of baseline code when it has not been executed recently Use time base code flushing instead of age Use a progress bar to scan large objects in increments when incremental marking is active force incremental marking for small heaps and run it more often force marking at random points between and X(inclusive) percent " "of the regular marking start limit") DEFINE_INT(stress_scavenge
Isolate * isolate
int32_t offset
ZoneVector< RpoNumber > & result
LiftoffRegister reg
int position
Definition liveedit.cc:290
uint32_t const mask
MaglevAssembler *const masm_
STL namespace.
void Add(RWDigits Z, Digits X, Digits Y)
UntaggedUnion< WordPtr, Code, JSFunction, Word32 > CallTarget
Definition index.h:547
base::PointerWithPayload< void, RegisterStateFlags, 2 > RegisterState
void And(LiftoffAssembler *lasm, Register dst, Register lhs, Register rhs)
void Sub(LiftoffAssembler *lasm, Register dst, Register lhs, Register rhs)
constexpr int W
constexpr int B
constexpr int kSystemPointerSize
Definition globals.h:410
std::unique_ptr< AssemblerBuffer > NewAssemblerBuffer(int size)
Definition assembler.cc:167
Condition NegateCondition(Condition cond)
V8_EXPORT_PRIVATE FlagValues v8_flags
constexpr int kWRegSize
Register ReassignRegister(Register &source)
constexpr int kXRegSize
constexpr Register NoReg
#define kCalleeSaved
#define UNREACHABLE()
Definition logging.h:67
#define DCHECK_LE(v1, v2)
Definition logging.h:490
#define DCHECK_NE(v1, v2)
Definition logging.h:486
#define DCHECK_GE(v1, v2)
Definition logging.h:488
#define DCHECK(condition)
Definition logging.h:482
#define DCHECK_EQ(v1, v2)
Definition logging.h:485
#define DCHECK_GT(v1, v2)
Definition logging.h:487
#define OFFSET_OF_DATA_START(Type)
#define V8_UNLIKELY(condition)
Definition v8config.h:660