blob: f8dfc97c9c05fc27515715a39e97586b17947c8b [file] [log] [blame]
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001// 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_ARM
6
7#include "src/regexp/arm/regexp-macro-assembler-arm.h"
8
9#include "src/code-stubs.h"
10#include "src/log.h"
11#include "src/macro-assembler.h"
12#include "src/profiler/cpu-profiler.h"
13#include "src/regexp/regexp-macro-assembler.h"
14#include "src/regexp/regexp-stack.h"
15#include "src/unicode.h"
16
17namespace v8 {
18namespace internal {
19
20#ifndef V8_INTERPRETED_REGEXP
21/*
22 * This assembler uses the following register assignment convention
23 * - r4 : Temporarily stores the index of capture start after a matching pass
24 * for a global regexp.
25 * - r5 : Pointer to current code object (Code*) including heap object tag.
26 * - r6 : Current position in input, as negative offset from end of string.
27 * Please notice that this is the byte offset, not the character offset!
28 * - r7 : Currently loaded character. Must be loaded using
29 * LoadCurrentCharacter before using any of the dispatch methods.
30 * - r8 : Points to tip of backtrack stack
31 * - r9 : Unused, might be used by C code and expected unchanged.
32 * - r10 : End of input (points to byte after last character in input).
33 * - r11 : Frame pointer. Used to access arguments, local variables and
34 * RegExp registers.
35 * - r12 : IP register, used by assembler. Very volatile.
36 * - r13/sp : Points to tip of C stack.
37 *
38 * The remaining registers are free for computations.
39 * Each call to a public method should retain this convention.
40 *
41 * The stack will have the following structure:
42 * - fp[56] Isolate* isolate (address of the current isolate)
43 * - fp[52] direct_call (if 1, direct call from JavaScript code,
44 * if 0, call through the runtime system).
45 * - fp[48] stack_area_base (high end of the memory area to use as
46 * backtracking stack).
47 * - fp[44] capture array size (may fit multiple sets of matches)
48 * - fp[40] int* capture_array (int[num_saved_registers_], for output).
49 * - fp[36] secondary link/return address used by native call.
50 * --- sp when called ---
51 * - fp[32] return address (lr).
52 * - fp[28] old frame pointer (r11).
53 * - fp[0..24] backup of registers r4..r10.
54 * --- frame pointer ----
55 * - fp[-4] end of input (address of end of string).
56 * - fp[-8] start of input (address of first character in string).
57 * - fp[-12] start index (character index of start).
58 * - fp[-16] void* input_string (location of a handle containing the string).
59 * - fp[-20] success counter (only for global regexps to count matches).
60 * - fp[-24] Offset of location before start of input (effectively character
61 * string start - 1). Used to initialize capture registers to a
62 * non-position.
63 * - fp[-28] At start (if 1, we are starting at the start of the
64 * string, otherwise 0)
65 * - fp[-32] register 0 (Only positions must be stored in the first
66 * - register 1 num_saved_registers_ registers)
67 * - ...
68 * - register num_registers-1
69 * --- sp ---
70 *
71 * The first num_saved_registers_ registers are initialized to point to
72 * "character -1" in the string (i.e., char_size() bytes before the first
73 * character of the string). The remaining registers start out as garbage.
74 *
75 * The data up to the return address must be placed there by the calling
76 * code and the remaining arguments are passed in registers, e.g. by calling the
77 * code entry as cast to a function with the signature:
78 * int (*match)(String* input_string,
79 * int start_index,
80 * Address start,
81 * Address end,
82 * Address secondary_return_address, // Only used by native call.
83 * int* capture_output_array,
84 * byte* stack_area_base,
85 * bool direct_call = false)
86 * The call is performed by NativeRegExpMacroAssembler::Execute()
87 * (in regexp-macro-assembler.cc) via the CALL_GENERATED_REGEXP_CODE macro
88 * in arm/simulator-arm.h.
89 * When calling as a non-direct call (i.e., from C++ code), the return address
90 * area is overwritten with the LR register by the RegExp code. When doing a
91 * direct call from generated code, the return address is placed there by
92 * the calling code, as in a normal exit frame.
93 */
94
95#define __ ACCESS_MASM(masm_)
96
97RegExpMacroAssemblerARM::RegExpMacroAssemblerARM(Isolate* isolate, Zone* zone,
98 Mode mode,
99 int registers_to_save)
100 : NativeRegExpMacroAssembler(isolate, zone),
101 masm_(new MacroAssembler(isolate, NULL, kRegExpCodeSize,
102 CodeObjectRequired::kYes)),
103 mode_(mode),
104 num_registers_(registers_to_save),
105 num_saved_registers_(registers_to_save),
106 entry_label_(),
107 start_label_(),
108 success_label_(),
109 backtrack_label_(),
110 exit_label_() {
111 DCHECK_EQ(0, registers_to_save % 2);
112 __ jmp(&entry_label_); // We'll write the entry code later.
113 __ bind(&start_label_); // And then continue from here.
114}
115
116
117RegExpMacroAssemblerARM::~RegExpMacroAssemblerARM() {
118 delete masm_;
119 // Unuse labels in case we throw away the assembler without calling GetCode.
120 entry_label_.Unuse();
121 start_label_.Unuse();
122 success_label_.Unuse();
123 backtrack_label_.Unuse();
124 exit_label_.Unuse();
125 check_preempt_label_.Unuse();
126 stack_overflow_label_.Unuse();
127}
128
129
130int RegExpMacroAssemblerARM::stack_limit_slack() {
131 return RegExpStack::kStackLimitSlack;
132}
133
134
135void RegExpMacroAssemblerARM::AdvanceCurrentPosition(int by) {
136 if (by != 0) {
137 __ add(current_input_offset(),
138 current_input_offset(), Operand(by * char_size()));
139 }
140}
141
142
143void RegExpMacroAssemblerARM::AdvanceRegister(int reg, int by) {
144 DCHECK(reg >= 0);
145 DCHECK(reg < num_registers_);
146 if (by != 0) {
147 __ ldr(r0, register_location(reg));
148 __ add(r0, r0, Operand(by));
149 __ str(r0, register_location(reg));
150 }
151}
152
153
154void RegExpMacroAssemblerARM::Backtrack() {
155 CheckPreemption();
156 // Pop Code* offset from backtrack stack, add Code* and jump to location.
157 Pop(r0);
158 __ add(pc, r0, Operand(code_pointer()));
159}
160
161
162void RegExpMacroAssemblerARM::Bind(Label* label) {
163 __ bind(label);
164}
165
166
167void RegExpMacroAssemblerARM::CheckCharacter(uint32_t c, Label* on_equal) {
168 __ cmp(current_character(), Operand(c));
169 BranchOrBacktrack(eq, on_equal);
170}
171
172
173void RegExpMacroAssemblerARM::CheckCharacterGT(uc16 limit, Label* on_greater) {
174 __ cmp(current_character(), Operand(limit));
175 BranchOrBacktrack(gt, on_greater);
176}
177
178
179void RegExpMacroAssemblerARM::CheckAtStart(Label* on_at_start) {
180 __ ldr(r1, MemOperand(frame_pointer(), kStringStartMinusOne));
181 __ add(r0, current_input_offset(), Operand(-char_size()));
182 __ cmp(r0, r1);
183 BranchOrBacktrack(eq, on_at_start);
184}
185
186
187void RegExpMacroAssemblerARM::CheckNotAtStart(int cp_offset,
188 Label* on_not_at_start) {
189 __ ldr(r1, MemOperand(frame_pointer(), kStringStartMinusOne));
190 __ add(r0, current_input_offset(),
191 Operand(-char_size() + cp_offset * char_size()));
192 __ cmp(r0, r1);
193 BranchOrBacktrack(ne, on_not_at_start);
194}
195
196
197void RegExpMacroAssemblerARM::CheckCharacterLT(uc16 limit, Label* on_less) {
198 __ cmp(current_character(), Operand(limit));
199 BranchOrBacktrack(lt, on_less);
200}
201
202
203void RegExpMacroAssemblerARM::CheckGreedyLoop(Label* on_equal) {
204 __ ldr(r0, MemOperand(backtrack_stackpointer(), 0));
205 __ cmp(current_input_offset(), r0);
206 __ add(backtrack_stackpointer(),
207 backtrack_stackpointer(), Operand(kPointerSize), LeaveCC, eq);
208 BranchOrBacktrack(eq, on_equal);
209}
210
211
212void RegExpMacroAssemblerARM::CheckNotBackReferenceIgnoreCase(
Ben Murdoch097c5b22016-05-18 11:27:45 +0100213 int start_reg, bool read_backward, bool unicode, Label* on_no_match) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000214 Label fallthrough;
215 __ ldr(r0, register_location(start_reg)); // Index of start of capture
216 __ ldr(r1, register_location(start_reg + 1)); // Index of end of capture
217 __ sub(r1, r1, r0, SetCC); // Length of capture.
218
219 // At this point, the capture registers are either both set or both cleared.
220 // If the capture length is zero, then the capture is either empty or cleared.
221 // Fall through in both cases.
222 __ b(eq, &fallthrough);
223
224 // Check that there are enough characters left in the input.
225 if (read_backward) {
226 __ ldr(r3, MemOperand(frame_pointer(), kStringStartMinusOne));
227 __ add(r3, r3, r1);
228 __ cmp(current_input_offset(), r3);
229 BranchOrBacktrack(le, on_no_match);
230 } else {
231 __ cmn(r1, Operand(current_input_offset()));
232 BranchOrBacktrack(gt, on_no_match);
233 }
234
235 if (mode_ == LATIN1) {
236 Label success;
237 Label fail;
238 Label loop_check;
239
240 // r0 - offset of start of capture
241 // r1 - length of capture
242 __ add(r0, r0, end_of_input_address());
243 __ add(r2, end_of_input_address(), current_input_offset());
244 if (read_backward) {
245 __ sub(r2, r2, r1); // Offset by length when matching backwards.
246 }
247 __ add(r1, r0, r1);
248
249 // r0 - Address of start of capture.
250 // r1 - Address of end of capture
251 // r2 - Address of current input position.
252
253 Label loop;
254 __ bind(&loop);
255 __ ldrb(r3, MemOperand(r0, char_size(), PostIndex));
256 __ ldrb(r4, MemOperand(r2, char_size(), PostIndex));
257 __ cmp(r4, r3);
258 __ b(eq, &loop_check);
259
260 // Mismatch, try case-insensitive match (converting letters to lower-case).
261 __ orr(r3, r3, Operand(0x20)); // Convert capture character to lower-case.
262 __ orr(r4, r4, Operand(0x20)); // Also convert input character.
263 __ cmp(r4, r3);
264 __ b(ne, &fail);
265 __ sub(r3, r3, Operand('a'));
266 __ cmp(r3, Operand('z' - 'a')); // Is r3 a lowercase letter?
267 __ b(ls, &loop_check); // In range 'a'-'z'.
268 // Latin-1: Check for values in range [224,254] but not 247.
269 __ sub(r3, r3, Operand(224 - 'a'));
270 __ cmp(r3, Operand(254 - 224));
271 __ b(hi, &fail); // Weren't Latin-1 letters.
272 __ cmp(r3, Operand(247 - 224)); // Check for 247.
273 __ b(eq, &fail);
274
275 __ bind(&loop_check);
276 __ cmp(r0, r1);
277 __ b(lt, &loop);
278 __ jmp(&success);
279
280 __ bind(&fail);
281 BranchOrBacktrack(al, on_no_match);
282
283 __ bind(&success);
284 // Compute new value of character position after the matched part.
285 __ sub(current_input_offset(), r2, end_of_input_address());
286 if (read_backward) {
287 __ ldr(r0, register_location(start_reg)); // Index of start of capture
288 __ ldr(r1, register_location(start_reg + 1)); // Index of end of capture
289 __ add(current_input_offset(), current_input_offset(), r0);
290 __ sub(current_input_offset(), current_input_offset(), r1);
291 }
292 } else {
293 DCHECK(mode_ == UC16);
294 int argument_count = 4;
295 __ PrepareCallCFunction(argument_count, r2);
296
297 // r0 - offset of start of capture
298 // r1 - length of capture
299
300 // Put arguments into arguments registers.
301 // Parameters are
302 // r0: Address byte_offset1 - Address captured substring's start.
303 // r1: Address byte_offset2 - Address of current character position.
304 // r2: size_t byte_length - length of capture in bytes(!)
Ben Murdoch097c5b22016-05-18 11:27:45 +0100305 // r3: Isolate* isolate or 0 if unicode flag.
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000306
307 // Address of start of capture.
308 __ add(r0, r0, Operand(end_of_input_address()));
309 // Length of capture.
310 __ mov(r2, Operand(r1));
311 // Save length in callee-save register for use on return.
312 __ mov(r4, Operand(r1));
313 // Address of current input position.
314 __ add(r1, current_input_offset(), end_of_input_address());
315 if (read_backward) {
316 __ sub(r1, r1, r4);
317 }
318 // Isolate.
Ben Murdoch097c5b22016-05-18 11:27:45 +0100319#ifdef V8_I18N_SUPPORT
320 if (unicode) {
321 __ mov(r3, Operand(0));
322 } else // NOLINT
323#endif // V8_I18N_SUPPORT
324 {
325 __ mov(r3, Operand(ExternalReference::isolate_address(isolate())));
326 }
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000327
328 {
329 AllowExternalCallThatCantCauseGC scope(masm_);
330 ExternalReference function =
331 ExternalReference::re_case_insensitive_compare_uc16(isolate());
332 __ CallCFunction(function, argument_count);
333 }
334
335 // Check if function returned non-zero for success or zero for failure.
336 __ cmp(r0, Operand::Zero());
337 BranchOrBacktrack(eq, on_no_match);
338
339 // On success, advance position by length of capture.
340 if (read_backward) {
341 __ sub(current_input_offset(), current_input_offset(), r4);
342 } else {
343 __ add(current_input_offset(), current_input_offset(), r4);
344 }
345 }
346
347 __ bind(&fallthrough);
348}
349
350
351void RegExpMacroAssemblerARM::CheckNotBackReference(int start_reg,
352 bool read_backward,
353 Label* on_no_match) {
354 Label fallthrough;
355 Label success;
356
357 // Find length of back-referenced capture.
358 __ ldr(r0, register_location(start_reg));
359 __ ldr(r1, register_location(start_reg + 1));
360 __ sub(r1, r1, r0, SetCC); // Length to check.
361
362 // At this point, the capture registers are either both set or both cleared.
363 // If the capture length is zero, then the capture is either empty or cleared.
364 // Fall through in both cases.
365 __ b(eq, &fallthrough);
366
367 // Check that there are enough characters left in the input.
368 if (read_backward) {
369 __ ldr(r3, MemOperand(frame_pointer(), kStringStartMinusOne));
370 __ add(r3, r3, r1);
371 __ cmp(current_input_offset(), r3);
372 BranchOrBacktrack(lt, on_no_match);
373 } else {
374 __ cmn(r1, Operand(current_input_offset()));
375 BranchOrBacktrack(gt, on_no_match);
376 }
377
378 // r0 - offset of start of capture
379 // r1 - length of capture
380 __ add(r0, r0, end_of_input_address());
381 __ add(r2, end_of_input_address(), current_input_offset());
382 if (read_backward) {
383 __ sub(r2, r2, r1); // Offset by length when matching backwards.
384 }
385 __ add(r1, r0, r1);
386
387 Label loop;
388 __ bind(&loop);
389 if (mode_ == LATIN1) {
390 __ ldrb(r3, MemOperand(r0, char_size(), PostIndex));
391 __ ldrb(r4, MemOperand(r2, char_size(), PostIndex));
392 } else {
393 DCHECK(mode_ == UC16);
394 __ ldrh(r3, MemOperand(r0, char_size(), PostIndex));
395 __ ldrh(r4, MemOperand(r2, char_size(), PostIndex));
396 }
397 __ cmp(r3, r4);
398 BranchOrBacktrack(ne, on_no_match);
399 __ cmp(r0, r1);
400 __ b(lt, &loop);
401
402 // Move current character position to position after match.
403 __ sub(current_input_offset(), r2, end_of_input_address());
404 if (read_backward) {
405 __ ldr(r0, register_location(start_reg)); // Index of start of capture
406 __ ldr(r1, register_location(start_reg + 1)); // Index of end of capture
407 __ add(current_input_offset(), current_input_offset(), r0);
408 __ sub(current_input_offset(), current_input_offset(), r1);
409 }
410
411 __ bind(&fallthrough);
412}
413
414
415void RegExpMacroAssemblerARM::CheckNotCharacter(unsigned c,
416 Label* on_not_equal) {
417 __ cmp(current_character(), Operand(c));
418 BranchOrBacktrack(ne, on_not_equal);
419}
420
421
422void RegExpMacroAssemblerARM::CheckCharacterAfterAnd(uint32_t c,
423 uint32_t mask,
424 Label* on_equal) {
425 if (c == 0) {
426 __ tst(current_character(), Operand(mask));
427 } else {
428 __ and_(r0, current_character(), Operand(mask));
429 __ cmp(r0, Operand(c));
430 }
431 BranchOrBacktrack(eq, on_equal);
432}
433
434
435void RegExpMacroAssemblerARM::CheckNotCharacterAfterAnd(unsigned c,
436 unsigned mask,
437 Label* on_not_equal) {
438 if (c == 0) {
439 __ tst(current_character(), Operand(mask));
440 } else {
441 __ and_(r0, current_character(), Operand(mask));
442 __ cmp(r0, Operand(c));
443 }
444 BranchOrBacktrack(ne, on_not_equal);
445}
446
447
448void RegExpMacroAssemblerARM::CheckNotCharacterAfterMinusAnd(
449 uc16 c,
450 uc16 minus,
451 uc16 mask,
452 Label* on_not_equal) {
453 DCHECK(minus < String::kMaxUtf16CodeUnit);
454 __ sub(r0, current_character(), Operand(minus));
455 __ and_(r0, r0, Operand(mask));
456 __ cmp(r0, Operand(c));
457 BranchOrBacktrack(ne, on_not_equal);
458}
459
460
461void RegExpMacroAssemblerARM::CheckCharacterInRange(
462 uc16 from,
463 uc16 to,
464 Label* on_in_range) {
465 __ sub(r0, current_character(), Operand(from));
466 __ cmp(r0, Operand(to - from));
467 BranchOrBacktrack(ls, on_in_range); // Unsigned lower-or-same condition.
468}
469
470
471void RegExpMacroAssemblerARM::CheckCharacterNotInRange(
472 uc16 from,
473 uc16 to,
474 Label* on_not_in_range) {
475 __ sub(r0, current_character(), Operand(from));
476 __ cmp(r0, Operand(to - from));
477 BranchOrBacktrack(hi, on_not_in_range); // Unsigned higher condition.
478}
479
480
481void RegExpMacroAssemblerARM::CheckBitInTable(
482 Handle<ByteArray> table,
483 Label* on_bit_set) {
484 __ mov(r0, Operand(table));
485 if (mode_ != LATIN1 || kTableMask != String::kMaxOneByteCharCode) {
486 __ and_(r1, current_character(), Operand(kTableSize - 1));
487 __ add(r1, r1, Operand(ByteArray::kHeaderSize - kHeapObjectTag));
488 } else {
489 __ add(r1,
490 current_character(),
491 Operand(ByteArray::kHeaderSize - kHeapObjectTag));
492 }
493 __ ldrb(r0, MemOperand(r0, r1));
494 __ cmp(r0, Operand::Zero());
495 BranchOrBacktrack(ne, on_bit_set);
496}
497
498
499bool RegExpMacroAssemblerARM::CheckSpecialCharacterClass(uc16 type,
500 Label* on_no_match) {
501 // Range checks (c in min..max) are generally implemented by an unsigned
502 // (c - min) <= (max - min) check
503 switch (type) {
504 case 's':
505 // Match space-characters
506 if (mode_ == LATIN1) {
507 // One byte space characters are '\t'..'\r', ' ' and \u00a0.
508 Label success;
509 __ cmp(current_character(), Operand(' '));
510 __ b(eq, &success);
511 // Check range 0x09..0x0d
512 __ sub(r0, current_character(), Operand('\t'));
513 __ cmp(r0, Operand('\r' - '\t'));
514 __ b(ls, &success);
515 // \u00a0 (NBSP).
516 __ cmp(r0, Operand(0x00a0 - '\t'));
517 BranchOrBacktrack(ne, on_no_match);
518 __ bind(&success);
519 return true;
520 }
521 return false;
522 case 'S':
523 // The emitted code for generic character classes is good enough.
524 return false;
525 case 'd':
526 // Match ASCII digits ('0'..'9')
527 __ sub(r0, current_character(), Operand('0'));
528 __ cmp(r0, Operand('9' - '0'));
529 BranchOrBacktrack(hi, on_no_match);
530 return true;
531 case 'D':
532 // Match non ASCII-digits
533 __ sub(r0, current_character(), Operand('0'));
534 __ cmp(r0, Operand('9' - '0'));
535 BranchOrBacktrack(ls, on_no_match);
536 return true;
537 case '.': {
538 // Match non-newlines (not 0x0a('\n'), 0x0d('\r'), 0x2028 and 0x2029)
539 __ eor(r0, current_character(), Operand(0x01));
540 // See if current character is '\n'^1 or '\r'^1, i.e., 0x0b or 0x0c
541 __ sub(r0, r0, Operand(0x0b));
542 __ cmp(r0, Operand(0x0c - 0x0b));
543 BranchOrBacktrack(ls, on_no_match);
544 if (mode_ == UC16) {
545 // Compare original value to 0x2028 and 0x2029, using the already
546 // computed (current_char ^ 0x01 - 0x0b). I.e., check for
547 // 0x201d (0x2028 - 0x0b) or 0x201e.
548 __ sub(r0, r0, Operand(0x2028 - 0x0b));
549 __ cmp(r0, Operand(1));
550 BranchOrBacktrack(ls, on_no_match);
551 }
552 return true;
553 }
554 case 'n': {
555 // Match newlines (0x0a('\n'), 0x0d('\r'), 0x2028 and 0x2029)
556 __ eor(r0, current_character(), Operand(0x01));
557 // See if current character is '\n'^1 or '\r'^1, i.e., 0x0b or 0x0c
558 __ sub(r0, r0, Operand(0x0b));
559 __ cmp(r0, Operand(0x0c - 0x0b));
560 if (mode_ == LATIN1) {
561 BranchOrBacktrack(hi, on_no_match);
562 } else {
563 Label done;
564 __ b(ls, &done);
565 // Compare original value to 0x2028 and 0x2029, using the already
566 // computed (current_char ^ 0x01 - 0x0b). I.e., check for
567 // 0x201d (0x2028 - 0x0b) or 0x201e.
568 __ sub(r0, r0, Operand(0x2028 - 0x0b));
569 __ cmp(r0, Operand(1));
570 BranchOrBacktrack(hi, on_no_match);
571 __ bind(&done);
572 }
573 return true;
574 }
575 case 'w': {
576 if (mode_ != LATIN1) {
577 // Table is 256 entries, so all Latin1 characters can be tested.
578 __ cmp(current_character(), Operand('z'));
579 BranchOrBacktrack(hi, on_no_match);
580 }
581 ExternalReference map = ExternalReference::re_word_character_map();
582 __ mov(r0, Operand(map));
583 __ ldrb(r0, MemOperand(r0, current_character()));
584 __ cmp(r0, Operand::Zero());
585 BranchOrBacktrack(eq, on_no_match);
586 return true;
587 }
588 case 'W': {
589 Label done;
590 if (mode_ != LATIN1) {
591 // Table is 256 entries, so all Latin1 characters can be tested.
592 __ cmp(current_character(), Operand('z'));
593 __ b(hi, &done);
594 }
595 ExternalReference map = ExternalReference::re_word_character_map();
596 __ mov(r0, Operand(map));
597 __ ldrb(r0, MemOperand(r0, current_character()));
598 __ cmp(r0, Operand::Zero());
599 BranchOrBacktrack(ne, on_no_match);
600 if (mode_ != LATIN1) {
601 __ bind(&done);
602 }
603 return true;
604 }
605 case '*':
606 // Match any character.
607 return true;
608 // No custom implementation (yet): s(UC16), S(UC16).
609 default:
610 return false;
611 }
612}
613
614
615void RegExpMacroAssemblerARM::Fail() {
616 __ mov(r0, Operand(FAILURE));
617 __ jmp(&exit_label_);
618}
619
620
621Handle<HeapObject> RegExpMacroAssemblerARM::GetCode(Handle<String> source) {
622 Label return_r0;
623 // Finalize code - write the entry point code now we know how many
624 // registers we need.
625
626 // Entry code:
627 __ bind(&entry_label_);
628
629 // Tell the system that we have a stack frame. Because the type is MANUAL, no
630 // is generated.
631 FrameScope scope(masm_, StackFrame::MANUAL);
632
633 // Actually emit code to start a new stack frame.
634 // Push arguments
635 // Save callee-save registers.
636 // Start new stack frame.
637 // Store link register in existing stack-cell.
638 // Order here should correspond to order of offset constants in header file.
639 RegList registers_to_retain = r4.bit() | r5.bit() | r6.bit() |
640 r7.bit() | r8.bit() | r9.bit() | r10.bit() | fp.bit();
641 RegList argument_registers = r0.bit() | r1.bit() | r2.bit() | r3.bit();
642 __ stm(db_w, sp, argument_registers | registers_to_retain | lr.bit());
643 // Set frame pointer in space for it if this is not a direct call
644 // from generated code.
645 __ add(frame_pointer(), sp, Operand(4 * kPointerSize));
646 __ mov(r0, Operand::Zero());
647 __ push(r0); // Make room for success counter and initialize it to 0.
648 __ push(r0); // Make room for "string start - 1" constant.
649 // Check if we have space on the stack for registers.
650 Label stack_limit_hit;
651 Label stack_ok;
652
653 ExternalReference stack_limit =
654 ExternalReference::address_of_stack_limit(isolate());
655 __ mov(r0, Operand(stack_limit));
656 __ ldr(r0, MemOperand(r0));
657 __ sub(r0, sp, r0, SetCC);
658 // Handle it if the stack pointer is already below the stack limit.
659 __ b(ls, &stack_limit_hit);
660 // Check if there is room for the variable number of registers above
661 // the stack limit.
662 __ cmp(r0, Operand(num_registers_ * kPointerSize));
663 __ b(hs, &stack_ok);
664 // Exit with OutOfMemory exception. There is not enough space on the stack
665 // for our working registers.
666 __ mov(r0, Operand(EXCEPTION));
667 __ jmp(&return_r0);
668
669 __ bind(&stack_limit_hit);
670 CallCheckStackGuardState(r0);
671 __ cmp(r0, Operand::Zero());
672 // If returned value is non-zero, we exit with the returned value as result.
673 __ b(ne, &return_r0);
674
675 __ bind(&stack_ok);
676
677 // Allocate space on stack for registers.
678 __ sub(sp, sp, Operand(num_registers_ * kPointerSize));
679 // Load string end.
680 __ ldr(end_of_input_address(), MemOperand(frame_pointer(), kInputEnd));
681 // Load input start.
682 __ ldr(r0, MemOperand(frame_pointer(), kInputStart));
683 // Find negative length (offset of start relative to end).
684 __ sub(current_input_offset(), r0, end_of_input_address());
685 // Set r0 to address of char before start of the input string
686 // (effectively string position -1).
687 __ ldr(r1, MemOperand(frame_pointer(), kStartIndex));
688 __ sub(r0, current_input_offset(), Operand(char_size()));
689 __ sub(r0, r0, Operand(r1, LSL, (mode_ == UC16) ? 1 : 0));
690 // Store this value in a local variable, for use when clearing
691 // position registers.
692 __ str(r0, MemOperand(frame_pointer(), kStringStartMinusOne));
693
694 // Initialize code pointer register
695 __ mov(code_pointer(), Operand(masm_->CodeObject()));
696
697 Label load_char_start_regexp, start_regexp;
698 // Load newline if index is at start, previous character otherwise.
699 __ cmp(r1, Operand::Zero());
700 __ b(ne, &load_char_start_regexp);
701 __ mov(current_character(), Operand('\n'), LeaveCC, eq);
702 __ jmp(&start_regexp);
703
704 // Global regexp restarts matching here.
705 __ bind(&load_char_start_regexp);
706 // Load previous char as initial value of current character register.
707 LoadCurrentCharacterUnchecked(-1, 1);
708 __ bind(&start_regexp);
709
710 // Initialize on-stack registers.
711 if (num_saved_registers_ > 0) { // Always is, if generated from a regexp.
712 // Fill saved registers with initial value = start offset - 1
713 if (num_saved_registers_ > 8) {
714 // Address of register 0.
715 __ add(r1, frame_pointer(), Operand(kRegisterZero));
716 __ mov(r2, Operand(num_saved_registers_));
717 Label init_loop;
718 __ bind(&init_loop);
719 __ str(r0, MemOperand(r1, kPointerSize, NegPostIndex));
720 __ sub(r2, r2, Operand(1), SetCC);
721 __ b(ne, &init_loop);
722 } else {
723 for (int i = 0; i < num_saved_registers_; i++) {
724 __ str(r0, register_location(i));
725 }
726 }
727 }
728
729 // Initialize backtrack stack pointer.
730 __ ldr(backtrack_stackpointer(), MemOperand(frame_pointer(), kStackHighEnd));
731
732 __ jmp(&start_label_);
733
734 // Exit code:
735 if (success_label_.is_linked()) {
736 // Save captures when successful.
737 __ bind(&success_label_);
738 if (num_saved_registers_ > 0) {
739 // copy captures to output
740 __ ldr(r1, MemOperand(frame_pointer(), kInputStart));
741 __ ldr(r0, MemOperand(frame_pointer(), kRegisterOutput));
742 __ ldr(r2, MemOperand(frame_pointer(), kStartIndex));
743 __ sub(r1, end_of_input_address(), r1);
744 // r1 is length of input in bytes.
745 if (mode_ == UC16) {
746 __ mov(r1, Operand(r1, LSR, 1));
747 }
748 // r1 is length of input in characters.
749 __ add(r1, r1, Operand(r2));
750 // r1 is length of string in characters.
751
752 DCHECK_EQ(0, num_saved_registers_ % 2);
753 // Always an even number of capture registers. This allows us to
754 // unroll the loop once to add an operation between a load of a register
755 // and the following use of that register.
756 for (int i = 0; i < num_saved_registers_; i += 2) {
757 __ ldr(r2, register_location(i));
758 __ ldr(r3, register_location(i + 1));
759 if (i == 0 && global_with_zero_length_check()) {
760 // Keep capture start in r4 for the zero-length check later.
761 __ mov(r4, r2);
762 }
763 if (mode_ == UC16) {
764 __ add(r2, r1, Operand(r2, ASR, 1));
765 __ add(r3, r1, Operand(r3, ASR, 1));
766 } else {
767 __ add(r2, r1, Operand(r2));
768 __ add(r3, r1, Operand(r3));
769 }
770 __ str(r2, MemOperand(r0, kPointerSize, PostIndex));
771 __ str(r3, MemOperand(r0, kPointerSize, PostIndex));
772 }
773 }
774
775 if (global()) {
776 // Restart matching if the regular expression is flagged as global.
777 __ ldr(r0, MemOperand(frame_pointer(), kSuccessfulCaptures));
778 __ ldr(r1, MemOperand(frame_pointer(), kNumOutputRegisters));
779 __ ldr(r2, MemOperand(frame_pointer(), kRegisterOutput));
780 // Increment success counter.
781 __ add(r0, r0, Operand(1));
782 __ str(r0, MemOperand(frame_pointer(), kSuccessfulCaptures));
783 // Capture results have been stored, so the number of remaining global
784 // output registers is reduced by the number of stored captures.
785 __ sub(r1, r1, Operand(num_saved_registers_));
786 // Check whether we have enough room for another set of capture results.
787 __ cmp(r1, Operand(num_saved_registers_));
788 __ b(lt, &return_r0);
789
790 __ str(r1, MemOperand(frame_pointer(), kNumOutputRegisters));
791 // Advance the location for output.
792 __ add(r2, r2, Operand(num_saved_registers_ * kPointerSize));
793 __ str(r2, MemOperand(frame_pointer(), kRegisterOutput));
794
795 // Prepare r0 to initialize registers with its value in the next run.
796 __ ldr(r0, MemOperand(frame_pointer(), kStringStartMinusOne));
797
798 if (global_with_zero_length_check()) {
799 // Special case for zero-length matches.
800 // r4: capture start index
801 __ cmp(current_input_offset(), r4);
802 // Not a zero-length match, restart.
803 __ b(ne, &load_char_start_regexp);
804 // Offset from the end is zero if we already reached the end.
805 __ cmp(current_input_offset(), Operand::Zero());
806 __ b(eq, &exit_label_);
807 // Advance current position after a zero-length match.
Ben Murdoch097c5b22016-05-18 11:27:45 +0100808 Label advance;
809 __ bind(&advance);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000810 __ add(current_input_offset(),
811 current_input_offset(),
812 Operand((mode_ == UC16) ? 2 : 1));
Ben Murdoch097c5b22016-05-18 11:27:45 +0100813 if (global_unicode()) CheckNotInSurrogatePair(0, &advance);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000814 }
815
816 __ b(&load_char_start_regexp);
817 } else {
818 __ mov(r0, Operand(SUCCESS));
819 }
820 }
821
822 // Exit and return r0
823 __ bind(&exit_label_);
824 if (global()) {
825 __ ldr(r0, MemOperand(frame_pointer(), kSuccessfulCaptures));
826 }
827
828 __ bind(&return_r0);
829 // Skip sp past regexp registers and local variables..
830 __ mov(sp, frame_pointer());
831 // Restore registers r4..r11 and return (restoring lr to pc).
832 __ ldm(ia_w, sp, registers_to_retain | pc.bit());
833
834 // Backtrack code (branch target for conditional backtracks).
835 if (backtrack_label_.is_linked()) {
836 __ bind(&backtrack_label_);
837 Backtrack();
838 }
839
840 Label exit_with_exception;
841
842 // Preempt-code
843 if (check_preempt_label_.is_linked()) {
844 SafeCallTarget(&check_preempt_label_);
845
846 CallCheckStackGuardState(r0);
847 __ cmp(r0, Operand::Zero());
848 // If returning non-zero, we should end execution with the given
849 // result as return value.
850 __ b(ne, &return_r0);
851
852 // String might have moved: Reload end of string from frame.
853 __ ldr(end_of_input_address(), MemOperand(frame_pointer(), kInputEnd));
854 SafeReturn();
855 }
856
857 // Backtrack stack overflow code.
858 if (stack_overflow_label_.is_linked()) {
859 SafeCallTarget(&stack_overflow_label_);
860 // Reached if the backtrack-stack limit has been hit.
861 Label grow_failed;
862
863 // Call GrowStack(backtrack_stackpointer(), &stack_base)
864 static const int num_arguments = 3;
865 __ PrepareCallCFunction(num_arguments, r0);
866 __ mov(r0, backtrack_stackpointer());
867 __ add(r1, frame_pointer(), Operand(kStackHighEnd));
868 __ mov(r2, Operand(ExternalReference::isolate_address(isolate())));
869 ExternalReference grow_stack =
870 ExternalReference::re_grow_stack(isolate());
871 __ CallCFunction(grow_stack, num_arguments);
872 // If return NULL, we have failed to grow the stack, and
873 // must exit with a stack-overflow exception.
874 __ cmp(r0, Operand::Zero());
875 __ b(eq, &exit_with_exception);
876 // Otherwise use return value as new stack pointer.
877 __ mov(backtrack_stackpointer(), r0);
878 // Restore saved registers and continue.
879 SafeReturn();
880 }
881
882 if (exit_with_exception.is_linked()) {
883 // If any of the code above needed to exit with an exception.
884 __ bind(&exit_with_exception);
885 // Exit with Result EXCEPTION(-1) to signal thrown exception.
886 __ mov(r0, Operand(EXCEPTION));
887 __ jmp(&return_r0);
888 }
889
890 CodeDesc code_desc;
891 masm_->GetCode(&code_desc);
892 Handle<Code> code = isolate()->factory()->NewCode(
893 code_desc, Code::ComputeFlags(Code::REGEXP), masm_->CodeObject());
Ben Murdochda12d292016-06-02 14:46:10 +0100894 PROFILE(masm_->isolate(),
895 RegExpCodeCreateEvent(AbstractCode::cast(*code), *source));
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000896 return Handle<HeapObject>::cast(code);
897}
898
899
900void RegExpMacroAssemblerARM::GoTo(Label* to) {
901 BranchOrBacktrack(al, to);
902}
903
904
905void RegExpMacroAssemblerARM::IfRegisterGE(int reg,
906 int comparand,
907 Label* if_ge) {
908 __ ldr(r0, register_location(reg));
909 __ cmp(r0, Operand(comparand));
910 BranchOrBacktrack(ge, if_ge);
911}
912
913
914void RegExpMacroAssemblerARM::IfRegisterLT(int reg,
915 int comparand,
916 Label* if_lt) {
917 __ ldr(r0, register_location(reg));
918 __ cmp(r0, Operand(comparand));
919 BranchOrBacktrack(lt, if_lt);
920}
921
922
923void RegExpMacroAssemblerARM::IfRegisterEqPos(int reg,
924 Label* if_eq) {
925 __ ldr(r0, register_location(reg));
926 __ cmp(r0, Operand(current_input_offset()));
927 BranchOrBacktrack(eq, if_eq);
928}
929
930
931RegExpMacroAssembler::IrregexpImplementation
932 RegExpMacroAssemblerARM::Implementation() {
933 return kARMImplementation;
934}
935
936
937void RegExpMacroAssemblerARM::LoadCurrentCharacter(int cp_offset,
938 Label* on_end_of_input,
939 bool check_bounds,
940 int characters) {
941 DCHECK(cp_offset < (1<<30)); // Be sane! (And ensure negation works)
942 if (check_bounds) {
943 if (cp_offset >= 0) {
944 CheckPosition(cp_offset + characters - 1, on_end_of_input);
945 } else {
946 CheckPosition(cp_offset, on_end_of_input);
947 }
948 }
949 LoadCurrentCharacterUnchecked(cp_offset, characters);
950}
951
952
953void RegExpMacroAssemblerARM::PopCurrentPosition() {
954 Pop(current_input_offset());
955}
956
957
958void RegExpMacroAssemblerARM::PopRegister(int register_index) {
959 Pop(r0);
960 __ str(r0, register_location(register_index));
961}
962
963
964void RegExpMacroAssemblerARM::PushBacktrack(Label* label) {
965 __ mov_label_offset(r0, label);
966 Push(r0);
967 CheckStackLimit();
968}
969
970
971void RegExpMacroAssemblerARM::PushCurrentPosition() {
972 Push(current_input_offset());
973}
974
975
976void RegExpMacroAssemblerARM::PushRegister(int register_index,
977 StackCheckFlag check_stack_limit) {
978 __ ldr(r0, register_location(register_index));
979 Push(r0);
980 if (check_stack_limit) CheckStackLimit();
981}
982
983
984void RegExpMacroAssemblerARM::ReadCurrentPositionFromRegister(int reg) {
985 __ ldr(current_input_offset(), register_location(reg));
986}
987
988
989void RegExpMacroAssemblerARM::ReadStackPointerFromRegister(int reg) {
990 __ ldr(backtrack_stackpointer(), register_location(reg));
991 __ ldr(r0, MemOperand(frame_pointer(), kStackHighEnd));
992 __ add(backtrack_stackpointer(), backtrack_stackpointer(), Operand(r0));
993}
994
995
996void RegExpMacroAssemblerARM::SetCurrentPositionFromEnd(int by) {
997 Label after_position;
998 __ cmp(current_input_offset(), Operand(-by * char_size()));
999 __ b(ge, &after_position);
1000 __ mov(current_input_offset(), Operand(-by * char_size()));
1001 // On RegExp code entry (where this operation is used), the character before
1002 // the current position is expected to be already loaded.
1003 // We have advanced the position, so it's safe to read backwards.
1004 LoadCurrentCharacterUnchecked(-1, 1);
1005 __ bind(&after_position);
1006}
1007
1008
1009void RegExpMacroAssemblerARM::SetRegister(int register_index, int to) {
1010 DCHECK(register_index >= num_saved_registers_); // Reserved for positions!
1011 __ mov(r0, Operand(to));
1012 __ str(r0, register_location(register_index));
1013}
1014
1015
1016bool RegExpMacroAssemblerARM::Succeed() {
1017 __ jmp(&success_label_);
1018 return global();
1019}
1020
1021
1022void RegExpMacroAssemblerARM::WriteCurrentPositionToRegister(int reg,
1023 int cp_offset) {
1024 if (cp_offset == 0) {
1025 __ str(current_input_offset(), register_location(reg));
1026 } else {
1027 __ add(r0, current_input_offset(), Operand(cp_offset * char_size()));
1028 __ str(r0, register_location(reg));
1029 }
1030}
1031
1032
1033void RegExpMacroAssemblerARM::ClearRegisters(int reg_from, int reg_to) {
1034 DCHECK(reg_from <= reg_to);
1035 __ ldr(r0, MemOperand(frame_pointer(), kStringStartMinusOne));
1036 for (int reg = reg_from; reg <= reg_to; reg++) {
1037 __ str(r0, register_location(reg));
1038 }
1039}
1040
1041
1042void RegExpMacroAssemblerARM::WriteStackPointerToRegister(int reg) {
1043 __ ldr(r1, MemOperand(frame_pointer(), kStackHighEnd));
1044 __ sub(r0, backtrack_stackpointer(), r1);
1045 __ str(r0, register_location(reg));
1046}
1047
1048
1049// Private methods:
1050
1051void RegExpMacroAssemblerARM::CallCheckStackGuardState(Register scratch) {
1052 __ PrepareCallCFunction(3, scratch);
1053
1054 // RegExp code frame pointer.
1055 __ mov(r2, frame_pointer());
1056 // Code* of self.
1057 __ mov(r1, Operand(masm_->CodeObject()));
1058
1059 // We need to make room for the return address on the stack.
1060 int stack_alignment = base::OS::ActivationFrameAlignment();
1061 DCHECK(IsAligned(stack_alignment, kPointerSize));
1062 __ sub(sp, sp, Operand(stack_alignment));
1063
1064 // r0 will point to the return address, placed by DirectCEntry.
1065 __ mov(r0, sp);
1066
1067 ExternalReference stack_guard_check =
1068 ExternalReference::re_check_stack_guard_state(isolate());
1069 __ mov(ip, Operand(stack_guard_check));
1070 DirectCEntryStub stub(isolate());
1071 stub.GenerateCall(masm_, ip);
1072
1073 // Drop the return address from the stack.
1074 __ add(sp, sp, Operand(stack_alignment));
1075
1076 DCHECK(stack_alignment != 0);
1077 __ ldr(sp, MemOperand(sp, 0));
1078
1079 __ mov(code_pointer(), Operand(masm_->CodeObject()));
1080}
1081
1082
1083// Helper function for reading a value out of a stack frame.
1084template <typename T>
1085static T& frame_entry(Address re_frame, int frame_offset) {
1086 return reinterpret_cast<T&>(Memory::int32_at(re_frame + frame_offset));
1087}
1088
1089
1090template <typename T>
1091static T* frame_entry_address(Address re_frame, int frame_offset) {
1092 return reinterpret_cast<T*>(re_frame + frame_offset);
1093}
1094
1095
1096int RegExpMacroAssemblerARM::CheckStackGuardState(Address* return_address,
1097 Code* re_code,
1098 Address re_frame) {
1099 return NativeRegExpMacroAssembler::CheckStackGuardState(
1100 frame_entry<Isolate*>(re_frame, kIsolate),
1101 frame_entry<int>(re_frame, kStartIndex),
1102 frame_entry<int>(re_frame, kDirectCall) == 1, return_address, re_code,
1103 frame_entry_address<String*>(re_frame, kInputString),
1104 frame_entry_address<const byte*>(re_frame, kInputStart),
1105 frame_entry_address<const byte*>(re_frame, kInputEnd));
1106}
1107
1108
1109MemOperand RegExpMacroAssemblerARM::register_location(int register_index) {
1110 DCHECK(register_index < (1<<30));
1111 if (num_registers_ <= register_index) {
1112 num_registers_ = register_index + 1;
1113 }
1114 return MemOperand(frame_pointer(),
1115 kRegisterZero - register_index * kPointerSize);
1116}
1117
1118
1119void RegExpMacroAssemblerARM::CheckPosition(int cp_offset,
1120 Label* on_outside_input) {
1121 if (cp_offset >= 0) {
1122 __ cmp(current_input_offset(), Operand(-cp_offset * char_size()));
1123 BranchOrBacktrack(ge, on_outside_input);
1124 } else {
1125 __ ldr(r1, MemOperand(frame_pointer(), kStringStartMinusOne));
1126 __ add(r0, current_input_offset(), Operand(cp_offset * char_size()));
1127 __ cmp(r0, r1);
1128 BranchOrBacktrack(le, on_outside_input);
1129 }
1130}
1131
1132
1133void RegExpMacroAssemblerARM::BranchOrBacktrack(Condition condition,
1134 Label* to) {
1135 if (condition == al) { // Unconditional.
1136 if (to == NULL) {
1137 Backtrack();
1138 return;
1139 }
1140 __ jmp(to);
1141 return;
1142 }
1143 if (to == NULL) {
1144 __ b(condition, &backtrack_label_);
1145 return;
1146 }
1147 __ b(condition, to);
1148}
1149
1150
1151void RegExpMacroAssemblerARM::SafeCall(Label* to, Condition cond) {
1152 __ bl(to, cond);
1153}
1154
1155
1156void RegExpMacroAssemblerARM::SafeReturn() {
1157 __ pop(lr);
1158 __ add(pc, lr, Operand(masm_->CodeObject()));
1159}
1160
1161
1162void RegExpMacroAssemblerARM::SafeCallTarget(Label* name) {
1163 __ bind(name);
1164 __ sub(lr, lr, Operand(masm_->CodeObject()));
1165 __ push(lr);
1166}
1167
1168
1169void RegExpMacroAssemblerARM::Push(Register source) {
1170 DCHECK(!source.is(backtrack_stackpointer()));
1171 __ str(source,
1172 MemOperand(backtrack_stackpointer(), kPointerSize, NegPreIndex));
1173}
1174
1175
1176void RegExpMacroAssemblerARM::Pop(Register target) {
1177 DCHECK(!target.is(backtrack_stackpointer()));
1178 __ ldr(target,
1179 MemOperand(backtrack_stackpointer(), kPointerSize, PostIndex));
1180}
1181
1182
1183void RegExpMacroAssemblerARM::CheckPreemption() {
1184 // Check for preemption.
1185 ExternalReference stack_limit =
1186 ExternalReference::address_of_stack_limit(isolate());
1187 __ mov(r0, Operand(stack_limit));
1188 __ ldr(r0, MemOperand(r0));
1189 __ cmp(sp, r0);
1190 SafeCall(&check_preempt_label_, ls);
1191}
1192
1193
1194void RegExpMacroAssemblerARM::CheckStackLimit() {
1195 ExternalReference stack_limit =
1196 ExternalReference::address_of_regexp_stack_limit(isolate());
1197 __ mov(r0, Operand(stack_limit));
1198 __ ldr(r0, MemOperand(r0));
1199 __ cmp(backtrack_stackpointer(), Operand(r0));
1200 SafeCall(&stack_overflow_label_, ls);
1201}
1202
1203
1204bool RegExpMacroAssemblerARM::CanReadUnaligned() {
1205 return CpuFeatures::IsSupported(UNALIGNED_ACCESSES) && !slow_safe();
1206}
1207
1208
1209void RegExpMacroAssemblerARM::LoadCurrentCharacterUnchecked(int cp_offset,
1210 int characters) {
1211 Register offset = current_input_offset();
1212 if (cp_offset != 0) {
1213 // r4 is not being used to store the capture start index at this point.
1214 __ add(r4, current_input_offset(), Operand(cp_offset * char_size()));
1215 offset = r4;
1216 }
1217 // The ldr, str, ldrh, strh instructions can do unaligned accesses, if the CPU
1218 // and the operating system running on the target allow it.
1219 // If unaligned load/stores are not supported then this function must only
1220 // be used to load a single character at a time.
1221 if (!CanReadUnaligned()) {
1222 DCHECK(characters == 1);
1223 }
1224
1225 if (mode_ == LATIN1) {
1226 if (characters == 4) {
1227 __ ldr(current_character(), MemOperand(end_of_input_address(), offset));
1228 } else if (characters == 2) {
1229 __ ldrh(current_character(), MemOperand(end_of_input_address(), offset));
1230 } else {
1231 DCHECK(characters == 1);
1232 __ ldrb(current_character(), MemOperand(end_of_input_address(), offset));
1233 }
1234 } else {
1235 DCHECK(mode_ == UC16);
1236 if (characters == 2) {
1237 __ ldr(current_character(), MemOperand(end_of_input_address(), offset));
1238 } else {
1239 DCHECK(characters == 1);
1240 __ ldrh(current_character(), MemOperand(end_of_input_address(), offset));
1241 }
1242 }
1243}
1244
1245
1246#undef __
1247
1248#endif // V8_INTERPRETED_REGEXP
1249
1250} // namespace internal
1251} // namespace v8
1252
1253#endif // V8_TARGET_ARCH_ARM