Preemption code for irregexp-native-ia32. Regexps can not only succeede or
authorlrn@chromium.org <lrn@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Fri, 12 Dec 2008 10:49:00 +0000 (10:49 +0000)
committerlrn@chromium.org <lrn@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Fri, 12 Dec 2008 10:49:00 +0000 (10:49 +0000)
fail, but also report a thrown exception.

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@974 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

src/assembler-ia32-inl.h
src/assembler-ia32.cc
src/assembler-ia32.h
src/execution.cc
src/execution.h
src/jsregexp.cc
src/regexp-macro-assembler-ia32.cc
src/regexp-macro-assembler-ia32.h
src/runtime.cc
test/cctest/test-regexp.cc

index 534d57e..aee847d 100644 (file)
@@ -152,6 +152,12 @@ Immediate::Immediate(const char* s) {
 }
 
 
+Immediate::Immediate(Label *internal_offset) {
+  x_ = reinterpret_cast<int32_t>(internal_offset);
+  rmode_ = RelocInfo::INTERNAL_REFERENCE;
+}
+
+
 Immediate::Immediate(Handle<Object> handle) {
   // Verify all Objects referred by code are NOT in new space.
   Object* obj = *handle;
@@ -200,11 +206,27 @@ void Assembler::emit(uint32_t x, RelocInfo::Mode rmode) {
 
 
 void Assembler::emit(const Immediate& x) {
+  if (x.rmode_ == RelocInfo::INTERNAL_REFERENCE) {
+    Label* label = reinterpret_cast<Label*>(x.x_);
+    emit_code_relative_offset(label);
+    return;
+  }
   if (x.rmode_ != RelocInfo::NONE) RecordRelocInfo(x.rmode_);
   emit(x.x_);
 }
 
 
+void Assembler::emit_code_relative_offset(Label* label) {
+  if (label->is_bound()) {
+    int32_t pos;
+    pos = label->pos() + Code::kHeaderSize - kHeapObjectTag;
+    emit(pos);
+  } else {
+    emit_disp(label, Displacement::CODE_RELATIVE);
+  }
+}
+
+
 void Assembler::emit_w(const Immediate& x) {
   ASSERT(x.rmode_ == RelocInfo::NONE);
   uint16_t value = static_cast<uint16_t>(x.x_);
index c7571e3..c174ea0 100644 (file)
@@ -420,29 +420,6 @@ void Assembler::push(const Operand& src) {
 }
 
 
-void Assembler::push(Label* label, RelocInfo::Mode reloc_mode) {
-  ASSERT_NOT_NULL(label);
-  EnsureSpace ensure_space(this);
-  last_pc_ = pc_;
-  // If reloc_mode == NONE, the label is stored as buffer relative.
-  ASSERT(reloc_mode == RelocInfo::NONE);
-  if (label->is_bound()) {
-    // Index of position relative to Code Object-pointer.
-    int rel_pos = label->pos() + Code::kHeaderSize - kHeapObjectTag;
-    if (rel_pos >= 0 && rel_pos < 256) {
-      EMIT(0x6a);
-      EMIT(rel_pos);
-    } else {
-      EMIT(0x68);
-      emit(rel_pos);
-    }
-  } else {
-    EMIT(0x68);
-    emit_disp(label, Displacement::CODE_RELATIVE);
-  }
-}
-
-
 void Assembler::pop(Register dst) {
   ASSERT(reloc_info_writer.last_pc() != NULL);
   if (FLAG_push_pop_elimination && (reloc_info_writer.last_pc() <= last_pc_)) {
index 17c6b82..2abf24a 100644 (file)
@@ -185,6 +185,10 @@ class Immediate BASE_EMBEDDED {
   inline explicit Immediate(Handle<Object> handle);
   inline explicit Immediate(Smi* value);
 
+  static Immediate CodeRelativeOffset(Label* label) {
+    return Immediate(label);
+  }
+
   bool is_zero() const { return x_ == 0 && rmode_ == RelocInfo::NONE; }
   bool is_int8() const {
     return -128 <= x_ && x_ < 128 && rmode_ == RelocInfo::NONE;
@@ -194,6 +198,8 @@ class Immediate BASE_EMBEDDED {
   }
 
  private:
+  inline explicit Immediate(Label* value);
+
   int x_;
   RelocInfo::Mode rmode_;
 
@@ -731,24 +737,6 @@ class Assembler : public Malloced {
 
 
  private:
-  // Code buffer:
-  // The buffer into which code and relocation info are generated.
-  byte* buffer_;
-  int buffer_size_;
-  // True if the assembler owns the buffer, false if buffer is external.
-  bool own_buffer_;
-
-  // code generation
-  byte* pc_;  // the program counter; moves forward
-  RelocInfoWriter reloc_info_writer;
-
-  // push-pop elimination
-  byte* last_pc_;
-
-  // source position information
-  int last_position_;
-  int last_statement_position_;
-
   byte* addr_at(int pos)  { return buffer_ + pos; }
   byte byte_at(int pos)  { return buffer_[pos]; }
   uint32_t long_at(int pos)  {
@@ -766,6 +754,9 @@ class Assembler : public Malloced {
   inline void emit(const Immediate& x);
   inline void emit_w(const Immediate& x);
 
+  // Emit the code-object-relative offset of the label's position
+  inline void emit_code_relative_offset(Label* label);
+
   // instruction generation
   void emit_arith_b(int op1, int op2, Register dst, int imm8);
 
@@ -794,6 +785,24 @@ class Assembler : public Malloced {
 
   friend class CodePatcher;
   friend class EnsureSpace;
+
+  // Code buffer:
+  // The buffer into which code and relocation info are generated.
+  byte* buffer_;
+  int buffer_size_;
+  // True if the assembler owns the buffer, false if buffer is external.
+  bool own_buffer_;
+
+  // code generation
+  byte* pc_;  // the program counter; moves forward
+  RelocInfoWriter reloc_info_writer;
+
+  // push-pop elimination
+  byte* last_pc_;
+
+  // source position information
+  int last_position_;
+  int last_statement_position_;
 };
 
 
index 941712a..7ccef5e 100644 (file)
@@ -38,6 +38,9 @@
 #include "simulator-ia32.h"
 #endif
 
+#include "debug.h"
+#include "v8threads.h"
+
 namespace v8 { namespace internal {
 
 
@@ -500,6 +503,69 @@ Handle<String> Execution::GetStackTraceLine(Handle<Object> recv,
 }
 
 
+static Object* RuntimePreempt() {
+  // Clear the preempt request flag.
+  StackGuard::Continue(PREEMPT);
+
+  ContextSwitcher::PreemptionReceived();
+
+  {
+    v8::Unlocker unlocker;
+    Thread::YieldCPU();
+  }
+
+  return Heap::undefined_value();
+}
+
+
+Object* Execution::DebugBreakHelper() {
+  // Just continue if breaks are disabled.
+  if (Debug::disable_break()) {
+    return Heap::undefined_value();
+  }
+
+  // Don't break in system functions. If the current function is
+  // either in the builtins object of some context or is in the debug
+  // context just return with the debug break stack guard active.
+  JavaScriptFrameIterator it;
+  JavaScriptFrame* frame = it.frame();
+  Object* fun = frame->function();
+  if (fun->IsJSFunction()) {
+    GlobalObject* global = JSFunction::cast(fun)->context()->global();
+    if (global->IsJSBuiltinsObject() || Debug::IsDebugGlobal(global)) {
+      return Heap::undefined_value();
+    }
+  }
+
+  // Clear the debug request flag.
+  StackGuard::Continue(DEBUGBREAK);
+
+  HandleScope scope;
+  // Enter the debugger. Just continue if we fail to enter the debugger.
+  EnterDebugger debugger;
+  if (debugger.FailedToEnter()) {
+    return Heap::undefined_value();
+  }
+
+  // Notify the debug event listeners.
+  Debugger::OnDebugBreak(Factory::undefined_value());
+
+  // Return to continue execution.
+  return Heap::undefined_value();
+}
+
+
+Object* Execution::HandleStackGuardInterrupt() {
+  if (StackGuard::IsDebugBreak()) DebugBreakHelper();
+  if (StackGuard::IsPreempted()) RuntimePreempt();
+  if (StackGuard::IsInterrupted()) {
+    // interrupt
+    StackGuard::Continue(INTERRUPT);
+    return Top::StackOverflow();
+  }
+  return Heap::undefined_value();
+}
+
 // --- G C   E x t e n s i o n ---
 
 const char* GCExtension::kSource = "native function gc();";
index 25a987f..bd37525 100644 (file)
@@ -118,6 +118,12 @@ class Execution : public AllStatic {
                                           Handle<Object> pos,
                                           Handle<Object> is_global);
 
+  static Object* DebugBreakHelper();
+
+  // If the stack guard is triggered, but it is not an actual
+  // stack overflow, then handle the interruption accordingly.
+  static Object* HandleStackGuardInterrupt();
+
   // Get a function delegate (or undefined) for the given non-function
   // object. Used for support calling objects as functions.
   static Handle<Object> GetFunctionDelegate(Handle<Object> object);
index c93cae9..c6f06a2 100644 (file)
@@ -309,7 +309,7 @@ Handle<Object> RegExpImpl::Exec(Handle<JSRegExp> regexp,
       return AtomExec(regexp, subject, index);
     case JSRegExp::IRREGEXP: {
       Handle<Object> result = IrregexpExec(regexp, subject, index);
-      if (!result.is_null()) {
+      if (!result.is_null() || Top::has_pending_exception()) {
         return result;
       }
       // We couldn't handle the regexp using Irregexp, so fall back
@@ -339,12 +339,13 @@ Handle<Object> RegExpImpl::ExecGlobal(Handle<JSRegExp> regexp,
       return AtomExecGlobal(regexp, subject);
     case JSRegExp::IRREGEXP: {
       Handle<Object> result = IrregexpExecGlobal(regexp, subject);
-      if (!result.is_null()) {
+      if (!result.is_null() || Top::has_pending_exception()) {
         return result;
       }
-      // We couldn't handle the regexp using Irregexp, so fall back
-      // on JSCRE.
-      // Reset the JSRegExp to use JSCRE.
+      // Empty handle as result but no exception thrown means that
+      // the regexp contains features not yet handled by the irregexp
+      // compiler.
+      // We have to fall back on JSCRE. Reset the JSRegExp to use JSCRE.
       JscrePrepare(regexp,
                    Handle<String>(regexp->Pattern()),
                    regexp->GetFlags());
@@ -683,6 +684,12 @@ Handle<Object> RegExpImpl::JscreExecGlobal(Handle<JSRegExp> regexp,
 // Irregexp implementation.
 
 
+// Retrieves a compiled version of the regexp for either ASCII or non-ASCII
+// strings. If the compiled version doesn't already exist, it is compiled
+// from the source pattern.
+// Irregexp is not feature complete yet. If there is something in the
+// regexp that the compiler cannot currently handle, an empty
+// handle is returned, but no exception is thrown.
 static Handle<FixedArray> GetCompiledIrregexp(Handle<JSRegExp> re,
                                               bool is_ascii) {
   ASSERT(re->DataAt(JSRegExp::kIrregexpDataIndex)->IsFixedArray());
@@ -912,6 +919,8 @@ Handle<Object> RegExpImpl::IrregexpExecOnce(Handle<FixedArray> irregexp,
       bool is_ascii = flatshape.IsAsciiRepresentation();
       int char_size_shift = is_ascii ? 0 : 1;
 
+      RegExpMacroAssemblerIA32::Result res;
+
       if (flatshape.IsExternal()) {
         const byte* address;
         if (is_ascii) {
@@ -921,7 +930,7 @@ Handle<Object> RegExpImpl::IrregexpExecOnce(Handle<FixedArray> irregexp,
           ExternalTwoByteString* ext = ExternalTwoByteString::cast(*subject);
           address = reinterpret_cast<const byte*>(ext->resource()->data());
         }
-        rc = RegExpMacroAssemblerIA32::Execute(
+        res = RegExpMacroAssemblerIA32::Execute(
             *code,
             &address,
             start_offset << char_size_shift,
@@ -933,7 +942,7 @@ Handle<Object> RegExpImpl::IrregexpExecOnce(Handle<FixedArray> irregexp,
             is_ascii ? SeqAsciiString::cast(*subject)->GetCharsAddress()
                      : SeqTwoByteString::cast(*subject)->GetCharsAddress();
         int byte_offset = char_address - reinterpret_cast<Address>(*subject);
-        rc = RegExpMacroAssemblerIA32::Execute(
+        res = RegExpMacroAssemblerIA32::Execute(
             *code,
             subject.location(),
             byte_offset + (start_offset << char_size_shift),
@@ -942,6 +951,12 @@ Handle<Object> RegExpImpl::IrregexpExecOnce(Handle<FixedArray> irregexp,
             previous_index == 0);
       }
 
+      if (res == RegExpMacroAssemblerIA32::EXCEPTION) {
+        ASSERT(Top::has_pending_exception());
+        return Handle<Object>::null();
+      }
+      rc = (res == RegExpMacroAssemblerIA32::SUCCESS);
+
       if (rc) {
         // Capture values are relative to start_offset only.
         for (int i = 0; i < offsets_vector_length; i++) {
index be2990e..4539b34 100644 (file)
@@ -107,6 +107,7 @@ RegExpMacroAssemblerIA32::~RegExpMacroAssemblerIA32() {
   start_label_.Unuse();
   success_label_.Unuse();
   exit_label_.Unuse();
+  check_preempt_label_.Unuse();
 }
 
 
@@ -126,9 +127,7 @@ void RegExpMacroAssemblerIA32::AdvanceRegister(int reg, int by) {
 
 
 void RegExpMacroAssemblerIA32::Backtrack() {
-  __ pop(ecx);
-  __ add(Operand(ecx), Immediate(self_));
-  __ jmp(Operand(ecx));
+  SafeReturn();
 }
 
 
@@ -215,6 +214,7 @@ void RegExpMacroAssemblerIA32::CheckCharacters(Vector<const uc16> str,
       constant_buffer.at<char>(i) = static_cast<char>(str[i]);
     }
   } else {
+    ASSERT(mode_ == UC16);
     memcpy(constant_buffer.location(),
            str.start(),
            str.length() * sizeof(uc16));
@@ -292,21 +292,12 @@ void RegExpMacroAssemblerIA32::CheckNotBackReferenceIgnoreCase(
     __ pop(esi);
     __ sub(edi, Operand(esi));
   } else {
-    // store state
+    ASSERT(mode_ == UC16);
     __ push(esi);
     __ push(edi);
     __ push(ecx);
-    // align stack
-    int frameAlignment = OS::ActivationFrameAlignment();
-    if (frameAlignment != 0) {
-      __ mov(ebx, esp);
-      __ sub(Operand(esp), Immediate(5 * kPointerSize));  // args + esp.
-      ASSERT(IsPowerOf2(frameAlignment));
-      __ and_(esp, -frameAlignment);
-      __ mov(Operand(esp, 4 * kPointerSize), ebx);
-    } else {
-      __ sub(Operand(esp), Immediate(4 * kPointerSize));
-    }
+    const int four_arguments = 4;
+    FrameAlign(four_arguments);
     // Put arguments on stack.
     __ mov(Operand(esp, 3 * kPointerSize), ecx);
     __ mov(ebx, Operand(ebp, kInputEndOffset));
@@ -317,17 +308,11 @@ void RegExpMacroAssemblerIA32::CheckNotBackReferenceIgnoreCase(
     __ mov(eax, Operand(ebp, kInputBuffer));
     __ mov(Operand(esp, 0 * kPointerSize), eax);
     Address function_address = FUNCTION_ADDR(&CaseInsensitiveCompareUC16);
-    __ mov(Operand(eax),
-        Immediate(reinterpret_cast<int32_t>(function_address)));
-    __ call(Operand(eax));
-    if (frameAlignment != 0) {
-      __ mov(esp, Operand(esp, 4 * kPointerSize));
-    } else {
-      __ add(Operand(esp), Immediate(4 * sizeof(int32_t)));
-    }
+    CallCFunction(function_address, four_arguments);
     __ pop(ecx);
     __ pop(edi);
     __ pop(esi);
+
     __ or_(eax, Operand(eax));
     BranchOrBacktrack(zero, on_no_match);
     __ add(edi, Operand(ecx));
@@ -536,25 +521,25 @@ Handle<Object> RegExpMacroAssemblerIA32::GetCode(Handle<String> source) {
 
 
   // Exit code:
-  // Success
-  __ bind(&success_label_);
-  if (num_saved_registers_ > 0) {
-    // copy captures to output
-    __ mov(ebx, Operand(ebp, kRegisterOutput));
-    __ mov(ecx, Operand(ebp, kInputEndOffset));
-    __ sub(ecx, Operand(ebp, kInputStartOffset));
-    for (int i = 0; i < num_saved_registers_; i++) {
-      __ mov(eax, register_location(i));
-      __ add(eax, Operand(ecx));  // Convert to index from start, not end.
-      if (char_size() > 1) {
-        ASSERT(char_size() == 2);
-        __ sar(eax, 1);  // Convert to character index, not byte.
+  if (success_label_.is_linked()) {
+    // Success
+    __ bind(&success_label_);
+    if (num_saved_registers_ > 0) {
+      // copy captures to output
+      __ mov(ebx, Operand(ebp, kRegisterOutput));
+      __ mov(ecx, Operand(ebp, kInputEndOffset));
+      __ sub(ecx, Operand(ebp, kInputStartOffset));
+      for (int i = 0; i < num_saved_registers_; i++) {
+        __ mov(eax, register_location(i));
+        __ add(eax, Operand(ecx));  // Convert to index from start, not end.
+        if (mode_ == UC16) {
+          __ sar(eax, 1);  // Convert byte index to character index.
+        }
+        __ mov(Operand(ebx, i * kPointerSize), eax);
       }
-      __ mov(Operand(ebx, i * kPointerSize), eax);
     }
+    __ mov(eax, Immediate(1));
   }
-  __ mov(eax, Immediate(1));
-
   // Exit and return eax
   __ bind(&exit_label_);
   __ leave();
@@ -563,6 +548,47 @@ Handle<Object> RegExpMacroAssemblerIA32::GetCode(Handle<String> source) {
   __ pop(esi);
   __ ret(0);
 
+  // Preempt-code
+  if (check_preempt_label_.is_linked()) {
+    __ bind(&check_preempt_label_);
+    // TODO(lrn): call C function to check the stack guard and return current
+    // stack state (0 = ok, positive = out of stack, negative = preempt).
+    // Then dispatch to an action depending on state, and loop.
+    __ push(edi);
+
+    Label retry;
+    Label stack_overflow;
+
+    __ bind(&retry);
+    int num_arguments = 2;
+    FrameAlign(num_arguments);
+    __ mov(Operand(esp, 1 * kPointerSize), Immediate(self_));
+    __ lea(eax, Operand(esp, -kPointerSize));
+    __ mov(Operand(esp, 0 * kPointerSize), eax);
+    CallCFunction(FUNCTION_ADDR(&CheckStackGuardState), num_arguments);
+
+    ExternalReference stack_guard_limit =
+        ExternalReference::address_of_stack_guard_limit();
+
+    __ or_(eax, Operand(eax));
+    __ j(not_equal, &stack_overflow);
+
+    __ cmp(esp, Operand::StaticVariable(stack_guard_limit));
+    __ j(below_equal, &retry);
+
+    __ pop(edi);
+    // String might have moved: Recompute esi from scratch.
+    __ mov(esi, Operand(esp, kInputBuffer));
+    __ mov(esi, Operand(esi, 0));
+    __ add(esi, Operand(esp, kInputEndOffset));
+    SafeReturn();
+
+    __ bind(&stack_overflow);
+    // Exit with result -1 to signal thrown exception.
+    __ mov(eax, -1);
+    __ jmp(&exit_label_);
+  }
+
   CodeDesc code_desc;
   masm_->GetCode(&code_desc);
   Handle<Code> code = Factory::NewCode(code_desc,
@@ -622,8 +648,8 @@ void RegExpMacroAssemblerIA32::PopRegister(int register_index) {
 
 
 void RegExpMacroAssemblerIA32::PushBacktrack(Label* label) {
-  // CheckStackLimit();  // Not ready yet.
-  __ push(label, RelocInfo::NONE);
+  __ push(Immediate::CodeRelativeOffset(label));
+  CheckStackLimit();
 }
 
 
@@ -710,6 +736,38 @@ int RegExpMacroAssemblerIA32::CaseInsensitiveCompareUC16(uc16** buffer,
 }
 
 
+int RegExpMacroAssemblerIA32::CheckStackGuardState(Address return_address,
+                                                   Code* re_code) {
+  if (StackGuard::IsStackOverflow()) {
+    Top::StackOverflow();
+    return 1;
+  }
+
+  // If not real stack overflow the stack guard was used to interrupt
+  // execution for another purpose.
+
+  // Prepare for possible GC.
+  Handle<Code> code_handle(re_code);
+#ifdef DEBUG
+  CHECK(re_code->instruction_start() <= return_address);
+  CHECK(return_address <=
+      re_code->instruction_start() + re_code->instruction_size());
+#endif
+
+  Object* result = Execution::HandleStackGuardInterrupt();
+
+  if (*code_handle != re_code) {  // Return address no longer valid
+    int delta = *code_handle - re_code;
+    *reinterpret_cast<int32_t*>(return_address) += delta;
+  }
+
+  if (result->IsException()) {
+    return 1;
+  }
+  return 0;
+}
+
+
 Operand RegExpMacroAssemblerIA32::register_location(int register_index) {
   ASSERT(register_index < (1<<30));
   if (num_registers_ <= register_index) {
@@ -750,51 +808,74 @@ void RegExpMacroAssemblerIA32::BranchOrBacktrack(Condition condition,
 }
 
 
+void RegExpMacroAssemblerIA32::SafeCall(Label* to) {
+  Label return_to;
+  __ push(Immediate::CodeRelativeOffset(&return_to));
+  __ jmp(to);
+  __ bind(&return_to);
+}
+
+
+void RegExpMacroAssemblerIA32::SafeReturn() {
+  __ pop(ecx);
+  __ add(Operand(ecx), Immediate(self_));
+  __ jmp(Operand(ecx));
+}
+
+
 void RegExpMacroAssemblerIA32::CheckStackLimit() {
   if (FLAG_check_stack) {
     // Check for preemption first.
     Label no_preempt;
-    Label retry_preempt;
     // Check for preemption.
     ExternalReference stack_guard_limit =
         ExternalReference::address_of_stack_guard_limit();
     __ cmp(esp, Operand::StaticVariable(stack_guard_limit));
     __ j(above, &no_preempt, taken);
-    __ push(edi);  // Current position.
-    __ push(edx);  // Current character.
-    // Restore original edi, esi.
-    __ mov(edi, Operand(ebp, kBackup_edi));
-    __ mov(esi, Operand(ebp, kBackup_esi));
-
-    __ bind(&retry_preempt);
-    // simulate stack for Runtime call.
-    __ push(eax);
-    __ push(Immediate(Smi::FromInt(0)));  // Dummy receiver
-    __ CallRuntime(Runtime::kStackGuard, 1);
-    __ pop(eax);
 
-    __ cmp(esp, Operand::StaticVariable(stack_guard_limit));
-    __ j(below_equal, &retry_preempt);
-
-    __ pop(edx);
-    __ pop(edi);
-    __ mov(esi, Operand(ebp, kInputBuffer));
-    __ mov(esi, Operand(esi, 0));
-    __ add(esi, Operand(ebp, kInputEndOffset));
+    SafeCall(&check_preempt_label_);
 
     __ bind(&no_preempt);
   }
 }
 
 
+void RegExpMacroAssemblerIA32::FrameAlign(int num_arguments) {
+  int frameAlignment = OS::ActivationFrameAlignment();
+  if (frameAlignment != 0) {
+    // Make stack end at alignment and make room for num_arguments words
+    // and the original value of esp.
+    __ mov(ebx, esp);
+    __ sub(Operand(esp), Immediate((num_arguments + 1) * kPointerSize));
+    ASSERT(IsPowerOf2(frameAlignment));
+    __ and_(esp, -frameAlignment);
+    __ mov(Operand(esp, num_arguments * kPointerSize), ebx);
+  } else {
+    __ sub(Operand(esp), Immediate(num_arguments * kPointerSize));
+  }
+}
+
+
+void RegExpMacroAssemblerIA32::CallCFunction(Address function_address,
+                                             int num_arguments) {
+  __ mov(Operand(eax), Immediate(reinterpret_cast<int32_t>(function_address)));
+  __ call(Operand(eax));
+  if (OS::ActivationFrameAlignment() != 0) {
+    __ mov(esp, Operand(esp, num_arguments * kPointerSize));
+  } else {
+    __ add(Operand(esp), Immediate(num_arguments * sizeof(int32_t)));
+  }
+}
+
+
 void RegExpMacroAssemblerIA32::LoadCurrentCharacterUnchecked(int cp_offset) {
   if (mode_ == ASCII) {
     __ movzx_b(current_character(), Operand(esi, edi, times_1, cp_offset));
-    return;
+  } else {
+    ASSERT(mode_ == UC16);
+    __ movzx_w(current_character(),
+               Operand(esi, edi, times_1, cp_offset * sizeof(uc16)));
   }
-  ASSERT(mode_ == UC16);
-  __ movzx_w(current_character(),
-             Operand(esi, edi, times_1, cp_offset * sizeof(uc16)));
 }
 
 
index 78ab2bd..f4918b6 100644 (file)
@@ -33,7 +33,8 @@ namespace v8 { namespace internal {
 class RegExpMacroAssemblerIA32: public RegExpMacroAssembler {
  public:
   // Type of input string to generate code for.
-  enum Mode {ASCII = 1, UC16 = 2};
+  enum Mode { ASCII = 1, UC16 = 2 };
+  enum Result { EXCEPTION = -1, FAILURE = 0, SUCCESS = 1 };
 
   RegExpMacroAssemblerIA32(Mode mode, int registers_to_save);
   virtual ~RegExpMacroAssemblerIA32();
@@ -92,16 +93,21 @@ class RegExpMacroAssemblerIA32: public RegExpMacroAssembler {
   virtual void WriteStackPointerToRegister(int reg);
 
   template <typename T>
-  static inline bool Execute(Code* code,
-                             T** input,
-                             int start_offset,
-                             int end_offset,
-                             int* output,
-                             bool at_start) {
-    typedef bool (*matcher)(T**, int, int, int*, int);
+  static inline Result Execute(Code* code,
+                               T** input,
+                               int start_offset,
+                               int end_offset,
+                               int* output,
+                               bool at_start) {
+    typedef int (*matcher)(T**, int, int, int*, int);
     matcher matcher_func = FUNCTION_CAST<matcher>(code->entry());
     int at_start_val = at_start ? 1 : 0;
-    return matcher_func(input, start_offset, end_offset, output, at_start_val);
+    int result = matcher_func(input,
+                              start_offset,
+                              end_offset,
+                              output,
+                              at_start_val);
+    return (result < 0) ? EXCEPTION : (result ? SUCCESS : FAILURE);
   }
 
  private:
@@ -129,6 +135,11 @@ class RegExpMacroAssemblerIA32: public RegExpMacroAssembler {
                                         int byte_offset2,
                                         size_t byte_length);
 
+  // Called from RegExp if the stack-guard is triggered.
+  // If the code object is relocated, the return address is fixed before
+  // returning.
+  static int CheckStackGuardState(Address return_address, Code* re_code);
+
   // The ebp-relative location of a regexp register.
   Operand register_location(int register_index);
 
@@ -151,6 +162,21 @@ class RegExpMacroAssemblerIA32: public RegExpMacroAssembler {
   // (and checks if we have hit the stack limit too).
   void CheckStackLimit();
 
+  // Call and return internally in the generated code in a way that
+  // is GC-safe (i.e., doesn't leave absolute code addresses on the stack)
+  void SafeCall(Label* to);
+  void SafeReturn();
+
+  // Before calling a C-function from generated code, align arguments on stack.
+  // After aligning the frame, arguments must be stored in esp[0], esp[4],
+  // etc., not pushed. The argument count assumes all arguments are word sized.
+  void FrameAlign(int num_arguments);
+  // Calls a C function and cleans up the space for arguments allocated
+  // by FrameAlign. The called function is not allowed to trigger a garbage
+  // collection, since that might move the code and invalidate the return
+  // address
+  void CallCFunction(Address function_address, int num_arguments);
+
   MacroAssembler* masm_;
   // Constant buffer provider. Allocates external storage for storing
   // constants.
@@ -167,6 +193,7 @@ class RegExpMacroAssemblerIA32: public RegExpMacroAssembler {
   Label start_label_;
   Label success_label_;
   Label exit_label_;
+  Label check_preempt_label_;
   // Handle used to represent the generated code object itself.
   Handle<Object> self_;
 };
index 8c3d043..b400390 100644 (file)
@@ -3664,61 +3664,9 @@ static Object* Runtime_StackOverflow(Arguments args) {
 }
 
 
-static Object* RuntimePreempt(Arguments args) {
-  // Clear the preempt request flag.
-  StackGuard::Continue(PREEMPT);
-
-  ContextSwitcher::PreemptionReceived();
-
-  {
-    v8::Unlocker unlocker;
-    Thread::YieldCPU();
-  }
-
-  return Heap::undefined_value();
-}
-
-
-static Object* DebugBreakHelper() {
-  // Just continue if breaks are disabled.
-  if (Debug::disable_break()) {
-    return Heap::undefined_value();
-  }
-
-  // Don't break in system functions. If the current function is
-  // either in the builtins object of some context or is in the debug
-  // context just return with the debug break stack guard active.
-  JavaScriptFrameIterator it;
-  JavaScriptFrame* frame = it.frame();
-  Object* fun = frame->function();
-  if (fun->IsJSFunction()) {
-    GlobalObject* global = JSFunction::cast(fun)->context()->global();
-    if (global->IsJSBuiltinsObject() || Debug::IsDebugGlobal(global)) {
-      return Heap::undefined_value();
-    }
-  }
-
-  // Clear the debug request flag.
-  StackGuard::Continue(DEBUGBREAK);
-
-  HandleScope scope;
-  // Enter the debugger. Just continue if we fail to enter the debugger.
-  EnterDebugger debugger;
-  if (debugger.FailedToEnter()) {
-    return Heap::undefined_value();
-  }
-
-  // Notify the debug event listeners.
-  Debugger::OnDebugBreak(Factory::undefined_value());
-
-  // Return to continue execution.
-  return Heap::undefined_value();
-}
-
-
 static Object* Runtime_DebugBreak(Arguments args) {
   ASSERT(args.length() == 0);
-  return DebugBreakHelper();
+  return Execution::DebugBreakHelper();
 }
 
 
@@ -3728,16 +3676,7 @@ static Object* Runtime_StackGuard(Arguments args) {
   // First check if this is a real stack overflow.
   if (StackGuard::IsStackOverflow()) return Runtime_StackOverflow(args);
 
-  // If not real stack overflow the stack guard was used to interrupt
-  // execution for another purpose.
-  if (StackGuard::IsDebugBreak()) DebugBreakHelper();
-  if (StackGuard::IsPreempted()) RuntimePreempt(args);
-  if (StackGuard::IsInterrupted()) {
-    // interrupt
-    StackGuard::Continue(INTERRUPT);
-    return Top::StackOverflow();
-  }
-  return Heap::undefined_value();
+  return Execution::HandleStackGuardInterrupt();
 }
 
 
index 998f113..4d03b2a 100644 (file)
@@ -574,12 +574,26 @@ TEST(MacroAssembler) {
 
 #ifndef ARM  // IA32 only tests.
 
+class ContextInitializer {
+ public:
+  ContextInitializer() : env_(), scope_(), stack_guard_() {
+    env_ = v8::Context::New();
+    env_->Enter();
+  }
+  ~ContextInitializer() {
+    env_->Exit();
+    env_.Dispose();
+  }
+ private:
+  v8::Persistent<v8::Context> env_;
+  v8::HandleScope scope_;
+  v8::internal::StackGuard stack_guard_;
+};
+
+
 TEST(MacroAssemblerIA32Success) {
   V8::Initialize(NULL);
-
-  // regexp-macro-assembler-ia32 needs a handle scope to allocate
-  // byte-arrays for constants.
-  v8::HandleScope scope;
+  ContextInitializer initializer;
 
   RegExpMacroAssemblerIA32 m(RegExpMacroAssemblerIA32::ASCII, 4);
 
@@ -596,14 +610,15 @@ TEST(MacroAssemblerIA32Success) {
   int start_offset = start_adr - reinterpret_cast<Address>(*seq_input);
   int end_offset = start_offset + seq_input->length();
 
-  bool success = RegExpMacroAssemblerIA32::Execute(*code,
-                                                   seq_input.location(),
-                                                   start_offset,
-                                                   end_offset,
-                                                   captures,
-                                                   true);
+  RegExpMacroAssemblerIA32::Result result =
+      RegExpMacroAssemblerIA32::Execute(*code,
+                                        seq_input.location(),
+                                        start_offset,
+                                        end_offset,
+                                        captures,
+                                        true);
 
-  CHECK(success);
+  CHECK_EQ(RegExpMacroAssemblerIA32::SUCCESS, result);
   CHECK_EQ(-1, captures[0]);
   CHECK_EQ(-1, captures[1]);
   CHECK_EQ(-1, captures[2]);
@@ -613,10 +628,7 @@ TEST(MacroAssemblerIA32Success) {
 
 TEST(MacroAssemblerIA32Simple) {
   V8::Initialize(NULL);
-
-  // regexp-macro-assembler-ia32 needs a handle scope to allocate
-  // byte-arrays for constants.
-  v8::HandleScope scope;
+  ContextInitializer initializer;
 
   RegExpMacroAssemblerIA32 m(RegExpMacroAssemblerIA32::ASCII, 4);
 
@@ -643,14 +655,15 @@ TEST(MacroAssemblerIA32Simple) {
   int start_offset = start_adr - reinterpret_cast<Address>(*seq_input);
   int end_offset = start_offset + seq_input->length();
 
-  bool success = RegExpMacroAssemblerIA32::Execute(*code,
-                                                   seq_input.location(),
-                                                   start_offset,
-                                                   end_offset,
-                                                   captures,
-                                                   true);
+  RegExpMacroAssemblerIA32::Result result =
+      RegExpMacroAssemblerIA32::Execute(*code,
+                                        seq_input.location(),
+                                        start_offset,
+                                        end_offset,
+                                        captures,
+                                        true);
 
-  CHECK(success);
+  CHECK_EQ(RegExpMacroAssemblerIA32::SUCCESS, result);
   CHECK_EQ(0, captures[0]);
   CHECK_EQ(3, captures[1]);
   CHECK_EQ(-1, captures[2]);
@@ -662,23 +675,20 @@ TEST(MacroAssemblerIA32Simple) {
   start_offset = start_adr - reinterpret_cast<Address>(*seq_input);
   end_offset = start_offset + seq_input->length();
 
-  success = RegExpMacroAssemblerIA32::Execute(*code,
-                                              seq_input.location(),
-                                              start_offset,
-                                              end_offset,
-                                              captures,
-                                              true);
+  result = RegExpMacroAssemblerIA32::Execute(*code,
+                                             seq_input.location(),
+                                             start_offset,
+                                             end_offset,
+                                             captures,
+                                             true);
 
-  CHECK(!success);
+  CHECK_EQ(RegExpMacroAssemblerIA32::FAILURE, result);
 }
 
 
 TEST(MacroAssemblerIA32SimpleUC16) {
   V8::Initialize(NULL);
-
-  // regexp-macro-assembler-ia32 needs a handle scope to allocate
-  // byte-arrays for constants.
-  v8::HandleScope scope;
+  ContextInitializer initializer;
 
   RegExpMacroAssemblerIA32 m(RegExpMacroAssemblerIA32::UC16, 4);
 
@@ -707,14 +717,15 @@ TEST(MacroAssemblerIA32SimpleUC16) {
   int start_offset = start_adr - reinterpret_cast<Address>(*seq_input);
   int end_offset = start_offset + seq_input->length() * sizeof(uc16);
 
-  bool success = RegExpMacroAssemblerIA32::Execute(*code,
-                                                   seq_input.location(),
-                                                   start_offset,
-                                                   end_offset,
-                                                   captures,
-                                                   true);
+  RegExpMacroAssemblerIA32::Result result =
+      RegExpMacroAssemblerIA32::Execute(*code,
+                                        seq_input.location(),
+                                        start_offset,
+                                        end_offset,
+                                        captures,
+                                        true);
 
-  CHECK(success);
+  CHECK_EQ(RegExpMacroAssemblerIA32::SUCCESS, result);
   CHECK_EQ(0, captures[0]);
   CHECK_EQ(3, captures[1]);
   CHECK_EQ(-1, captures[2]);
@@ -727,23 +738,20 @@ TEST(MacroAssemblerIA32SimpleUC16) {
   start_offset = start_adr - reinterpret_cast<Address>(*seq_input);
   end_offset = start_offset + seq_input->length() * sizeof(uc16);
 
-  success = RegExpMacroAssemblerIA32::Execute(*code,
-                                              seq_input.location(),
-                                              start_offset,
-                                              end_offset,
-                                              captures,
-                                              true);
+  result = RegExpMacroAssemblerIA32::Execute(*code,
+                                             seq_input.location(),
+                                             start_offset,
+                                             end_offset,
+                                             captures,
+                                             true);
 
-  CHECK(!success);
+  CHECK_EQ(RegExpMacroAssemblerIA32::FAILURE, result);
 }
 
 
 TEST(MacroAssemblerIA32Backtrack) {
   V8::Initialize(NULL);
-
-  // regexp-macro-assembler-ia32 needs a handle scope to allocate
-  // byte-arrays for constants.
-  v8::HandleScope scope;
+  ContextInitializer initializer;
 
   RegExpMacroAssemblerIA32 m(RegExpMacroAssemblerIA32::ASCII, 0);
 
@@ -768,23 +776,21 @@ TEST(MacroAssemblerIA32Backtrack) {
   int start_offset = start_adr - reinterpret_cast<Address>(*seq_input);
   int end_offset = start_offset + seq_input->length();
 
-  bool success = RegExpMacroAssemblerIA32::Execute(*code,
-                                                   seq_input.location(),
-                                                   start_offset,
-                                                   end_offset,
-                                                   NULL,
-                                                   true);
+  RegExpMacroAssemblerIA32::Result result =
+      RegExpMacroAssemblerIA32::Execute(*code,
+                                        seq_input.location(),
+                                        start_offset,
+                                        end_offset,
+                                        NULL,
+                                        true);
 
-  CHECK(!success);
+  CHECK_EQ(RegExpMacroAssemblerIA32::FAILURE, result);
 }
 
 
 TEST(MacroAssemblerIA32BackReference) {
   V8::Initialize(NULL);
-
-  // regexp-macro-assembler-ia32 needs a handle scope to allocate
-  // byte-arrays for constants.
-  v8::HandleScope scope;
+  ContextInitializer initializer;
 
   RegExpMacroAssemblerIA32 m(RegExpMacroAssemblerIA32::ASCII, 3);
 
@@ -814,14 +820,15 @@ TEST(MacroAssemblerIA32BackReference) {
   int end_offset = start_offset + seq_input->length();
 
   int output[3];
-  bool success = RegExpMacroAssemblerIA32::Execute(*code,
-                                                   seq_input.location(),
-                                                   start_offset,
-                                                   end_offset,
-                                                   output,
-                                                   true);
-
-  CHECK(success);
+  RegExpMacroAssemblerIA32::Result result =
+      RegExpMacroAssemblerIA32::Execute(*code,
+                                        seq_input.location(),
+                                        start_offset,
+                                        end_offset,
+                                        output,
+                                        true);
+
+  CHECK_EQ(RegExpMacroAssemblerIA32::SUCCESS, result);
   CHECK_EQ(0, output[0]);
   CHECK_EQ(2, output[1]);
   CHECK_EQ(6, output[2]);
@@ -830,10 +837,7 @@ TEST(MacroAssemblerIA32BackReference) {
 
 TEST(MacroAssemblerIA32AtStart) {
   V8::Initialize(NULL);
-
-  // regexp-macro-assembler-ia32 needs a handle scope to allocate
-  // byte-arrays for constants.
-  v8::HandleScope scope;
+  ContextInitializer initializer;
 
   RegExpMacroAssemblerIA32 m(RegExpMacroAssemblerIA32::ASCII, 0);
 
@@ -868,24 +872,25 @@ TEST(MacroAssemblerIA32AtStart) {
   int start_offset = start_adr - reinterpret_cast<Address>(*seq_input);
   int end_offset = start_offset + seq_input->length();
 
-  bool success = RegExpMacroAssemblerIA32::Execute(*code,
-                                                   seq_input.location(),
-                                                   start_offset,
-                                                   end_offset,
-                                                   NULL,
-                                                   true);
+  RegExpMacroAssemblerIA32::Result result =
+      RegExpMacroAssemblerIA32::Execute(*code,
+                                        seq_input.location(),
+                                        start_offset,
+                                        end_offset,
+                                        NULL,
+                                        true);
 
-  CHECK(success);
+  CHECK_EQ(RegExpMacroAssemblerIA32::SUCCESS, result);
 
   start_offset += 3;
-  success = RegExpMacroAssemblerIA32::Execute(*code,
+  result = RegExpMacroAssemblerIA32::Execute(*code,
                                               seq_input.location(),
                                               start_offset,
                                               end_offset,
                                               NULL,
                                               false);
 
-  CHECK(success);
+  CHECK_EQ(RegExpMacroAssemblerIA32::SUCCESS, result);
 }
 
 
@@ -893,10 +898,7 @@ TEST(MacroAssemblerIA32AtStart) {
 
 TEST(MacroAssemblerIA32BackRefNoCase) {
   V8::Initialize(NULL);
-
-  // regexp-macro-assembler-ia32 needs a handle scope to allocate
-  // byte-arrays for constants.
-  v8::HandleScope scope;
+  ContextInitializer initializer;
 
   RegExpMacroAssemblerIA32 m(RegExpMacroAssemblerIA32::ASCII, 4);
 
@@ -935,14 +937,15 @@ TEST(MacroAssemblerIA32BackRefNoCase) {
   int end_offset = start_offset + seq_input->length();
 
   int output[4];
-  bool success = RegExpMacroAssemblerIA32::Execute(*code,
-                                                   seq_input.location(),
-                                                   start_offset,
-                                                   end_offset,
-                                                   output,
-                                                   true);
-
-  CHECK(success);
+  RegExpMacroAssemblerIA32::Result result =
+      RegExpMacroAssemblerIA32::Execute(*code,
+                                        seq_input.location(),
+                                        start_offset,
+                                        end_offset,
+                                        output,
+                                        true);
+
+  CHECK_EQ(RegExpMacroAssemblerIA32::SUCCESS, result);
   CHECK_EQ(0, output[0]);
   CHECK_EQ(12, output[1]);
   CHECK_EQ(0, output[2]);
@@ -953,10 +956,7 @@ TEST(MacroAssemblerIA32BackRefNoCase) {
 
 TEST(MacroAssemblerIA32Registers) {
   V8::Initialize(NULL);
-
-  // regexp-macro-assembler-ia32 needs a handle scope to allocate
-  // byte-arrays for constants.
-  v8::HandleScope scope;
+  ContextInitializer initializer;
 
   RegExpMacroAssemblerIA32 m(RegExpMacroAssemblerIA32::ASCII, 5);
 
@@ -1038,14 +1038,15 @@ TEST(MacroAssemblerIA32Registers) {
   int end_offset = start_offset + seq_input->length();
 
   int output[5];
-  bool success = RegExpMacroAssemblerIA32::Execute(*code,
-                                                   seq_input.location(),
-                                                   start_offset,
-                                                   end_offset,
-                                                   output,
-                                                   true);
-
-  CHECK(success);
+  RegExpMacroAssemblerIA32::Result result =
+      RegExpMacroAssemblerIA32::Execute(*code,
+                                        seq_input.location(),
+                                        start_offset,
+                                        end_offset,
+                                        output,
+                                        true);
+
+  CHECK_EQ(RegExpMacroAssemblerIA32::SUCCESS, result);
   CHECK_EQ(0, output[0]);
   CHECK_EQ(3, output[1]);
   CHECK_EQ(6, output[2]);
@@ -1053,6 +1054,45 @@ TEST(MacroAssemblerIA32Registers) {
   CHECK_EQ(9, output[4]);
 }
 
+
+TEST(MacroAssemblerIA32StackOverflow) {
+  V8::Initialize(NULL);
+  ContextInitializer initializer;
+
+  RegExpMacroAssemblerIA32 m(RegExpMacroAssemblerIA32::ASCII, 0);
+
+  Label loop;
+  m.Bind(&loop);
+  m.PushBacktrack(&loop);
+  m.GoTo(&loop);
+
+  Handle<String> source =
+      Factory::NewStringFromAscii(CStrVector("<stack overflow test>"));
+  Handle<Object> code_object = m.GetCode(source);
+  Handle<Code> code = Handle<Code>::cast(code_object);
+
+  // String long enough for test (content doesn't matter).
+  Handle<String> input =
+      Factory::NewStringFromAscii(CStrVector("dummy"));
+  Handle<SeqAsciiString> seq_input = Handle<SeqAsciiString>::cast(input);
+  Address start_adr = seq_input->GetCharsAddress();
+  int start_offset = start_adr - reinterpret_cast<Address>(*seq_input);
+  int end_offset = start_offset + seq_input->length();
+
+  RegExpMacroAssemblerIA32::Result result =
+      RegExpMacroAssemblerIA32::Execute(*code,
+                                        seq_input.location(),
+                                        start_offset,
+                                        end_offset,
+                                        NULL,
+                                        true);
+
+  CHECK_EQ(RegExpMacroAssemblerIA32::EXCEPTION, result);
+  CHECK(Top::has_pending_exception());
+  Top::clear_pending_exception();
+}
+
+
 #endif  // !defined ARM
 
 TEST(AddInverseToTable) {