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