v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
regexp-macro-assembler-mips64.cc
Go to the documentation of this file.
1// Copyright 2012 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_MIPS64
6
8
11#include "src/heap/factory.h"
12#include "src/logging/log.h"
16
17namespace v8 {
18namespace internal {
19
20/* clang-format off
21 *
22 * This assembler uses the following register assignment convention
23 * - s0 : Unused.
24 * - s1 : Pointer to current InstructionStream object including heap object tag.
25 * - s2 : Current position in input, as negative offset from end of string.
26 * Please notice that this is the byte offset, not the character offset!
27 * - s5 : Currently loaded character. Must be loaded using
28 * LoadCurrentCharacter before using any of the dispatch methods.
29 * - s6 : Points to tip of backtrack stack
30 * - s7 : End of input (points to byte after last character in input).
31 * - fp : Frame pointer. Used to access arguments, local variables and
32 * RegExp registers.
33 * - sp : Points to tip of C stack.
34 *
35 * The remaining registers are free for computations.
36 * Each call to a public method should retain this convention.
37 *
38 * TODO(plind): O32 documented here with intent of having single 32/64 codebase
39 * in the future.
40 *
41 * The O32 stack will have the following structure:
42 *
43 * - fp[72] Isolate* isolate (address of the current isolate)
44 * - fp[68] direct_call (if 1, direct call from JavaScript code,
45 * if 0, call through the runtime system).
46 * - fp[64] stack_area_base (High end of the memory area to use as
47 * backtracking stack).
48 * - fp[60] capture array size (may fit multiple sets of matches)
49 * - fp[44..59] MIPS O32 four argument slots
50 * - fp[40] int* capture_array (int[num_saved_registers_], for output).
51 * --- sp when called ---
52 * - fp[36] return address (lr).
53 * - fp[32] old frame pointer (r11).
54 * - fp[0..31] backup of registers s0..s7.
55 * --- frame pointer ----
56 * - fp[-4] end of input (address of end of string).
57 * - fp[-8] start of input (address of first character in string).
58 * - fp[-12] start index (character index of start).
59 * - fp[-16] void* input_string (location of a handle containing the string).
60 * - fp[-20] success counter (only for global regexps to count matches).
61 * - fp[-24] Offset of location before start of input (effectively character
62 * string start - 1). Used to initialize capture registers to a
63 * non-position.
64 * - fp[-28] At start (if 1, we are starting at the start of the
65 * string, otherwise 0)
66 * - fp[-32] register 0 (Only positions must be stored in the first
67 * - register 1 num_saved_registers_ registers)
68 * - ...
69 * - register num_registers-1
70 * --- sp ---
71 *
72 *
73 * The N64 stack will have the following structure:
74 *
75 * - fp[80] Isolate* isolate (address of the current isolate) kIsolate
76 * kStackFrameHeader
77 * --- sp when called ---
78 * - fp[72] ra Return from RegExp code (ra). kReturnAddress
79 * - fp[64] s9, old-fp Old fp, callee saved(s9).
80 * - fp[0..63] s0..s7 Callee-saved registers s0..s7.
81 * --- frame pointer ----
82 * - fp[-8] frame marker
83 * - fp[-16] direct_call (1 = direct call from JS, 0 = from runtime) kDirectCallOffset
84 * - fp[-24] capture array size (may fit multiple sets of matches) kNumOutputRegistersOffset
85 * - fp[-32] int* capture_array (int[num_saved_registers_], for output). kRegisterOutputOffset
86 * - fp[-40] end of input (address of end of string). kInputEndOffset
87 * - fp[-48] start of input (address of first character in string). kInputStartOffset
88 * - fp[-56] start index (character index of start). kStartIndexOffset
89 * - fp[-64] void* input_string (location of a handle containing the string). kInputStringOffset
90 * - fp[-72] success counter (only for global regexps to count matches). kSuccessfulCapturesOffset
91 * - fp[-80] Offset of location before start of input (effectively character kStringStartMinusOneOffset
92 * position -1). Used to initialize capture registers to a
93 * non-position.
94 * --------- The following output registers are 32-bit values. ---------
95 * - fp[-88] register 0 (Only positions must be stored in the first kRegisterZero
96 * - register 1 num_saved_registers_ registers)
97 * - ...
98 * - register num_registers-1
99 * --- sp ---
100 *
101 * The first num_saved_registers_ registers are initialized to point to
102 * "character -1" in the string (i.e., char_size() bytes before the first
103 * character of the string). The remaining registers start out as garbage.
104 *
105 * The data up to the return address must be placed there by the calling
106 * code and the remaining arguments are passed in registers, e.g. by calling the
107 * code entry as cast to a function with the signature:
108 * int (*match)(String input_string,
109 * int start_index,
110 * Address start,
111 * Address end,
112 * int* capture_output_array,
113 * int num_capture_registers,
114 * bool direct_call = false,
115 * Isolate* isolate);
116 * The call is performed by NativeRegExpMacroAssembler::Execute()
117 * (in regexp-macro-assembler.cc) via the GeneratedCode wrapper.
118 *
119 * clang-format on
120 */
121
122#define __ ACCESS_MASM(masm_)
123
125 Mode mode,
126 int registers_to_save)
127 : NativeRegExpMacroAssembler(isolate, zone),
128 masm_(std::make_unique<MacroAssembler>(
129 isolate, CodeObjectRequired::kYes,
130 NewAssemblerBuffer(kInitialBufferSize))),
131 no_root_array_scope_(masm_.get()),
132 mode_(mode),
133 num_registers_(registers_to_save),
134 num_saved_registers_(registers_to_save),
135 entry_label_(),
136 start_label_(),
137 success_label_(),
138 backtrack_label_(),
139 exit_label_(),
140 internal_failure_label_() {
141 DCHECK_EQ(0, registers_to_save % 2);
142 __ jmp(&entry_label_); // We'll write the entry code later.
143 // If the code gets too big or corrupted, an internal exception will be
144 // raised, and we will exit right away.
145 __ bind(&internal_failure_label_);
146 __ li(v0, Operand(FAILURE));
147 __ Ret();
148 __ bind(&start_label_); // And then continue from here.
149}
150
151RegExpMacroAssemblerMIPS::~RegExpMacroAssemblerMIPS() {
152 // Unuse labels in case we throw away the assembler without calling GetCode.
153 entry_label_.Unuse();
154 start_label_.Unuse();
155 success_label_.Unuse();
156 backtrack_label_.Unuse();
157 exit_label_.Unuse();
158 check_preempt_label_.Unuse();
159 stack_overflow_label_.Unuse();
160 internal_failure_label_.Unuse();
161 fallback_label_.Unuse();
162}
163
164int RegExpMacroAssemblerMIPS::stack_limit_slack_slot_count() {
165 return RegExpStack::kStackLimitSlackSlotCount;
166}
167
168void RegExpMacroAssemblerMIPS::AdvanceCurrentPosition(int by) {
169 if (by != 0) {
170 __ Daddu(current_input_offset(),
171 current_input_offset(), Operand(by * char_size()));
172 }
173}
174
175
176void RegExpMacroAssemblerMIPS::AdvanceRegister(int reg, int by) {
177 DCHECK_LE(0, reg);
178 DCHECK_GT(num_registers_, reg);
179 if (by != 0) {
180 __ Ld(a0, register_location(reg));
181 __ Daddu(a0, a0, Operand(by));
182 __ Sd(a0, register_location(reg));
183 }
184}
185
186
187void RegExpMacroAssemblerMIPS::Backtrack() {
188 CheckPreemption();
189 if (has_backtrack_limit()) {
190 Label next;
191 __ Ld(a0, MemOperand(frame_pointer(), kBacktrackCountOffset));
192 __ Daddu(a0, a0, Operand(1));
193 __ Sd(a0, MemOperand(frame_pointer(), kBacktrackCountOffset));
194 __ Branch(&next, ne, a0, Operand(backtrack_limit()));
195
196 // Backtrack limit exceeded.
197 if (can_fallback()) {
198 __ jmp(&fallback_label_);
199 } else {
200 // Can't fallback, so we treat it as a failed match.
201 Fail();
202 }
203
204 __ bind(&next);
205 }
206 // Pop Code offset from backtrack stack, add Code and jump to location.
207 Pop(a0);
208 __ Daddu(a0, a0, code_pointer());
209 __ Jump(a0);
210}
211
212
213void RegExpMacroAssemblerMIPS::Bind(Label* label) {
214 __ bind(label);
215}
216
217
218void RegExpMacroAssemblerMIPS::CheckCharacter(uint32_t c, Label* on_equal) {
219 BranchOrBacktrack(on_equal, eq, current_character(), Operand(c));
220}
221
222void RegExpMacroAssemblerMIPS::CheckCharacterGT(base::uc16 limit,
223 Label* on_greater) {
224 BranchOrBacktrack(on_greater, gt, current_character(), Operand(limit));
225}
226
227void RegExpMacroAssemblerMIPS::CheckAtStart(int cp_offset, Label* on_at_start) {
228 __ Ld(a1, MemOperand(frame_pointer(), kStringStartMinusOneOffset));
229 __ Daddu(a0, current_input_offset(),
230 Operand(-char_size() + cp_offset * char_size()));
231 BranchOrBacktrack(on_at_start, eq, a0, Operand(a1));
232}
233
234
235void RegExpMacroAssemblerMIPS::CheckNotAtStart(int cp_offset,
236 Label* on_not_at_start) {
237 __ Ld(a1, MemOperand(frame_pointer(), kStringStartMinusOneOffset));
238 __ Daddu(a0, current_input_offset(),
239 Operand(-char_size() + cp_offset * char_size()));
240 BranchOrBacktrack(on_not_at_start, ne, a0, Operand(a1));
241}
242
243void RegExpMacroAssemblerMIPS::CheckCharacterLT(base::uc16 limit,
244 Label* on_less) {
245 BranchOrBacktrack(on_less, lt, current_character(), Operand(limit));
246}
247
248void RegExpMacroAssemblerMIPS::CheckGreedyLoop(Label* on_equal) {
249 Label backtrack_non_equal;
250 __ Lw(a0, MemOperand(backtrack_stackpointer(), 0));
251 __ Branch(&backtrack_non_equal, ne, current_input_offset(), Operand(a0));
252 __ Daddu(backtrack_stackpointer(),
253 backtrack_stackpointer(),
254 Operand(kIntSize));
255 __ bind(&backtrack_non_equal);
256 BranchOrBacktrack(on_equal, eq, current_input_offset(), Operand(a0));
257}
258
259void RegExpMacroAssemblerMIPS::CheckNotBackReferenceIgnoreCase(
260 int start_reg, bool read_backward, bool unicode, Label* on_no_match) {
261 Label fallthrough;
262 __ Ld(a0, register_location(start_reg)); // Index of start of capture.
263 __ Ld(a1, register_location(start_reg + 1)); // Index of end of capture.
264 __ Dsubu(a1, a1, a0); // Length of capture.
265
266 // At this point, the capture registers are either both set or both cleared.
267 // If the capture length is zero, then the capture is either empty or cleared.
268 // Fall through in both cases.
269 __ Branch(&fallthrough, eq, a1, Operand(zero_reg));
270
271 if (read_backward) {
272 __ Ld(t1, MemOperand(frame_pointer(), kStringStartMinusOneOffset));
273 __ Daddu(t1, t1, a1);
274 BranchOrBacktrack(on_no_match, le, current_input_offset(), Operand(t1));
275 } else {
276 __ Daddu(t1, a1, current_input_offset());
277 // Check that there are enough characters left in the input.
278 BranchOrBacktrack(on_no_match, gt, t1, Operand(zero_reg));
279 }
280
281 if (mode_ == LATIN1) {
282 Label success;
283 Label fail;
284 Label loop_check;
285
286 // a0 - offset of start of capture.
287 // a1 - length of capture.
288 __ Daddu(a0, a0, Operand(end_of_input_address()));
289 __ Daddu(a2, end_of_input_address(), Operand(current_input_offset()));
290 if (read_backward) {
291 __ Dsubu(a2, a2, Operand(a1));
292 }
293 __ Daddu(a1, a0, Operand(a1));
294
295 // a0 - Address of start of capture.
296 // a1 - Address of end of capture.
297 // a2 - Address of current input position.
298
299 Label loop;
300 __ bind(&loop);
301 __ Lbu(a3, MemOperand(a0, 0));
302 __ daddiu(a0, a0, char_size());
303 __ Lbu(a4, MemOperand(a2, 0));
304 __ daddiu(a2, a2, char_size());
305
306 __ Branch(&loop_check, eq, a4, Operand(a3));
307
308 // Mismatch, try case-insensitive match (converting letters to lower-case).
309 __ Or(a3, a3, Operand(0x20)); // Convert capture character to lower-case.
310 __ Or(a4, a4, Operand(0x20)); // Also convert input character.
311 __ Branch(&fail, ne, a4, Operand(a3));
312 __ Dsubu(a3, a3, Operand('a'));
313 __ Branch(&loop_check, ls, a3, Operand('z' - 'a'));
314 // Latin-1: Check for values in range [224,254] but not 247.
315 __ Dsubu(a3, a3, Operand(224 - 'a'));
316 // Weren't Latin-1 letters.
317 __ Branch(&fail, hi, a3, Operand(254 - 224));
318 // Check for 247.
319 __ Branch(&fail, eq, a3, Operand(247 - 224));
320
321 __ bind(&loop_check);
322 __ Branch(&loop, lt, a0, Operand(a1));
323 __ jmp(&success);
324
325 __ bind(&fail);
326 GoTo(on_no_match);
327
328 __ bind(&success);
329 // Compute new value of character position after the matched part.
330 __ Dsubu(current_input_offset(), a2, end_of_input_address());
331 if (read_backward) {
332 __ Ld(t1, register_location(start_reg)); // Index of start of capture.
333 __ Ld(a2, register_location(start_reg + 1)); // Index of end of capture.
334 __ Daddu(current_input_offset(), current_input_offset(), Operand(t1));
335 __ Dsubu(current_input_offset(), current_input_offset(), Operand(a2));
336 }
337 } else {
338 DCHECK(mode_ == UC16);
339
340 int argument_count = 4;
341 __ PrepareCallCFunction(argument_count, a2);
342
343 // a0 - offset of start of capture.
344 // a1 - length of capture.
345
346 // Put arguments into arguments registers.
347 // Parameters are
348 // a0: Address byte_offset1 - Address captured substring's start.
349 // a1: Address byte_offset2 - Address of current character position.
350 // a2: size_t byte_length - length of capture in bytes(!).
351 // a3: Isolate* isolate.
352
353 // Address of start of capture.
354 __ Daddu(a0, a0, Operand(end_of_input_address()));
355 // Length of capture.
356 __ mov(a2, a1);
357 // Save length in callee-save register for use on return.
358 __ mov(s3, a1);
359 // Address of current input position.
360 __ Daddu(a1, current_input_offset(), Operand(end_of_input_address()));
361 if (read_backward) {
362 __ Dsubu(a1, a1, Operand(s3));
363 }
364 // Isolate.
365 __ li(a3, Operand(ExternalReference::isolate_address(masm_->isolate())));
366
367 {
368 AllowExternalCallThatCantCauseGC scope(masm_.get());
369 ExternalReference function =
370 unicode
371 ? ExternalReference::re_case_insensitive_compare_unicode()
372 : ExternalReference::re_case_insensitive_compare_non_unicode();
373 CallCFunctionFromIrregexpCode(function, argument_count);
374 }
375
376 // Check if function returned non-zero for success or zero for failure.
377 BranchOrBacktrack(on_no_match, eq, v0, Operand(zero_reg));
378 // On success, increment position by length of capture.
379 if (read_backward) {
380 __ Dsubu(current_input_offset(), current_input_offset(), Operand(s3));
381 } else {
382 __ Daddu(current_input_offset(), current_input_offset(), Operand(s3));
383 }
384 }
385
386 __ bind(&fallthrough);
387}
388
389void RegExpMacroAssemblerMIPS::CheckNotBackReference(int start_reg,
390 bool read_backward,
391 Label* on_no_match) {
392 Label fallthrough;
393
394 // Find length of back-referenced capture.
395 __ Ld(a0, register_location(start_reg));
396 __ Ld(a1, register_location(start_reg + 1));
397 __ Dsubu(a1, a1, a0); // Length to check.
398
399 // At this point, the capture registers are either both set or both cleared.
400 // If the capture length is zero, then the capture is either empty or cleared.
401 // Fall through in both cases.
402 __ Branch(&fallthrough, eq, a1, Operand(zero_reg));
403
404 if (read_backward) {
405 __ Ld(t1, MemOperand(frame_pointer(), kStringStartMinusOneOffset));
406 __ Daddu(t1, t1, a1);
407 BranchOrBacktrack(on_no_match, le, current_input_offset(), Operand(t1));
408 } else {
409 __ Daddu(t1, a1, current_input_offset());
410 // Check that there are enough characters left in the input.
411 BranchOrBacktrack(on_no_match, gt, t1, Operand(zero_reg));
412 }
413
414 // Compute pointers to match string and capture string.
415 __ Daddu(a0, a0, Operand(end_of_input_address()));
416 __ Daddu(a2, end_of_input_address(), Operand(current_input_offset()));
417 if (read_backward) {
418 __ Dsubu(a2, a2, Operand(a1));
419 }
420 __ Daddu(a1, a1, Operand(a0));
421
422 Label loop;
423 __ bind(&loop);
424 if (mode_ == LATIN1) {
425 __ Lbu(a3, MemOperand(a0, 0));
426 __ daddiu(a0, a0, char_size());
427 __ Lbu(a4, MemOperand(a2, 0));
428 __ daddiu(a2, a2, char_size());
429 } else {
430 DCHECK(mode_ == UC16);
431 __ Lhu(a3, MemOperand(a0, 0));
432 __ daddiu(a0, a0, char_size());
433 __ Lhu(a4, MemOperand(a2, 0));
434 __ daddiu(a2, a2, char_size());
435 }
436 BranchOrBacktrack(on_no_match, ne, a3, Operand(a4));
437 __ Branch(&loop, lt, a0, Operand(a1));
438
439 // Move current character position to position after match.
440 __ Dsubu(current_input_offset(), a2, end_of_input_address());
441 if (read_backward) {
442 __ Ld(t1, register_location(start_reg)); // Index of start of capture.
443 __ Ld(a2, register_location(start_reg + 1)); // Index of end of capture.
444 __ Daddu(current_input_offset(), current_input_offset(), Operand(t1));
445 __ Dsubu(current_input_offset(), current_input_offset(), Operand(a2));
446 }
447 __ bind(&fallthrough);
448}
449
450
451void RegExpMacroAssemblerMIPS::CheckNotCharacter(uint32_t c,
452 Label* on_not_equal) {
453 BranchOrBacktrack(on_not_equal, ne, current_character(), Operand(c));
454}
455
456
457void RegExpMacroAssemblerMIPS::CheckCharacterAfterAnd(uint32_t c,
458 uint32_t mask,
459 Label* on_equal) {
460 __ And(a0, current_character(), Operand(mask));
461 Operand rhs = (c == 0) ? Operand(zero_reg) : Operand(c);
462 BranchOrBacktrack(on_equal, eq, a0, rhs);
463}
464
465
466void RegExpMacroAssemblerMIPS::CheckNotCharacterAfterAnd(uint32_t c,
467 uint32_t mask,
468 Label* on_not_equal) {
469 __ And(a0, current_character(), Operand(mask));
470 Operand rhs = (c == 0) ? Operand(zero_reg) : Operand(c);
471 BranchOrBacktrack(on_not_equal, ne, a0, rhs);
472}
473
474void RegExpMacroAssemblerMIPS::CheckNotCharacterAfterMinusAnd(
475 base::uc16 c, base::uc16 minus, base::uc16 mask, Label* on_not_equal) {
476 DCHECK_GT(String::kMaxUtf16CodeUnit, minus);
477 __ Dsubu(a0, current_character(), Operand(minus));
478 __ And(a0, a0, Operand(mask));
479 BranchOrBacktrack(on_not_equal, ne, a0, Operand(c));
480}
481
482void RegExpMacroAssemblerMIPS::CheckCharacterInRange(base::uc16 from,
483 base::uc16 to,
484 Label* on_in_range) {
485 __ Dsubu(a0, current_character(), Operand(from));
486 // Unsigned lower-or-same condition.
487 BranchOrBacktrack(on_in_range, ls, a0, Operand(to - from));
488}
489
490void RegExpMacroAssemblerMIPS::CheckCharacterNotInRange(
491 base::uc16 from, base::uc16 to, Label* on_not_in_range) {
492 __ Dsubu(a0, current_character(), Operand(from));
493 // Unsigned higher condition.
494 BranchOrBacktrack(on_not_in_range, hi, a0, Operand(to - from));
495}
496
497void RegExpMacroAssemblerMIPS::CallIsCharacterInRangeArray(
498 const ZoneList<CharacterRange>* ranges) {
499 static const int kNumArguments = 3;
500 __ PrepareCallCFunction(kNumArguments, a0);
501
502 __ mov(a0, current_character());
503 __ li(a1, Operand(GetOrAddRangeArray(ranges)));
504 __ li(a2, Operand(ExternalReference::isolate_address(isolate())));
505
506 {
507 // We have a frame (set up in GetCode), but the assembler doesn't know.
508 FrameScope scope(masm_.get(), StackFrame::MANUAL);
509 CallCFunctionFromIrregexpCode(
510 ExternalReference::re_is_character_in_range_array(), kNumArguments);
511 }
512
513 __ li(code_pointer(), Operand(masm_->CodeObject()));
514}
515
516bool RegExpMacroAssemblerMIPS::CheckCharacterInRangeArray(
517 const ZoneList<CharacterRange>* ranges, Label* on_in_range) {
518 CallIsCharacterInRangeArray(ranges);
519 BranchOrBacktrack(on_in_range, ne, v0, Operand(zero_reg));
520 return true;
521}
522
523bool RegExpMacroAssemblerMIPS::CheckCharacterNotInRangeArray(
524 const ZoneList<CharacterRange>* ranges, Label* on_not_in_range) {
525 CallIsCharacterInRangeArray(ranges);
526 BranchOrBacktrack(on_not_in_range, eq, v0, Operand(zero_reg));
527 return true;
528}
529
530void RegExpMacroAssemblerMIPS::CheckBitInTable(
531 Handle<ByteArray> table,
532 Label* on_bit_set) {
533 __ li(a0, Operand(table));
534 if (mode_ != LATIN1 || kTableMask != String::kMaxOneByteCharCode) {
535 __ And(a1, current_character(), Operand(kTableSize - 1));
536 __ Daddu(a0, a0, a1);
537 } else {
538 __ Daddu(a0, a0, current_character());
539 }
540
541 __ Lbu(a0, FieldMemOperand(a0, OFFSET_OF_DATA_START(ByteArray)));
542 BranchOrBacktrack(on_bit_set, ne, a0, Operand(zero_reg));
543}
544
545void RegExpMacroAssemblerMIPS::SkipUntilBitInTable(
546 int cp_offset, Handle<ByteArray> table, Handle<ByteArray> nibble_table,
547 int advance_by) {
548 // TODO(pthier): Optimize. Table can be loaded outside of the loop.
549 Label cont, again;
550 Bind(&again);
551 LoadCurrentCharacter(cp_offset, &cont, true);
552 CheckBitInTable(table, &cont);
553 AdvanceCurrentPosition(advance_by);
554 GoTo(&again);
555 Bind(&cont);
556}
557
558bool RegExpMacroAssemblerMIPS::CheckSpecialClassRanges(
559 StandardCharacterSet type, Label* on_no_match) {
560 // Range checks (c in min..max) are generally implemented by an unsigned
561 // (c - min) <= (max - min) check.
562 // TODO(jgruber): No custom implementation (yet): s(UC16), S(UC16).
563 switch (type) {
564 case StandardCharacterSet::kWhitespace:
565 // Match space-characters.
566 if (mode_ == LATIN1) {
567 // One byte space characters are '\t'..'\r', ' ' and \u00a0.
568 Label success;
569 __ Branch(&success, eq, current_character(), Operand(' '));
570 // Check range 0x09..0x0D.
571 __ Dsubu(a0, current_character(), Operand('\t'));
572 __ Branch(&success, ls, a0, Operand('\r' - '\t'));
573 // \u00a0 (NBSP).
574 BranchOrBacktrack(on_no_match, ne, a0, Operand(0x00A0 - '\t'));
575 __ bind(&success);
576 return true;
577 }
578 return false;
579 case StandardCharacterSet::kNotWhitespace:
580 // The emitted code for generic character classes is good enough.
581 return false;
582 case StandardCharacterSet::kDigit:
583 // Match Latin1 digits ('0'..'9').
584 __ Dsubu(a0, current_character(), Operand('0'));
585 BranchOrBacktrack(on_no_match, hi, a0, Operand('9' - '0'));
586 return true;
587 case StandardCharacterSet::kNotDigit:
588 // Match non Latin1-digits.
589 __ Dsubu(a0, current_character(), Operand('0'));
590 BranchOrBacktrack(on_no_match, ls, a0, Operand('9' - '0'));
591 return true;
592 case StandardCharacterSet::kNotLineTerminator: {
593 // Match non-newlines (not 0x0A('\n'), 0x0D('\r'), 0x2028 and 0x2029).
594 __ Xor(a0, current_character(), Operand(0x01));
595 // See if current character is '\n'^1 or '\r'^1, i.e., 0x0B or 0x0C.
596 __ Dsubu(a0, a0, Operand(0x0B));
597 BranchOrBacktrack(on_no_match, ls, a0, Operand(0x0C - 0x0B));
598 if (mode_ == UC16) {
599 // Compare original value to 0x2028 and 0x2029, using the already
600 // computed (current_char ^ 0x01 - 0x0B). I.e., check for
601 // 0x201D (0x2028 - 0x0B) or 0x201E.
602 __ Dsubu(a0, a0, Operand(0x2028 - 0x0B));
603 BranchOrBacktrack(on_no_match, ls, a0, Operand(1));
604 }
605 return true;
606 }
607 case StandardCharacterSet::kLineTerminator: {
608 // Match newlines (0x0A('\n'), 0x0D('\r'), 0x2028 and 0x2029).
609 __ Xor(a0, current_character(), Operand(0x01));
610 // See if current character is '\n'^1 or '\r'^1, i.e., 0x0B or 0x0C.
611 __ Dsubu(a0, a0, Operand(0x0B));
612 if (mode_ == LATIN1) {
613 BranchOrBacktrack(on_no_match, hi, a0, Operand(0x0C - 0x0B));
614 } else {
615 Label done;
616 BranchOrBacktrack(&done, ls, a0, Operand(0x0C - 0x0B));
617 // Compare original value to 0x2028 and 0x2029, using the already
618 // computed (current_char ^ 0x01 - 0x0B). I.e., check for
619 // 0x201D (0x2028 - 0x0B) or 0x201E.
620 __ Dsubu(a0, a0, Operand(0x2028 - 0x0B));
621 BranchOrBacktrack(on_no_match, hi, a0, Operand(1));
622 __ bind(&done);
623 }
624 return true;
625 }
626 case StandardCharacterSet::kWord: {
627 if (mode_ != LATIN1) {
628 // Table is 256 entries, so all Latin1 characters can be tested.
629 BranchOrBacktrack(on_no_match, hi, current_character(), Operand('z'));
630 }
631 ExternalReference map = ExternalReference::re_word_character_map();
632 __ li(a0, Operand(map));
633 __ Daddu(a0, a0, current_character());
634 __ Lbu(a0, MemOperand(a0, 0));
635 BranchOrBacktrack(on_no_match, eq, a0, Operand(zero_reg));
636 return true;
637 }
638 case StandardCharacterSet::kNotWord: {
639 Label done;
640 if (mode_ != LATIN1) {
641 // Table is 256 entries, so all Latin1 characters can be tested.
642 __ Branch(&done, hi, current_character(), Operand('z'));
643 }
644 ExternalReference map = ExternalReference::re_word_character_map();
645 __ li(a0, Operand(map));
646 __ Daddu(a0, a0, current_character());
647 __ Lbu(a0, MemOperand(a0, 0));
648 BranchOrBacktrack(on_no_match, ne, a0, Operand(zero_reg));
649 if (mode_ != LATIN1) {
650 __ bind(&done);
651 }
652 return true;
653 }
654 case StandardCharacterSet::kEverything:
655 // Match any character.
656 return true;
657 }
658}
659
660void RegExpMacroAssemblerMIPS::Fail() {
661 __ li(v0, Operand(FAILURE));
662 __ jmp(&exit_label_);
663}
664
665void RegExpMacroAssemblerMIPS::LoadRegExpStackPointerFromMemory(Register dst) {
666 ExternalReference ref =
667 ExternalReference::address_of_regexp_stack_stack_pointer(isolate());
668 __ li(dst, Operand(ref));
669 __ Ld(dst, MemOperand(dst));
670}
671
672void RegExpMacroAssemblerMIPS::StoreRegExpStackPointerToMemory(
673 Register src, Register scratch) {
674 ExternalReference ref =
675 ExternalReference::address_of_regexp_stack_stack_pointer(isolate());
676 __ li(scratch, Operand(ref));
677 __ Sd(src, MemOperand(scratch));
678}
679
680void RegExpMacroAssemblerMIPS::PushRegExpBasePointer(Register stack_pointer,
681 Register scratch) {
682 ExternalReference ref =
683 ExternalReference::address_of_regexp_stack_memory_top_address(isolate());
684 __ li(scratch, Operand(ref));
685 __ Ld(scratch, MemOperand(scratch));
686 __ Dsubu(scratch, stack_pointer, scratch);
687 __ Sd(scratch, MemOperand(frame_pointer(), kRegExpStackBasePointerOffset));
688}
689
690void RegExpMacroAssemblerMIPS::PopRegExpBasePointer(Register stack_pointer_out,
691 Register scratch) {
692 ExternalReference ref =
693 ExternalReference::address_of_regexp_stack_memory_top_address(isolate());
694 __ Ld(stack_pointer_out,
695 MemOperand(frame_pointer(), kRegExpStackBasePointerOffset));
696 __ li(scratch, Operand(ref));
697 __ Ld(scratch, MemOperand(scratch));
698 __ Daddu(stack_pointer_out, stack_pointer_out, scratch);
699 StoreRegExpStackPointerToMemory(stack_pointer_out, scratch);
700}
701
702DirectHandle<HeapObject> RegExpMacroAssemblerMIPS::GetCode(
703 DirectHandle<String> source, RegExpFlags flags) {
704 Label return_v0;
705 if (masm_->has_exception()) {
706 // If the code gets corrupted due to long regular expressions and lack of
707 // space on trampolines, an internal exception flag is set. If this case
708 // is detected, we will jump into exit sequence right away.
709 __ bind_to(&entry_label_, internal_failure_label_.pos());
710 } else {
711 // Finalize code - write the entry point code now we know how many
712 // registers we need.
713
714 // Entry code:
715 __ bind(&entry_label_);
716
717 // Tell the system that we have a stack frame. Because the type is MANUAL,
718 // no is generated.
719 FrameScope scope(masm_.get(), StackFrame::MANUAL);
720
721 // Emit code to start a new stack frame. In the following we push all
722 // callee-save registers (these end up above the fp) and all register
723 // arguments (in {a0,a1,a2,a3}, these end up below the fp).
724 // TODO(plind): we save s0..s7, but ONLY use s3 here - use the regs
725 // or dont save.
726 RegList registers_to_retain = {s0, s1, s2, s3, s4, s5, s6, s7, fp};
727
728 __ MultiPush(registers_to_retain | ra);
729 __ mov(frame_pointer(), sp);
730
731 // Registers {a0,a1,a2,a3} are the first four arguments as per the C calling
732 // convention, and must match our specified offsets (e.g. kInputEndOffset).
733 //
734 // a0: input_string
735 // a1: start_offset
736 // a2: input_start
737 // a3: input_end
738 RegList argument_registers = {a0, a1, a2, a3};
739 argument_registers |= {a4, a5, a6, a7};
740
741 // Also push the frame marker.
742 __ li(kScratchReg, Operand(StackFrame::TypeToMarker(StackFrame::IRREGEXP)));
743 static_assert(kFrameTypeOffset == kFramePointerOffset - kSystemPointerSize);
744 static_assert(kInputEndOffset ==
745 kRegisterOutputOffset - kSystemPointerSize);
746 static_assert(kInputStartOffset == kInputEndOffset - kSystemPointerSize);
747 static_assert(kStartIndexOffset == kInputStartOffset - kSystemPointerSize);
748 static_assert(kInputStringOffset == kStartIndexOffset - kSystemPointerSize);
749 __ MultiPush(argument_registers | kScratchReg);
750
751 static_assert(kSuccessfulCapturesOffset ==
752 kInputStringOffset - kSystemPointerSize);
753 __ mov(a0, zero_reg);
754 __ push(a0); // Make room for success counter and initialize it to 0.
755 static_assert(kStringStartMinusOneOffset ==
756 kSuccessfulCapturesOffset - kSystemPointerSize);
757 __ push(a0); // Make room for "string start - 1" constant.
758 static_assert(kBacktrackCountOffset ==
759 kStringStartMinusOneOffset - kSystemPointerSize);
760 __ push(a0); // The backtrack counter
761 static_assert(kRegExpStackBasePointerOffset ==
762 kBacktrackCountOffset - kSystemPointerSize);
763 __ push(a0); // The regexp stack base ptr.
764
765 // Initialize backtrack stack pointer. It must not be clobbered from here
766 // on. Note the backtrack_stackpointer is callee-saved.
767 static_assert(backtrack_stackpointer() == s7);
768 LoadRegExpStackPointerFromMemory(backtrack_stackpointer());
769
770 // Store the regexp base pointer - we'll later restore it / write it to
771 // memory when returning from this irregexp code object.
772 PushRegExpBasePointer(backtrack_stackpointer(), a1);
773
774 {
775 // Check if we have space on the stack for registers.
776 Label stack_limit_hit, stack_ok;
777
778 ExternalReference stack_limit =
779 ExternalReference::address_of_jslimit(masm_->isolate());
780 Operand extra_space_for_variables(num_registers_ * kPointerSize);
781
782 __ li(a0, Operand(stack_limit));
783 __ Ld(a0, MemOperand(a0));
784 __ Dsubu(a0, sp, a0);
785 // Handle it if the stack pointer is already below the stack limit.
786 __ Branch(&stack_limit_hit, le, a0, Operand(zero_reg));
787 // Check if there is room for the variable number of registers above
788 // the stack limit.
789 __ Branch(&stack_ok, hs, a0, extra_space_for_variables);
790 // Exit with OutOfMemory exception. There is not enough space on the stack
791 // for our working registers.
792 __ li(v0, Operand(EXCEPTION));
793 __ jmp(&return_v0);
794
795 __ bind(&stack_limit_hit);
796 CallCheckStackGuardState(a0, extra_space_for_variables);
797 // If returned value is non-zero, we exit with the returned value as
798 // result.
799 __ Branch(&return_v0, ne, v0, Operand(zero_reg));
800
801 __ bind(&stack_ok);
802 }
803
804 // Allocate space on stack for registers.
805 __ Dsubu(sp, sp, Operand(num_registers_ * kPointerSize));
806 // Load string end.
807 __ Ld(end_of_input_address(), MemOperand(frame_pointer(), kInputEndOffset));
808 // Load input start.
809 __ Ld(a0, MemOperand(frame_pointer(), kInputStartOffset));
810 // Find negative length (offset of start relative to end).
811 __ Dsubu(current_input_offset(), a0, end_of_input_address());
812 // Set a0 to address of char before start of the input string
813 // (effectively string position -1).
814 __ Ld(a1, MemOperand(frame_pointer(), kStartIndexOffset));
815 __ Dsubu(a0, current_input_offset(), Operand(char_size()));
816 __ dsll(t1, a1, (mode_ == UC16) ? 1 : 0);
817 __ Dsubu(a0, a0, t1);
818 // Store this value in a local variable, for use when clearing
819 // position registers.
820 __ Sd(a0, MemOperand(frame_pointer(), kStringStartMinusOneOffset));
821
822 // Initialize code pointer register
823 __ li(code_pointer(), Operand(masm_->CodeObject()), CONSTANT_SIZE);
824
825 Label load_char_start_regexp;
826 {
827 Label start_regexp;
828 // Load newline if index is at start, previous character otherwise.
829 __ Branch(&load_char_start_regexp, ne, a1, Operand(zero_reg));
830 __ li(current_character(), Operand('\n'));
831 __ jmp(&start_regexp);
832
833 // Global regexp restarts matching here.
834 __ bind(&load_char_start_regexp);
835 // Load previous char as initial value of current character register.
836 LoadCurrentCharacterUnchecked(-1, 1);
837 __ bind(&start_regexp);
838 }
839
840 // Initialize on-stack registers.
841 if (num_saved_registers_ > 0) { // Always is, if generated from a regexp.
842 // Fill saved registers with initial value = start offset - 1.
843 if (num_saved_registers_ > 8) {
844 // Address of register 0.
845 __ Daddu(a1, frame_pointer(), Operand(kRegisterZeroOffset));
846 __ li(a2, Operand(num_saved_registers_));
847 Label init_loop;
848 __ bind(&init_loop);
849 __ Sd(a0, MemOperand(a1));
850 __ Daddu(a1, a1, Operand(-kPointerSize));
851 __ Dsubu(a2, a2, Operand(1));
852 __ Branch(&init_loop, ne, a2, Operand(zero_reg));
853 } else {
854 for (int i = 0; i < num_saved_registers_; i++) {
855 __ Sd(a0, register_location(i));
856 }
857 }
858 }
859
860 __ jmp(&start_label_);
861
862 // Exit code:
863 if (success_label_.is_linked()) {
864 // Save captures when successful.
865 __ bind(&success_label_);
866 if (num_saved_registers_ > 0) {
867 // Copy captures to output.
868 __ Ld(a1, MemOperand(frame_pointer(), kInputStartOffset));
869 __ Ld(a0, MemOperand(frame_pointer(), kRegisterOutputOffset));
870 __ Ld(a2, MemOperand(frame_pointer(), kStartIndexOffset));
871 __ Dsubu(a1, end_of_input_address(), a1);
872 // a1 is length of input in bytes.
873 if (mode_ == UC16) {
874 __ dsrl(a1, a1, 1);
875 }
876 // a1 is length of input in characters.
877 __ Daddu(a1, a1, Operand(a2));
878 // a1 is length of string in characters.
879
880 DCHECK_EQ(0, num_saved_registers_ % 2);
881 // Always an even number of capture registers. This allows us to
882 // unroll the loop once to add an operation between a load of a register
883 // and the following use of that register.
884 for (int i = 0; i < num_saved_registers_; i += 2) {
885 __ Ld(a2, register_location(i));
886 __ Ld(a3, register_location(i + 1));
887 if (i == 0 && global_with_zero_length_check()) {
888 // Keep capture start in a4 for the zero-length check later.
889 __ mov(t3, a2);
890 }
891 if (mode_ == UC16) {
892 __ dsra(a2, a2, 1);
893 __ Daddu(a2, a2, a1);
894 __ dsra(a3, a3, 1);
895 __ Daddu(a3, a3, a1);
896 } else {
897 __ Daddu(a2, a1, Operand(a2));
898 __ Daddu(a3, a1, Operand(a3));
899 }
900 // V8 expects the output to be an int32_t array.
901 __ Sw(a2, MemOperand(a0));
902 __ Daddu(a0, a0, kIntSize);
903 __ Sw(a3, MemOperand(a0));
904 __ Daddu(a0, a0, kIntSize);
905 }
906 }
907
908 if (global()) {
909 // Restart matching if the regular expression is flagged as global.
910 __ Ld(a0, MemOperand(frame_pointer(), kSuccessfulCapturesOffset));
911 __ Ld(a1, MemOperand(frame_pointer(), kNumOutputRegistersOffset));
912 __ Ld(a2, MemOperand(frame_pointer(), kRegisterOutputOffset));
913 // Increment success counter.
914 __ Daddu(a0, a0, 1);
915 __ Sd(a0, MemOperand(frame_pointer(), kSuccessfulCapturesOffset));
916 // Capture results have been stored, so the number of remaining global
917 // output registers is reduced by the number of stored captures.
918 __ Dsubu(a1, a1, num_saved_registers_);
919 // Check whether we have enough room for another set of capture results.
920 __ mov(v0, a0);
921 __ Branch(&return_v0, lt, a1, Operand(num_saved_registers_));
922
923 __ Sd(a1, MemOperand(frame_pointer(), kNumOutputRegistersOffset));
924 // Advance the location for output.
925 __ Daddu(a2, a2, num_saved_registers_ * kIntSize);
926 __ Sd(a2, MemOperand(frame_pointer(), kRegisterOutputOffset));
927
928 // Restore the original regexp stack pointer value (effectively, pop the
929 // stored base pointer).
930 PopRegExpBasePointer(backtrack_stackpointer(), a2);
931
932 Label reload_string_start_minus_one;
933
934 if (global_with_zero_length_check()) {
935 // Special case for zero-length matches.
936 // t3: capture start index
937 // Not a zero-length match, restart.
938 __ Branch(&reload_string_start_minus_one, ne, current_input_offset(),
939 Operand(t3));
940 // Offset from the end is zero if we already reached the end.
941 __ Branch(&exit_label_, eq, current_input_offset(),
942 Operand(zero_reg));
943 // Advance current position after a zero-length match.
944 Label advance;
945 __ bind(&advance);
946 __ Daddu(current_input_offset(), current_input_offset(),
947 Operand((mode_ == UC16) ? 2 : 1));
948 if (global_unicode()) CheckNotInSurrogatePair(0, &advance);
949 }
950
951 __ bind(&reload_string_start_minus_one);
952 // Prepare a0 to initialize registers with its value in the next run.
953 // Must be immediately before the jump to avoid clobbering.
954 __ Ld(a0, MemOperand(frame_pointer(), kStringStartMinusOneOffset));
955
956 __ Branch(&load_char_start_regexp);
957 } else {
958 __ li(v0, Operand(SUCCESS));
959 }
960 }
961 // Exit and return v0.
962 __ bind(&exit_label_);
963 if (global()) {
964 __ Ld(v0, MemOperand(frame_pointer(), kSuccessfulCapturesOffset));
965 }
966
967 __ bind(&return_v0);
968 // Restore the original regexp stack pointer value (effectively, pop the
969 // stored base pointer).
970 PopRegExpBasePointer(backtrack_stackpointer(), a1);
971
972 // Skip sp past regexp registers and local variables..
973 __ mov(sp, frame_pointer());
974 // Restore registers s0..s7 and return (restoring ra to pc).
975 __ MultiPop(registers_to_retain | ra);
976 __ Ret();
977
978 // Backtrack code (branch target for conditional backtracks).
979 if (backtrack_label_.is_linked()) {
980 __ bind(&backtrack_label_);
981 Backtrack();
982 }
983
984 Label exit_with_exception;
985
986 // Preempt-code.
987 if (check_preempt_label_.is_linked()) {
988 SafeCallTarget(&check_preempt_label_);
989 StoreRegExpStackPointerToMemory(backtrack_stackpointer(), a0);
990
991 CallCheckStackGuardState(a0);
992 // If returning non-zero, we should end execution with the given
993 // result as return value.
994 __ Branch(&return_v0, ne, v0, Operand(zero_reg));
995
996 LoadRegExpStackPointerFromMemory(backtrack_stackpointer());
997
998 // String might have moved: Reload end of string from frame.
999 __ Ld(end_of_input_address(),
1000 MemOperand(frame_pointer(), kInputEndOffset));
1001 SafeReturn();
1002 }
1003
1004 // Backtrack stack overflow code.
1005 if (stack_overflow_label_.is_linked()) {
1006 SafeCallTarget(&stack_overflow_label_);
1007 StoreRegExpStackPointerToMemory(backtrack_stackpointer(), a0);
1008 // Reached if the backtrack-stack limit has been hit.
1009
1010 // Call GrowStack(isolate)
1011 static constexpr int kNumArguments = 1;
1012 __ PrepareCallCFunction(kNumArguments, a0);
1013 __ li(a0, Operand(ExternalReference::isolate_address(masm_->isolate())));
1014 ExternalReference grow_stack = ExternalReference::re_grow_stack();
1015 CallCFunctionFromIrregexpCode(grow_stack, kNumArguments);
1016 // If nullptr is returned, we have failed to grow the stack, and must exit
1017 // with a stack-overflow exception.
1018 __ Branch(&exit_with_exception, eq, v0, Operand(zero_reg));
1019 // Otherwise use return value as new stack pointer.
1020 __ mov(backtrack_stackpointer(), v0);
1021 SafeReturn();
1022 }
1023
1024 if (exit_with_exception.is_linked()) {
1025 // If any of the code above needed to exit with an exception.
1026 __ bind(&exit_with_exception);
1027 // Exit with Result EXCEPTION(-1) to signal thrown exception.
1028 __ li(v0, Operand(EXCEPTION));
1029 __ jmp(&return_v0);
1030 }
1031
1032 if (fallback_label_.is_linked()) {
1033 __ bind(&fallback_label_);
1034 __ li(v0, Operand(FALLBACK_TO_EXPERIMENTAL));
1035 __ jmp(&return_v0);
1036 }
1037 }
1038
1039 CodeDesc code_desc;
1040 masm_->GetCode(isolate(), &code_desc);
1041 Handle<Code> code =
1042 Factory::CodeBuilder(isolate(), code_desc, CodeKind::REGEXP)
1043 .set_self_reference(masm_->CodeObject())
1044 .set_empty_source_position_table()
1045 .Build();
1046 LOG(masm_->isolate(),
1047 RegExpCodeCreateEvent(Cast<AbstractCode>(code), source, flags));
1048 return Cast<HeapObject>(code);
1049}
1050
1051void RegExpMacroAssemblerMIPS::GoTo(Label* to) {
1052 if (to == nullptr) {
1053 Backtrack();
1054 return;
1055 }
1056 __ jmp(to);
1057 return;
1058}
1059
1060void RegExpMacroAssemblerMIPS::IfRegisterGE(int reg,
1061 int comparand,
1062 Label* if_ge) {
1063 __ Ld(a0, register_location(reg));
1064 BranchOrBacktrack(if_ge, ge, a0, Operand(comparand));
1065}
1066
1067void RegExpMacroAssemblerMIPS::IfRegisterLT(int reg,
1068 int comparand,
1069 Label* if_lt) {
1070 __ Ld(a0, register_location(reg));
1071 BranchOrBacktrack(if_lt, lt, a0, Operand(comparand));
1072}
1073
1074void RegExpMacroAssemblerMIPS::IfRegisterEqPos(int reg,
1075 Label* if_eq) {
1076 __ Ld(a0, register_location(reg));
1077 BranchOrBacktrack(if_eq, eq, a0, Operand(current_input_offset()));
1078}
1079
1080RegExpMacroAssembler::IrregexpImplementation
1081 RegExpMacroAssemblerMIPS::Implementation() {
1082 return kMIPSImplementation;
1083}
1084
1085void RegExpMacroAssemblerMIPS::PopCurrentPosition() {
1086 Pop(current_input_offset());
1087}
1088
1089void RegExpMacroAssemblerMIPS::PopRegister(int register_index) {
1090 Pop(a0);
1091 __ Sd(a0, register_location(register_index));
1092}
1093
1094void RegExpMacroAssemblerMIPS::PushBacktrack(Label* label) {
1095 if (label->is_bound()) {
1096 int target = label->pos();
1097 __ li(a0,
1098 Operand(target + InstructionStream::kHeaderSize - kHeapObjectTag));
1099 } else {
1100 Assembler::BlockTrampolinePoolScope block_trampoline_pool(masm_.get());
1101 Label after_constant;
1102 __ Branch(&after_constant);
1103 int offset = masm_->pc_offset();
1104 int cp_offset = offset + InstructionStream::kHeaderSize - kHeapObjectTag;
1105 __ emit(0);
1106 masm_->label_at_put(label, offset);
1107 __ bind(&after_constant);
1108 if (is_int16(cp_offset)) {
1109 __ Lwu(a0, MemOperand(code_pointer(), cp_offset));
1110 } else {
1111 __ Daddu(a0, code_pointer(), cp_offset);
1112 __ Lwu(a0, MemOperand(a0, 0));
1113 }
1114 }
1115 Push(a0);
1116 CheckStackLimit();
1117}
1118
1119void RegExpMacroAssemblerMIPS::PushCurrentPosition() {
1120 Push(current_input_offset());
1121 CheckStackLimit();
1122}
1123
1124void RegExpMacroAssemblerMIPS::PushRegister(int register_index,
1125 StackCheckFlag check_stack_limit) {
1126 __ Ld(a0, register_location(register_index));
1127 Push(a0);
1128 if (check_stack_limit) {
1129 CheckStackLimit();
1130 } else if (V8_UNLIKELY(v8_flags.slow_debug_code)) {
1131 AssertAboveStackLimitMinusSlack();
1132 }
1133}
1134
1135void RegExpMacroAssemblerMIPS::ReadCurrentPositionFromRegister(int reg) {
1136 __ Ld(current_input_offset(), register_location(reg));
1137}
1138
1139void RegExpMacroAssemblerMIPS::WriteStackPointerToRegister(int reg) {
1140 ExternalReference ref =
1141 ExternalReference::address_of_regexp_stack_memory_top_address(isolate());
1142 __ li(a0, Operand(ref));
1143 __ Ld(a0, MemOperand(a0));
1144 __ Dsubu(a0, backtrack_stackpointer(), a0);
1145 __ Sd(a0, register_location(reg));
1146}
1147
1148void RegExpMacroAssemblerMIPS::ReadStackPointerFromRegister(int reg) {
1149 ExternalReference ref =
1150 ExternalReference::address_of_regexp_stack_memory_top_address(isolate());
1151 __ li(a0, Operand(ref));
1152 __ Ld(a0, MemOperand(a0));
1153 __ Ld(backtrack_stackpointer(), register_location(reg));
1154 __ Daddu(backtrack_stackpointer(), backtrack_stackpointer(), Operand(a0));
1155}
1156
1157void RegExpMacroAssemblerMIPS::SetCurrentPositionFromEnd(int by) {
1158 Label after_position;
1159 __ Branch(&after_position,
1160 ge,
1161 current_input_offset(),
1162 Operand(-by * char_size()));
1163 __ li(current_input_offset(), -by * char_size());
1164 // On RegExp code entry (where this operation is used), the character before
1165 // the current position is expected to be already loaded.
1166 // We have advanced the position, so it's safe to read backwards.
1167 LoadCurrentCharacterUnchecked(-1, 1);
1168 __ bind(&after_position);
1169}
1170
1171void RegExpMacroAssemblerMIPS::SetRegister(int register_index, int to) {
1172 DCHECK(register_index >= num_saved_registers_); // Reserved for positions!
1173 __ li(a0, Operand(to));
1174 __ Sd(a0, register_location(register_index));
1175}
1176
1177bool RegExpMacroAssemblerMIPS::Succeed() {
1178 __ jmp(&success_label_);
1179 return global();
1180}
1181
1182void RegExpMacroAssemblerMIPS::WriteCurrentPositionToRegister(int reg,
1183 int cp_offset) {
1184 if (cp_offset == 0) {
1185 __ Sd(current_input_offset(), register_location(reg));
1186 } else {
1187 __ Daddu(a0, current_input_offset(), Operand(cp_offset * char_size()));
1188 __ Sd(a0, register_location(reg));
1189 }
1190}
1191
1192void RegExpMacroAssemblerMIPS::ClearRegisters(int reg_from, int reg_to) {
1193 DCHECK(reg_from <= reg_to);
1194 __ Ld(a0, MemOperand(frame_pointer(), kStringStartMinusOneOffset));
1195 for (int reg = reg_from; reg <= reg_to; reg++) {
1196 __ Sd(a0, register_location(reg));
1197 }
1198}
1199
1200bool RegExpMacroAssemblerMIPS::CanReadUnaligned() const { return false; }
1201
1202// Private methods:
1203
1204void RegExpMacroAssemblerMIPS::CallCheckStackGuardState(Register scratch,
1205 Operand extra_space) {
1206 DCHECK(!isolate()->IsGeneratingEmbeddedBuiltins());
1207 DCHECK(!masm_->options().isolate_independent_code);
1208
1209 int stack_alignment = base::OS::ActivationFrameAlignment();
1210
1211 // Align the stack pointer and save the original sp value on the stack.
1212 __ mov(scratch, sp);
1213 __ Dsubu(sp, sp, Operand(kPointerSize));
1214 DCHECK(base::bits::IsPowerOfTwo(stack_alignment));
1215 __ And(sp, sp, Operand(-stack_alignment));
1216 __ Sd(scratch, MemOperand(sp));
1217
1218 // Extra space for variables to consider in stack check.
1219 __ li(a3, extra_space);
1220 // RegExp code frame pointer.
1221 __ mov(a2, frame_pointer());
1222 // InstructionStream of self.
1223 __ li(a1, Operand(masm_->CodeObject()), CONSTANT_SIZE);
1224
1225 // We need to make room for the return address on the stack.
1226 DCHECK(IsAligned(stack_alignment, kPointerSize));
1227 __ Dsubu(sp, sp, Operand(stack_alignment));
1228
1229 // a0 will point to the return address, placed by DirectCEntry.
1230 __ mov(a0, sp);
1231
1232 ExternalReference stack_guard_check =
1233 ExternalReference::re_check_stack_guard_state();
1234 __ li(t9, Operand(stack_guard_check));
1235
1236 EmbeddedData d = EmbeddedData::FromBlob();
1237 CHECK(Builtins::IsIsolateIndependent(Builtin::kDirectCEntry));
1238 Address entry = d.InstructionStartOf(Builtin::kDirectCEntry);
1239 __ li(kScratchReg, Operand(entry, RelocInfo::OFF_HEAP_TARGET));
1240 __ Call(kScratchReg);
1241
1242 __ Ld(sp, MemOperand(sp, stack_alignment + kCArgsSlotsSize));
1243
1244 __ li(code_pointer(), Operand(masm_->CodeObject()));
1245}
1246
1247// Helper function for reading a value out of a stack frame.
1248template <typename T>
1249static T& frame_entry(Address re_frame, int frame_offset) {
1250 return reinterpret_cast<T&>(Memory<int32_t>(re_frame + frame_offset));
1251}
1252
1253template <typename T>
1254static T* frame_entry_address(Address re_frame, int frame_offset) {
1255 return reinterpret_cast<T*>(re_frame + frame_offset);
1256}
1257
1258int64_t RegExpMacroAssemblerMIPS::CheckStackGuardState(Address* return_address,
1259 Address raw_code,
1260 Address re_frame,
1261 uintptr_t extra_space) {
1262 Tagged<InstructionStream> re_code =
1263 Cast<InstructionStream>(Tagged<Object>(raw_code));
1264 return NativeRegExpMacroAssembler::CheckStackGuardState(
1265 frame_entry<Isolate*>(re_frame, kIsolateOffset),
1266 static_cast<int>(frame_entry<int64_t>(re_frame, kStartIndexOffset)),
1267 static_cast<RegExp::CallOrigin>(
1268 frame_entry<int64_t>(re_frame, kDirectCallOffset)),
1269 return_address, re_code,
1270 frame_entry_address<Address>(re_frame, kInputStringOffset),
1271 frame_entry_address<const uint8_t*>(re_frame, kInputStartOffset),
1272 frame_entry_address<const uint8_t*>(re_frame, kInputEndOffset),
1273 extra_space);
1274}
1275
1276MemOperand RegExpMacroAssemblerMIPS::register_location(int register_index) {
1277 DCHECK(register_index < (1<<30));
1278 if (num_registers_ <= register_index) {
1279 num_registers_ = register_index + 1;
1280 }
1281 return MemOperand(frame_pointer(),
1282 kRegisterZeroOffset - register_index * kPointerSize);
1283}
1284
1285void RegExpMacroAssemblerMIPS::CheckPosition(int cp_offset,
1286 Label* on_outside_input) {
1287 if (cp_offset >= 0) {
1288 BranchOrBacktrack(on_outside_input, ge, current_input_offset(),
1289 Operand(-cp_offset * char_size()));
1290 } else {
1291 __ Ld(a1, MemOperand(frame_pointer(), kStringStartMinusOneOffset));
1292 __ Daddu(a0, current_input_offset(), Operand(cp_offset * char_size()));
1293 BranchOrBacktrack(on_outside_input, le, a0, Operand(a1));
1294 }
1295}
1296
1297void RegExpMacroAssemblerMIPS::BranchOrBacktrack(Label* to,
1298 Condition condition,
1299 Register rs,
1300 const Operand& rt) {
1301 if (condition == al) { // Unconditional.
1302 if (to == nullptr) {
1303 Backtrack();
1304 return;
1305 }
1306 __ jmp(to);
1307 return;
1308 }
1309 if (to == nullptr) {
1310 __ Branch(&backtrack_label_, condition, rs, rt);
1311 return;
1312 }
1313 __ Branch(to, condition, rs, rt);
1314}
1315
1316void RegExpMacroAssemblerMIPS::SafeCall(Label* to,
1317 Condition cond,
1318 Register rs,
1319 const Operand& rt) {
1320 __ BranchAndLink(to, cond, rs, rt);
1321}
1322
1323void RegExpMacroAssemblerMIPS::SafeReturn() {
1324 __ pop(ra);
1325 __ Daddu(t1, ra, Operand(masm_->CodeObject()));
1326 __ Jump(t1);
1327}
1328
1329void RegExpMacroAssemblerMIPS::SafeCallTarget(Label* name) {
1330 __ bind(name);
1331 __ Dsubu(ra, ra, Operand(masm_->CodeObject()));
1332 __ push(ra);
1333}
1334
1335void RegExpMacroAssemblerMIPS::Push(Register source) {
1336 DCHECK(source != backtrack_stackpointer());
1337 __ Daddu(backtrack_stackpointer(),
1338 backtrack_stackpointer(),
1339 Operand(-kIntSize));
1340 __ Sw(source, MemOperand(backtrack_stackpointer()));
1341}
1342
1343void RegExpMacroAssemblerMIPS::Pop(Register target) {
1344 DCHECK(target != backtrack_stackpointer());
1345 __ Lw(target, MemOperand(backtrack_stackpointer()));
1346 __ Daddu(backtrack_stackpointer(), backtrack_stackpointer(), kIntSize);
1347}
1348
1349void RegExpMacroAssemblerMIPS::CallCFunctionFromIrregexpCode(
1350 ExternalReference function, int num_arguments) {
1351 // Irregexp code must not set fast_c_call_caller_fp and fast_c_call_caller_pc
1352 // since
1353 //
1354 // 1. it may itself have been called using CallCFunction and nested calls are
1355 // unsupported, and
1356 // 2. it may itself have been called directly from C where the frame pointer
1357 // might not be set (-fomit-frame-pointer), and thus frame iteration would
1358 // fail.
1359 //
1360 // See also: crbug.com/v8/12670#c17.
1361 __ CallCFunction(function, num_arguments, SetIsolateDataSlots::kNo);
1362}
1363
1364void RegExpMacroAssemblerMIPS::CheckPreemption() {
1365 // Check for preemption.
1366 ExternalReference stack_limit =
1367 ExternalReference::address_of_jslimit(masm_->isolate());
1368 __ li(a0, Operand(stack_limit));
1369 __ Ld(a0, MemOperand(a0));
1370 SafeCall(&check_preempt_label_, ls, sp, Operand(a0));
1371}
1372
1373void RegExpMacroAssemblerMIPS::CheckStackLimit() {
1374 ExternalReference stack_limit =
1375 ExternalReference::address_of_regexp_stack_limit_address(
1376 masm_->isolate());
1377
1378 __ li(a0, Operand(stack_limit));
1379 __ Ld(a0, MemOperand(a0));
1380 SafeCall(&stack_overflow_label_, ls, backtrack_stackpointer(), Operand(a0));
1381}
1382
1383void RegExpMacroAssemblerMIPS::AssertAboveStackLimitMinusSlack() {
1384 ExternalReference stack_limit =
1385 ExternalReference::address_of_regexp_stack_limit_address(
1386 masm_->isolate());
1387
1388 __ li(a0, Operand(stack_limit));
1389 __ Ld(a0, MemOperand(a0));
1390 SafeCall(&stack_overflow_label_, ls, backtrack_stackpointer(), Operand(a0));
1391
1392 DCHECK(v8_flags.slow_debug_code);
1393 Label no_stack_overflow;
1394 ASM_CODE_COMMENT_STRING(masm_.get(), "AssertAboveStackLimitMinusSlack");
1395 auto l = ExternalReference::address_of_regexp_stack_limit_address(isolate());
1396 __ li(a0, l);
1397 __ Ld(a0, MemOperand(a0));
1398 __ Dsubu(a0, a0, Operand(RegExpStack::kStackLimitSlackSize));
1399 __ Branch(&no_stack_overflow, hi, backtrack_stackpointer(), Operand(a0));
1400 __ DebugBreak();
1401 __ bind(&no_stack_overflow);
1402}
1403
1404void RegExpMacroAssemblerMIPS::LoadCurrentCharacterUnchecked(int cp_offset,
1405 int characters) {
1406 Register offset = current_input_offset();
1407 if (cp_offset != 0) {
1408 // t3 is not being used to store the capture start index at this point.
1409 __ Daddu(t3, current_input_offset(), Operand(cp_offset * char_size()));
1410 offset = t3;
1411 }
1412 // We assume that we cannot do unaligned loads on MIPS, so this function
1413 // must only be used to load a single character at a time.
1414 DCHECK_EQ(1, characters);
1415 __ Daddu(t1, end_of_input_address(), Operand(offset));
1416 if (mode_ == LATIN1) {
1417 __ Lbu(current_character(), MemOperand(t1, 0));
1418 } else {
1419 DCHECK(mode_ == UC16);
1420 __ Lhu(current_character(), MemOperand(t1, 0));
1421 }
1422}
1423
1424#undef __
1425
1426} // namespace internal
1427} // namespace v8
1428
1429#endif // V8_TARGET_ARCH_MIPS64
friend Zone
Definition asm-types.cc:195
RegExpMacroAssemblerMIPS(Isolate *isolate, Zone *zone, Mode mode, int registers_to_save)
RecordWriteMode const mode_
#define kScratchReg
const CodeDesc * code_desc
#define ASM_CODE_COMMENT_STRING(asm,...)
Definition assembler.h:618
Label label
Isolate * isolate
int32_t offset
LiftoffRegister reg
#define LOG(isolate, Call)
Definition log.h:78
uint32_t const mask
MaglevAssembler *const masm_
STL namespace.
uintptr_t Address
Definition memory.h:13
void Or(LiftoffAssembler *lasm, Register dst, Register lhs, Register rhs)
void Xor(LiftoffAssembler *lasm, Register dst, Register lhs, Register rhs)
void And(LiftoffAssembler *lasm, Register dst, Register lhs, Register rhs)
void push(LiftoffAssembler *assm, LiftoffRegister reg, ValueKind kind, int padding=0)
Address grow_stack(Isolate *isolate, void *current_sp, size_t frame_size, size_t gap, Address current_fp)
RegListBase< Register > RegList
Definition reglist-arm.h:14
MemOperand FieldMemOperand(Register object, int offset)
constexpr int kSystemPointerSize
Definition globals.h:410
std::unique_ptr< AssemblerBuffer > NewAssemblerBuffer(int size)
Definition assembler.cc:167
const int kHeapObjectTag
Definition v8-internal.h:72
V8_EXPORT_PRIVATE FlagValues v8_flags
#define DCHECK_LE(v1, v2)
Definition logging.h:490
#define CHECK(condition)
Definition logging.h:124
#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
constexpr bool IsAligned(T value, U alignment)
Definition macros.h:403
#define OFFSET_OF_DATA_START(Type)
#define V8_UNLIKELY(condition)
Definition v8config.h:660