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