Deoptimization support for accessors.
authorsvenpanne@chromium.org <svenpanne@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Fri, 17 Aug 2012 10:43:32 +0000 (10:43 +0000)
committersvenpanne@chromium.org <svenpanne@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Fri, 17 Aug 2012 10:43:32 +0000 (10:43 +0000)
Highlights of this CL:

 * Introduced a new opcode in the deoptimizer for a setter stub frame.

 * Added a global setter stub for returning after deoptimizing a setter.

 * We do not need special deopt support for getters, although the getter stub creates an internal frame. The normal machinery works just right for this case, although we generate a stack that can never occur during normal fullcode execution. If this hurts us one day, we can parameterize and reuse the setter deopt machinery.

Review URL: https://chromiumcodereview.appspot.com/10855098

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

22 files changed:
include/v8.h
src/arm/deoptimizer-arm.cc
src/arm/lithium-codegen-arm.cc
src/arm/stub-cache-arm.cc
src/builtins.cc
src/builtins.h
src/deoptimizer.cc
src/deoptimizer.h
src/flag-definitions.h
src/heap.h
src/ia32/deoptimizer-ia32.cc
src/ia32/lithium-codegen-ia32.cc
src/ia32/stub-cache-ia32.cc
src/mips/deoptimizer-mips.cc
src/mips/lithium-codegen-mips.cc
src/mips/stub-cache-mips.cc
src/objects.cc
src/stub-cache.h
src/x64/deoptimizer-x64.cc
src/x64/lithium-codegen-x64.cc
src/x64/stub-cache-x64.cc
test/mjsunit/compiler/inline-accessors.js

index 480cbaa..b210638 100644 (file)
@@ -3968,7 +3968,7 @@ class Internals {
   static const int kNullValueRootIndex = 7;
   static const int kTrueValueRootIndex = 8;
   static const int kFalseValueRootIndex = 9;
-  static const int kEmptySymbolRootIndex = 112;
+  static const int kEmptySymbolRootIndex = 113;
 
   static const int kJSObjectType = 0xaa;
   static const int kFirstNonstringType = 0x80;
index 9eec16f..bf55be2 100644 (file)
@@ -595,6 +595,114 @@ void Deoptimizer::DoComputeConstructStubFrame(TranslationIterator* iterator,
 }
 
 
+void Deoptimizer::DoComputeSetterStubFrame(TranslationIterator* iterator,
+                                           int frame_index) {
+  JSFunction* setter = JSFunction::cast(ComputeLiteral(iterator->Next()));
+  // The receiver and the implicit return value are expected in registers by the
+  // StoreIC, so they don't belong to the output stack frame. This means that we
+  // have to use a height of 0.
+  unsigned height = 0;
+  unsigned height_in_bytes = height * kPointerSize;
+  if (FLAG_trace_deopt) {
+    PrintF("  translating setter stub => height=%u\n", height_in_bytes);
+  }
+
+  // We need 5 stack entries from StackFrame::INTERNAL (lr, fp, cp, frame type,
+  // code object, see MacroAssembler::EnterFrame) + 1 stack entry from setter
+  // stub (implicit return value, see StoreStubCompiler::CompileStoreViaSetter).
+  unsigned fixed_frame_size = (5 + 1) * kPointerSize;
+  unsigned output_frame_size = height_in_bytes + fixed_frame_size;
+
+  // Allocate and store the output frame description.
+  FrameDescription* output_frame =
+      new(output_frame_size) FrameDescription(output_frame_size, setter);
+  output_frame->SetFrameType(StackFrame::INTERNAL);
+
+  // A frame for a setter stub can not be the topmost or bottommost one.
+  ASSERT(frame_index > 0 && frame_index < output_count_ - 1);
+  ASSERT(output_[frame_index] == NULL);
+  output_[frame_index] = output_frame;
+
+  // The top address of the frame is computed from the previous frame's top and
+  // this frame's size.
+  uint32_t top_address = output_[frame_index - 1]->GetTop() - output_frame_size;
+  output_frame->SetTop(top_address);
+
+  unsigned output_offset = output_frame_size;
+
+  // Read caller's PC from the previous frame.
+  output_offset -= kPointerSize;
+  intptr_t callers_pc = output_[frame_index - 1]->GetPc();
+  output_frame->SetFrameSlot(output_offset, callers_pc);
+  if (FLAG_trace_deopt) {
+    PrintF("    0x%08" V8PRIxPTR ": [top + %u] <- 0x%08" V8PRIxPTR
+           " ; caller's pc\n",
+           top_address + output_offset, output_offset, callers_pc);
+  }
+
+  // Read caller's FP from the previous frame, and set this frame's FP.
+  output_offset -= kPointerSize;
+  intptr_t value = output_[frame_index - 1]->GetFp();
+  output_frame->SetFrameSlot(output_offset, value);
+  intptr_t fp_value = top_address + output_offset;
+  output_frame->SetFp(fp_value);
+  if (FLAG_trace_deopt) {
+    PrintF("    0x%08" V8PRIxPTR ": [top + %u] <- 0x%08" V8PRIxPTR
+           " ; caller's fp\n",
+           fp_value, output_offset, value);
+  }
+
+  // The context can be gotten from the previous frame.
+  output_offset -= kPointerSize;
+  value = output_[frame_index - 1]->GetContext();
+  output_frame->SetFrameSlot(output_offset, value);
+  if (FLAG_trace_deopt) {
+    PrintF("    0x%08" V8PRIxPTR ": [top + %u] <- 0x%08" V8PRIxPTR
+           " ; context\n",
+           top_address + output_offset, output_offset, value);
+  }
+
+  // A marker value is used in place of the function.
+  output_offset -= kPointerSize;
+  value = reinterpret_cast<intptr_t>(Smi::FromInt(StackFrame::INTERNAL));
+  output_frame->SetFrameSlot(output_offset, value);
+  if (FLAG_trace_deopt) {
+    PrintF("    0x%08" V8PRIxPTR ": [top + %u] <- 0x%08" V8PRIxPTR
+           " ; function (setter sentinel)\n",
+           top_address + output_offset, output_offset, value);
+  }
+
+  // Get Code object from setter stub.
+  output_offset -= kPointerSize;
+  Code* setter_stub =
+      isolate_->builtins()->builtin(Builtins::kStoreIC_Setter_ForDeopt);
+  value = reinterpret_cast<intptr_t>(setter_stub);
+  output_frame->SetFrameSlot(output_offset, value);
+  if (FLAG_trace_deopt) {
+    PrintF("    0x%08" V8PRIxPTR ": [top + %u] <- 0x%08" V8PRIxPTR
+           " ; code object\n",
+           top_address + output_offset, output_offset, value);
+  }
+
+  // Skip receiver.
+  Translation::Opcode opcode =
+      static_cast<Translation::Opcode>(iterator->Next());
+  iterator->Skip(Translation::NumberOfOperandsFor(opcode));
+
+  // The implicit return value was part of the artificial setter stub
+  // environment.
+  output_offset -= kPointerSize;
+  DoTranslateCommand(iterator, frame_index, output_offset);
+
+  ASSERT(0 == output_offset);
+
+  intptr_t pc = reinterpret_cast<intptr_t>(
+      setter_stub->instruction_start() +
+      isolate_->heap()->setter_stub_deopt_pc_offset()->value());
+  output_frame->SetPc(pc);
+}
+
+
 // This code is very similar to ia32 code, but relies on register names (fp, sp)
 // and how the frame is laid out.
 void Deoptimizer::DoComputeJSFrame(TranslationIterator* iterator,
index cce148c..a66ffef 100644 (file)
@@ -494,7 +494,9 @@ void LCodeGen::WriteTranslation(LEnvironment* environment,
       translation->BeginConstructStubFrame(closure_id, translation_size);
       break;
     case JS_SETTER:
-      // TODO(svenpanne) Implement me!
+      ASSERT(translation_size == 2);
+      ASSERT(height == 0);
+      translation->BeginSetterStubFrame(closure_id);
       break;
     case ARGUMENTS_ADAPTOR:
       translation->BeginArgumentsAdaptorFrame(closure_id, translation_size);
index da4f4b9..a850562 100644 (file)
@@ -2715,10 +2715,12 @@ Handle<Code> StoreStubCompiler::CompileStoreCallback(
 }
 
 
-Handle<Code> StoreStubCompiler::CompileStoreViaSetter(
-    Handle<String> name,
-    Handle<JSObject> receiver,
-    Handle<JSObject> holder,
+#undef __
+#define __ ACCESS_MASM(masm)
+
+
+void StoreStubCompiler::GenerateStoreViaSetter(
+    MacroAssembler* masm,
     Handle<JSFunction> setter) {
   // ----------- S t a t e -------------
   //  -- r0    : value
@@ -2726,23 +2728,23 @@ Handle<Code> StoreStubCompiler::CompileStoreViaSetter(
   //  -- r2    : name
   //  -- lr    : return address
   // -----------------------------------
-  Label miss;
-
-  // Check that the maps haven't changed.
-  __ JumpIfSmi(r1, &miss);
-  CheckPrototypes(receiver, r1, holder, r3, r4, r5, name, &miss);
-
   {
-    FrameScope scope(masm(), StackFrame::INTERNAL);
+    FrameScope scope(masm, StackFrame::INTERNAL);
 
     // Save value register, so we can restore it later.
     __ push(r0);
 
-    // Call the JavaScript setter with the receiver and the value on the stack.
-    __ Push(r1, r0);
-    ParameterCount actual(1);
-    __ InvokeFunction(setter, actual, CALL_FUNCTION, NullCallWrapper(),
-                      CALL_AS_METHOD);
+    if (!setter.is_null()) {
+      // Call the JavaScript setter with receiver and value on the stack.
+      __ Push(r1, r0);
+      ParameterCount actual(1);
+      __ InvokeFunction(setter, actual, CALL_FUNCTION, NullCallWrapper(),
+                        CALL_AS_METHOD);
+    } else {
+      // If we generate a global code snippet for deoptimization only, remember
+      // the place to continue after deoptimization.
+      masm->isolate()->heap()->SetSetterStubDeoptPCOffset(masm->pc_offset());
+    }
 
     // We have to return the passed value, not the return value of the setter.
     __ pop(r0);
@@ -2751,6 +2753,31 @@ Handle<Code> StoreStubCompiler::CompileStoreViaSetter(
     __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
   }
   __ Ret();
+}
+
+
+#undef __
+#define __ ACCESS_MASM(masm())
+
+
+Handle<Code> StoreStubCompiler::CompileStoreViaSetter(
+    Handle<String> name,
+    Handle<JSObject> receiver,
+    Handle<JSObject> holder,
+    Handle<JSFunction> setter) {
+  // ----------- S t a t e -------------
+  //  -- r0    : value
+  //  -- r1    : receiver
+  //  -- r2    : name
+  //  -- lr    : return address
+  // -----------------------------------
+  Label miss;
+
+  // Check that the maps haven't changed.
+  __ JumpIfSmi(r1, &miss);
+  CheckPrototypes(receiver, r1, holder, r3, r4, r5, name, &miss);
+
+  GenerateStoreViaSetter(masm(), setter);
 
   __ bind(&miss);
   Handle<Code> ic = masm()->isolate()->builtins()->StoreIC_Miss();
index 005af03..fa30549 100644 (file)
@@ -35,6 +35,7 @@
 #include "ic-inl.h"
 #include "heap-profiler.h"
 #include "mark-compact.h"
+#include "stub-cache.h"
 #include "vm-state-inl.h"
 
 namespace v8 {
@@ -1388,6 +1389,11 @@ static void Generate_StoreIC_GlobalProxy_Strict(MacroAssembler* masm) {
 }
 
 
+static void Generate_StoreIC_Setter_ForDeopt(MacroAssembler* masm) {
+  StoreStubCompiler::GenerateStoreViaSetter(masm, Handle<JSFunction>());
+}
+
+
 static void Generate_KeyedStoreIC_Generic(MacroAssembler* masm) {
   KeyedStoreIC::GenerateGeneric(masm, kNonStrictMode);
 }
index c0a850a..9bdb0ad 100644 (file)
@@ -157,6 +157,8 @@ enum BuiltinExtraArguments {
                                     kStrictMode)                        \
   V(StoreIC_GlobalProxy_Strict,     STORE_IC, MEGAMORPHIC,              \
                                     kStrictMode)                        \
+  V(StoreIC_Setter_ForDeopt,        STORE_IC, MONOMORPHIC,              \
+                                    kStrictMode)                        \
                                                                         \
   V(KeyedStoreIC_Initialize,        KEYED_STORE_IC, UNINITIALIZED,      \
                                     Code::kNoExtraICState)              \
index c14ae99..8aeee1a 100644 (file)
@@ -589,7 +589,19 @@ void Deoptimizer::DoComputeOutputFrames() {
       case Translation::CONSTRUCT_STUB_FRAME:
         DoComputeConstructStubFrame(&iterator, i);
         break;
-      default:
+      case Translation::SETTER_STUB_FRAME:
+        DoComputeSetterStubFrame(&iterator, i);
+        break;
+      case Translation::BEGIN:
+      case Translation::REGISTER:
+      case Translation::INT32_REGISTER:
+      case Translation::DOUBLE_REGISTER:
+      case Translation::STACK_SLOT:
+      case Translation::INT32_STACK_SLOT:
+      case Translation::DOUBLE_STACK_SLOT:
+      case Translation::LITERAL:
+      case Translation::ARGUMENTS_OBJECT:
+      case Translation::DUPLICATE:
         UNREACHABLE();
         break;
     }
@@ -708,6 +720,7 @@ void Deoptimizer::DoTranslateCommand(TranslationIterator* iterator,
     case Translation::JS_FRAME:
     case Translation::ARGUMENTS_ADAPTOR_FRAME:
     case Translation::CONSTRUCT_STUB_FRAME:
+    case Translation::SETTER_STUB_FRAME:
     case Translation::DUPLICATE:
       UNREACHABLE();
       return;
@@ -895,6 +908,7 @@ bool Deoptimizer::DoOsrTranslateCommand(TranslationIterator* iterator,
     case Translation::JS_FRAME:
     case Translation::ARGUMENTS_ADAPTOR_FRAME:
     case Translation::CONSTRUCT_STUB_FRAME:
+    case Translation::SETTER_STUB_FRAME:
     case Translation::DUPLICATE:
       UNREACHABLE();  // Malformed input.
        return false;
@@ -1350,6 +1364,12 @@ void Translation::BeginConstructStubFrame(int literal_id, unsigned height) {
 }
 
 
+void Translation::BeginSetterStubFrame(int literal_id) {
+  buffer_->Add(SETTER_STUB_FRAME, zone());
+  buffer_->Add(literal_id, zone());
+}
+
+
 void Translation::BeginArgumentsAdaptorFrame(int literal_id, unsigned height) {
   buffer_->Add(ARGUMENTS_ADAPTOR_FRAME, zone());
   buffer_->Add(literal_id, zone());
@@ -1424,6 +1444,7 @@ int Translation::NumberOfOperandsFor(Opcode opcode) {
     case ARGUMENTS_OBJECT:
     case DUPLICATE:
       return 0;
+    case SETTER_STUB_FRAME:
     case REGISTER:
     case INT32_REGISTER:
     case DOUBLE_REGISTER:
@@ -1456,6 +1477,8 @@ const char* Translation::StringFor(Opcode opcode) {
       return "ARGUMENTS_ADAPTOR_FRAME";
     case CONSTRUCT_STUB_FRAME:
       return "CONSTRUCT_STUB_FRAME";
+    case SETTER_STUB_FRAME:
+      return "SETTER_STUB_FRAME";
     case REGISTER:
       return "REGISTER";
     case INT32_REGISTER:
@@ -1512,6 +1535,7 @@ SlotRef SlotRef::ComputeSlotForNextArgument(TranslationIterator* iterator,
     case Translation::JS_FRAME:
     case Translation::ARGUMENTS_ADAPTOR_FRAME:
     case Translation::CONSTRUCT_STUB_FRAME:
+    case Translation::SETTER_STUB_FRAME:
       // Peeled off before getting here.
       break;
 
index d78b032..cd8c9ea 100644 (file)
@@ -284,6 +284,8 @@ class Deoptimizer : public Malloced {
                                       int frame_index);
   void DoComputeConstructStubFrame(TranslationIterator* iterator,
                                    int frame_index);
+  void DoComputeSetterStubFrame(TranslationIterator* iterator,
+                                int frame_index);
   void DoTranslateCommand(TranslationIterator* iterator,
                           int frame_index,
                           unsigned output_offset);
@@ -559,6 +561,7 @@ class Translation BASE_EMBEDDED {
     BEGIN,
     JS_FRAME,
     CONSTRUCT_STUB_FRAME,
+    SETTER_STUB_FRAME,
     ARGUMENTS_ADAPTOR_FRAME,
     REGISTER,
     INT32_REGISTER,
@@ -590,6 +593,7 @@ class Translation BASE_EMBEDDED {
   void BeginJSFrame(BailoutId node_id, int literal_id, unsigned height);
   void BeginArgumentsAdaptorFrame(int literal_id, unsigned height);
   void BeginConstructStubFrame(int literal_id, unsigned height);
+  void BeginSetterStubFrame(int literal_id);
   void StoreRegister(Register reg);
   void StoreInt32Register(Register reg);
   void StoreDoubleRegister(DoubleRegister reg);
index d561950..64e9bb2 100644 (file)
@@ -213,7 +213,7 @@ DEFINE_bool(cache_optimized_code, true,
             "cache optimized code for closures")
 DEFINE_bool(inline_construct, true, "inline constructor calls")
 DEFINE_bool(inline_arguments, true, "inline functions with arguments object")
-DEFINE_bool(inline_accessors, false, "inline JavaScript accessors")
+DEFINE_bool(inline_accessors, true, "inline JavaScript accessors")
 DEFINE_int(loop_weight, 1, "loop weight for representation inference")
 
 DEFINE_bool(optimize_for_in, true,
index 5e5ba52..121d1b9 100644 (file)
@@ -150,7 +150,8 @@ namespace internal {
   V(Smi, real_stack_limit, RealStackLimit)                                     \
   V(StringDictionary, intrinsic_function_names, IntrinsicFunctionNames)        \
   V(Smi, arguments_adaptor_deopt_pc_offset, ArgumentsAdaptorDeoptPCOffset)     \
-  V(Smi, construct_stub_deopt_pc_offset, ConstructStubDeoptPCOffset)
+  V(Smi, construct_stub_deopt_pc_offset, ConstructStubDeoptPCOffset)           \
+  V(Smi, setter_stub_deopt_pc_offset, SetterStubDeoptPCOffset)
 
 #define ROOT_LIST(V)                                  \
   STRONG_ROOT_LIST(V)                                 \
@@ -1585,6 +1586,11 @@ class Heap {
     set_construct_stub_deopt_pc_offset(Smi::FromInt(pc_offset));
   }
 
+  void SetSetterStubDeoptPCOffset(int pc_offset) {
+    ASSERT(setter_stub_deopt_pc_offset() == Smi::FromInt(0));
+    set_setter_stub_deopt_pc_offset(Smi::FromInt(pc_offset));
+  }
+
   // For post mortem debugging.
   void RememberUnmappedPage(Address page, bool compacted);
 
index 14e76a2..4057ba7 100644 (file)
@@ -694,6 +694,115 @@ void Deoptimizer::DoComputeConstructStubFrame(TranslationIterator* iterator,
 }
 
 
+void Deoptimizer::DoComputeSetterStubFrame(TranslationIterator* iterator,
+                                           int frame_index) {
+  JSFunction* setter = JSFunction::cast(ComputeLiteral(iterator->Next()));
+  // The receiver and the implicit return value are expected in registers by the
+  // StoreIC, so they don't belong to the output stack frame. This means that we
+  // have to use a height of 0.
+  unsigned height = 0;
+  unsigned height_in_bytes = height * kPointerSize;
+  if (FLAG_trace_deopt) {
+    PrintF("  translating setter stub => height=%u\n", height_in_bytes);
+  }
+
+  // We need 1 stack entry for the return address + 4 stack entries from
+  // StackFrame::INTERNAL (FP, context, frame type, code object, see
+  // MacroAssembler::EnterFrame) + 1 stack entry from setter stub (implicit
+  // return value, see StoreStubCompiler::CompileStoreViaSetter).
+  unsigned fixed_frame_size = (1 + 4 + 1) * kPointerSize;
+  unsigned output_frame_size = height_in_bytes + fixed_frame_size;
+
+  // Allocate and store the output frame description.
+  FrameDescription* output_frame =
+      new(output_frame_size) FrameDescription(output_frame_size, setter);
+  output_frame->SetFrameType(StackFrame::INTERNAL);
+
+  // A frame for a setter stub can not be the topmost or bottommost one.
+  ASSERT(frame_index > 0 && frame_index < output_count_ - 1);
+  ASSERT(output_[frame_index] == NULL);
+  output_[frame_index] = output_frame;
+
+  // The top address of the frame is computed from the previous frame's top and
+  // this frame's size.
+  intptr_t top_address = output_[frame_index - 1]->GetTop() - output_frame_size;
+  output_frame->SetTop(top_address);
+
+  unsigned output_offset = output_frame_size;
+
+  // Read caller's PC from the previous frame.
+  output_offset -= kPointerSize;
+  intptr_t callers_pc = output_[frame_index - 1]->GetPc();
+  output_frame->SetFrameSlot(output_offset, callers_pc);
+  if (FLAG_trace_deopt) {
+    PrintF("    0x%08" V8PRIxPTR ": [top + %u] <- 0x%08" V8PRIxPTR
+           " ; caller's pc\n",
+           top_address + output_offset, output_offset, callers_pc);
+  }
+
+  // Read caller's FP from the previous frame, and set this frame's FP.
+  output_offset -= kPointerSize;
+  intptr_t value = output_[frame_index - 1]->GetFp();
+  output_frame->SetFrameSlot(output_offset, value);
+  intptr_t fp_value = top_address + output_offset;
+  output_frame->SetFp(fp_value);
+  if (FLAG_trace_deopt) {
+    PrintF("    0x%08" V8PRIxPTR ": [top + %u] <- 0x%08" V8PRIxPTR
+           " ; caller's fp\n",
+           fp_value, output_offset, value);
+  }
+
+  // The context can be gotten from the previous frame.
+  output_offset -= kPointerSize;
+  value = output_[frame_index - 1]->GetContext();
+  output_frame->SetFrameSlot(output_offset, value);
+  if (FLAG_trace_deopt) {
+    PrintF("    0x%08" V8PRIxPTR ": [top + %u] <- 0x%08" V8PRIxPTR
+           " ; context\n",
+           top_address + output_offset, output_offset, value);
+  }
+
+  // A marker value is used in place of the function.
+  output_offset -= kPointerSize;
+  value = reinterpret_cast<intptr_t>(Smi::FromInt(StackFrame::INTERNAL));
+  output_frame->SetFrameSlot(output_offset, value);
+  if (FLAG_trace_deopt) {
+    PrintF("    0x%08" V8PRIxPTR ": [top + %u] <- 0x%08" V8PRIxPTR
+           " ; function (setter sentinel)\n",
+           top_address + output_offset, output_offset, value);
+  }
+
+  // Get Code object from setter stub.
+  output_offset -= kPointerSize;
+  Code* setter_stub =
+      isolate_->builtins()->builtin(Builtins::kStoreIC_Setter_ForDeopt);
+  value = reinterpret_cast<intptr_t>(setter_stub);
+  output_frame->SetFrameSlot(output_offset, value);
+  if (FLAG_trace_deopt) {
+    PrintF("    0x%08" V8PRIxPTR ": [top + %u] <- 0x%08" V8PRIxPTR
+           " ; code object\n",
+           top_address + output_offset, output_offset, value);
+  }
+
+  // Skip receiver.
+  Translation::Opcode opcode =
+      static_cast<Translation::Opcode>(iterator->Next());
+  iterator->Skip(Translation::NumberOfOperandsFor(opcode));
+
+  // The implicit return value was part of the artificial setter stub
+  // environment.
+  output_offset -= kPointerSize;
+  DoTranslateCommand(iterator, frame_index, output_offset);
+
+  ASSERT(0 == output_offset);
+
+  intptr_t pc = reinterpret_cast<intptr_t>(
+      setter_stub->instruction_start() +
+      isolate_->heap()->setter_stub_deopt_pc_offset()->value());
+  output_frame->SetPc(pc);
+}
+
+
 void Deoptimizer::DoComputeJSFrame(TranslationIterator* iterator,
                                    int frame_index) {
   BailoutId node_id = BailoutId(iterator->Next());
index 829bda0..b9783d0 100644 (file)
@@ -433,7 +433,9 @@ void LCodeGen::WriteTranslation(LEnvironment* environment,
       translation->BeginConstructStubFrame(closure_id, translation_size);
       break;
     case JS_SETTER:
-      // TODO(svenpanne) Implement me!
+      ASSERT(translation_size == 2);
+      ASSERT(height == 0);
+      translation->BeginSetterStubFrame(closure_id);
       break;
     case ARGUMENTS_ADAPTOR:
       translation->BeginArgumentsAdaptorFrame(closure_id, translation_size);
index 8d8d27d..d612772 100644 (file)
@@ -2647,10 +2647,12 @@ Handle<Code> StoreStubCompiler::CompileStoreCallback(
 }
 
 
-Handle<Code> StoreStubCompiler::CompileStoreViaSetter(
-    Handle<String> name,
-    Handle<JSObject> receiver,
-    Handle<JSObject> holder,
+#undef __
+#define __ ACCESS_MASM(masm)
+
+
+void StoreStubCompiler::GenerateStoreViaSetter(
+    MacroAssembler* masm,
     Handle<JSFunction> setter) {
   // ----------- S t a t e -------------
   //  -- eax    : value
@@ -2658,26 +2660,24 @@ Handle<Code> StoreStubCompiler::CompileStoreViaSetter(
   //  -- edx    : receiver
   //  -- esp[0] : return address
   // -----------------------------------
-  Label miss;
-
-  // Check that the maps haven't changed, preserving the name register.
-  __ push(ecx);
-  __ JumpIfSmi(edx, &miss);
-  CheckPrototypes(receiver, edx, holder, ebx, ecx, edi, name, &miss);
-  __ pop(ecx);
-
   {
-    FrameScope scope(masm(), StackFrame::INTERNAL);
+    FrameScope scope(masm, StackFrame::INTERNAL);
 
     // Save value register, so we can restore it later.
     __ push(eax);
 
-    // Call the JavaScript setter with the receiver and the value on the stack.
-    __ push(edx);
-    __ push(eax);
-    ParameterCount actual(1);
-    __ InvokeFunction(setter, actual, CALL_FUNCTION, NullCallWrapper(),
-                      CALL_AS_METHOD);
+    if (!setter.is_null()) {
+      // Call the JavaScript setter with receiver and value on the stack.
+      __ push(edx);
+      __ push(eax);
+      ParameterCount actual(1);
+      __ InvokeFunction(setter, actual, CALL_FUNCTION, NullCallWrapper(),
+                        CALL_AS_METHOD);
+    } else {
+      // If we generate a global code snippet for deoptimization only, remember
+      // the place to continue after deoptimization.
+      masm->isolate()->heap()->SetSetterStubDeoptPCOffset(masm->pc_offset());
+    }
 
     // We have to return the passed value, not the return value of the setter.
     __ pop(eax);
@@ -2686,6 +2686,33 @@ Handle<Code> StoreStubCompiler::CompileStoreViaSetter(
     __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
   }
   __ ret(0);
+}
+
+
+#undef __
+#define __ ACCESS_MASM(masm())
+
+
+Handle<Code> StoreStubCompiler::CompileStoreViaSetter(
+    Handle<String> name,
+    Handle<JSObject> receiver,
+    Handle<JSObject> holder,
+    Handle<JSFunction> setter) {
+  // ----------- S t a t e -------------
+  //  -- eax    : value
+  //  -- ecx    : name
+  //  -- edx    : receiver
+  //  -- esp[0] : return address
+  // -----------------------------------
+  Label miss;
+
+  // Check that the maps haven't changed, preserving the name register.
+  __ push(ecx);
+  __ JumpIfSmi(edx, &miss);
+  CheckPrototypes(receiver, edx, holder, ebx, ecx, edi, name, &miss);
+  __ pop(ecx);
+
+  GenerateStoreViaSetter(masm(), setter);
 
   __ bind(&miss);
   __ pop(ecx);
index 14af894..666b8b3 100644 (file)
@@ -582,6 +582,114 @@ void Deoptimizer::DoComputeConstructStubFrame(TranslationIterator* iterator,
 }
 
 
+void Deoptimizer::DoComputeSetterStubFrame(TranslationIterator* iterator,
+                                           int frame_index) {
+  JSFunction* setter = JSFunction::cast(ComputeLiteral(iterator->Next()));
+  // The receiver and the implicit return value are expected in registers by the
+  // StoreIC, so they don't belong to the output stack frame. This means that we
+  // have to use a height of 0.
+  unsigned height = 0;
+  unsigned height_in_bytes = height * kPointerSize;
+  if (FLAG_trace_deopt) {
+    PrintF("  translating setter stub => height=%u\n", height_in_bytes);
+  }
+
+  // We need 5 stack entries from StackFrame::INTERNAL (ra, fp, cp, frame type,
+  // code object, see MacroAssembler::EnterFrame) + 1 stack entry from setter
+  // stub (implicit return value, see StoreStubCompiler::CompileStoreViaSetter).
+  unsigned fixed_frame_size = (5 + 1) * kPointerSize;
+  unsigned output_frame_size = height_in_bytes + fixed_frame_size;
+
+  // Allocate and store the output frame description.
+  FrameDescription* output_frame =
+      new(output_frame_size) FrameDescription(output_frame_size, setter);
+  output_frame->SetFrameType(StackFrame::INTERNAL);
+
+  // A frame for a setter stub can not be the topmost or bottommost one.
+  ASSERT(frame_index > 0 && frame_index < output_count_ - 1);
+  ASSERT(output_[frame_index] == NULL);
+  output_[frame_index] = output_frame;
+
+  // The top address of the frame is computed from the previous frame's top and
+  // this frame's size.
+  uint32_t top_address = output_[frame_index - 1]->GetTop() - output_frame_size;
+  output_frame->SetTop(top_address);
+
+  unsigned output_offset = output_frame_size;
+
+  // Read caller's PC from the previous frame.
+  output_offset -= kPointerSize;
+  intptr_t value = output_[frame_index - 1]->GetPc();
+  output_frame->SetFrameSlot(output_offset, value);
+  if (FLAG_trace_deopt) {
+    PrintF("    0x%08" V8PRIxPTR ": [top + %u] <- 0x%08" V8PRIxPTR
+           " ; caller's pc\n",
+           top_address + output_offset, output_offset, value);
+  }
+
+  // Read caller's FP from the previous frame, and set this frame's FP.
+  output_offset -= kPointerSize;
+  value = output_[frame_index - 1]->GetFp();
+  output_frame->SetFrameSlot(output_offset, value);
+  intptr_t fp_value = top_address + output_offset;
+  output_frame->SetFp(fp_value);
+  if (FLAG_trace_deopt) {
+    PrintF("    0x%08" V8PRIxPTR ": [top + %u] <- 0x%08" V8PRIxPTR
+           " ; caller's fp\n",
+           fp_value, output_offset, value);
+  }
+
+  // The context can be gotten from the previous frame.
+  output_offset -= kPointerSize;
+  value = output_[frame_index - 1]->GetContext();
+  output_frame->SetFrameSlot(output_offset, value);
+  if (FLAG_trace_deopt) {
+    PrintF("    0x%08" V8PRIxPTR ": [top + %u] <- 0x%08" V8PRIxPTR
+           " ; context\n",
+           top_address + output_offset, output_offset, value);
+  }
+
+  // A marker value is used in place of the function.
+  output_offset -= kPointerSize;
+  value = reinterpret_cast<intptr_t>(Smi::FromInt(StackFrame::INTERNAL));
+  output_frame->SetFrameSlot(output_offset, value);
+  if (FLAG_trace_deopt) {
+    PrintF("    0x%08" V8PRIxPTR ": [top + %u] <- 0x%08" V8PRIxPTR
+           " ; function (setter sentinel)\n",
+           top_address + output_offset, output_offset, value);
+  }
+
+  // Get Code object from setter stub.
+  output_offset -= kPointerSize;
+  Code* setter_stub =
+      isolate_->builtins()->builtin(Builtins::kStoreIC_Setter_ForDeopt);
+  value = reinterpret_cast<intptr_t>(setter_stub);
+  output_frame->SetFrameSlot(output_offset, value);
+  if (FLAG_trace_deopt) {
+    PrintF("    0x%08" V8PRIxPTR ": [top + %u] <- 0x%08" V8PRIxPTR
+           " ; code object\n",
+           top_address + output_offset, output_offset, value);
+  }
+
+  // Skip receiver.
+  Translation::Opcode opcode =
+      static_cast<Translation::Opcode>(iterator->Next());
+  iterator->Skip(Translation::NumberOfOperandsFor(opcode));
+
+  // The implicit return value was part of the artificial setter stub
+  // environment.
+  output_offset -= kPointerSize;
+  DoTranslateCommand(iterator, frame_index, output_offset);
+
+  ASSERT(0 == output_offset);
+
+  intptr_t pc = reinterpret_cast<intptr_t>(
+      setter_stub->instruction_start() +
+      isolate_->heap()->setter_stub_deopt_pc_offset()->value());
+  output_frame->SetPc(pc);
+}
+
+
 // This code is very similar to ia32/arm code, but relies on register names
 // (fp, sp) and how the frame is laid out.
 void Deoptimizer::DoComputeJSFrame(TranslationIterator* iterator,
index d7ccd69..b0496d2 100644 (file)
@@ -462,7 +462,9 @@ void LCodeGen::WriteTranslation(LEnvironment* environment,
       translation->BeginConstructStubFrame(closure_id, translation_size);
       break;
     case JS_SETTER:
-      // TODO(svenpanne) Implement me!
+      ASSERT(translation_size == 2);
+      ASSERT(height == 0);
+      translation->BeginSetterStubFrame(closure_id);
       break;
     case ARGUMENTS_ADAPTOR:
       translation->BeginArgumentsAdaptorFrame(closure_id, translation_size);
index 89f1b50..b1d556a 100644 (file)
@@ -2718,10 +2718,12 @@ Handle<Code> StoreStubCompiler::CompileStoreCallback(
 }
 
 
-Handle<Code> StoreStubCompiler::CompileStoreViaSetter(
-    Handle<String> name,
-    Handle<JSObject> receiver,
-    Handle<JSObject> holder,
+#undef __
+#define __ ACCESS_MASM(masm)
+
+
+void StoreStubCompiler::GenerateStoreViaSetter(
+    MacroAssembler* masm,
     Handle<JSFunction> setter) {
   // ----------- S t a t e -------------
   //  -- a0    : value
@@ -2729,24 +2731,24 @@ Handle<Code> StoreStubCompiler::CompileStoreViaSetter(
   //  -- a2    : name
   //  -- ra    : return address
   // -----------------------------------
-  Label miss;
-
-  // Check that the maps haven't changed.
-  __ JumpIfSmi(a1, &miss);
-  CheckPrototypes(receiver, a1, holder, a3, t0, t1, name, &miss);
-
   {
-    FrameScope scope(masm(), StackFrame::INTERNAL);
+    FrameScope scope(masm, StackFrame::INTERNAL);
 
     // Save value register, so we can restore it later.
     __ push(a0);
 
-    // Call the JavaScript setter with the receiver and the value on the stack.
-    __ push(a1);
-    __ push(a0);
-    ParameterCount actual(1);
-    __ InvokeFunction(setter, actual, CALL_FUNCTION, NullCallWrapper(),
-                      CALL_AS_METHOD);
+    if (!setter.is_null()) {
+      // Call the JavaScript setter with receiver and value on the stack.
+      __ push(a1);
+      __ push(a0);
+      ParameterCount actual(1);
+      __ InvokeFunction(setter, actual, CALL_FUNCTION, NullCallWrapper(),
+                        CALL_AS_METHOD);
+    } else {
+      // If we generate a global code snippet for deoptimization only, remember
+      // the place to continue after deoptimization.
+      masm->isolate()->heap()->SetSetterStubDeoptPCOffset(masm->pc_offset());
+    }
 
     // We have to return the passed value, not the return value of the setter.
     __ pop(v0);
@@ -2755,6 +2757,31 @@ Handle<Code> StoreStubCompiler::CompileStoreViaSetter(
     __ lw(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
   }
   __ Ret();
+}
+
+
+#undef __
+#define __ ACCESS_MASM(masm())
+
+
+Handle<Code> StoreStubCompiler::CompileStoreViaSetter(
+    Handle<String> name,
+    Handle<JSObject> receiver,
+    Handle<JSObject> holder,
+    Handle<JSFunction> setter) {
+  // ----------- S t a t e -------------
+  //  -- a0    : value
+  //  -- a1    : receiver
+  //  -- a2    : name
+  //  -- ra    : return address
+  // -----------------------------------
+  Label miss;
+
+  // Check that the maps haven't changed.
+  __ JumpIfSmi(a1, &miss);
+  CheckPrototypes(receiver, a1, holder, a3, t0, t1, name, &miss);
+
+  GenerateStoreViaSetter(masm(), setter);
 
   __ bind(&miss);
   Handle<Code> ic = masm()->isolate()->builtins()->StoreIC_Miss();
index b81e9d4..5149ae1 100644 (file)
@@ -8314,6 +8314,16 @@ void DeoptimizationInputData::DeoptimizationInputDataPrint(FILE* out) {
           break;
         }
 
+        case Translation::SETTER_STUB_FRAME: {
+          int function_id = iterator.Next();
+          JSFunction* function =
+              JSFunction::cast(LiteralArray()->get(function_id));
+          PrintF(out, "{function=");
+          function->PrintName(out);
+          PrintF(out, "}");
+          break;
+        }
+
         case Translation::DUPLICATE:
           break;
 
index e79691c..1c6c09b 100644 (file)
@@ -708,6 +708,9 @@ class StoreStubCompiler: public StubCompiler {
                                     Handle<AccessorInfo> callback,
                                     Handle<String> name);
 
+  static void GenerateStoreViaSetter(MacroAssembler* masm,
+                                     Handle<JSFunction> setter);
+
   Handle<Code> CompileStoreViaSetter(Handle<String> name,
                                      Handle<JSObject> receiver,
                                      Handle<JSObject> holder,
index 3cbde4f..a0cdf33 100644 (file)
@@ -591,6 +591,115 @@ void Deoptimizer::DoComputeConstructStubFrame(TranslationIterator* iterator,
 }
 
 
+void Deoptimizer::DoComputeSetterStubFrame(TranslationIterator* iterator,
+                                           int frame_index) {
+  JSFunction* setter = JSFunction::cast(ComputeLiteral(iterator->Next()));
+  // The receiver and the implicit return value are expected in registers by the
+  // StoreIC, so they don't belong to the output stack frame. This means that we
+  // have to use a height of 0.
+  unsigned height = 0;
+  unsigned height_in_bytes = height * kPointerSize;
+  if (FLAG_trace_deopt) {
+    PrintF("  translating setter stub => height=%u\n", height_in_bytes);
+  }
+
+  // We need 1 stack entry for the return address + 4 stack entries from
+  // StackFrame::INTERNAL (FP, context, frame type, code object, see
+  // MacroAssembler::EnterFrame) + 1 stack entry from setter stub (implicit
+  // return value, see StoreStubCompiler::CompileStoreViaSetter).
+  unsigned fixed_frame_size = (1 + 4 + 1) * kPointerSize;
+  unsigned output_frame_size = height_in_bytes + fixed_frame_size;
+
+  // Allocate and store the output frame description.
+  FrameDescription* output_frame =
+      new(output_frame_size) FrameDescription(output_frame_size, setter);
+  output_frame->SetFrameType(StackFrame::INTERNAL);
+
+  // A frame for a setter stub can not be the topmost or bottommost one.
+  ASSERT(frame_index > 0 && frame_index < output_count_ - 1);
+  ASSERT(output_[frame_index] == NULL);
+  output_[frame_index] = output_frame;
+
+  // The top address of the frame is computed from the previous frame's top and
+  // this frame's size.
+  intptr_t top_address = output_[frame_index - 1]->GetTop() - output_frame_size;
+  output_frame->SetTop(top_address);
+
+  unsigned output_offset = output_frame_size;
+
+  // Read caller's PC from the previous frame.
+  output_offset -= kPointerSize;
+  intptr_t callers_pc = output_[frame_index - 1]->GetPc();
+  output_frame->SetFrameSlot(output_offset, callers_pc);
+  if (FLAG_trace_deopt) {
+    PrintF("    0x%08" V8PRIxPTR ": [top + %u] <- 0x%08" V8PRIxPTR
+           " ; caller's pc\n",
+           top_address + output_offset, output_offset, callers_pc);
+  }
+
+  // Read caller's FP from the previous frame, and set this frame's FP.
+  output_offset -= kPointerSize;
+  intptr_t value = output_[frame_index - 1]->GetFp();
+  output_frame->SetFrameSlot(output_offset, value);
+  intptr_t fp_value = top_address + output_offset;
+  output_frame->SetFp(fp_value);
+  if (FLAG_trace_deopt) {
+    PrintF("    0x%08" V8PRIxPTR ": [top + %u] <- 0x%08" V8PRIxPTR
+           " ; caller's fp\n",
+           fp_value, output_offset, value);
+  }
+
+  // The context can be gotten from the previous frame.
+  output_offset -= kPointerSize;
+  value = output_[frame_index - 1]->GetContext();
+  output_frame->SetFrameSlot(output_offset, value);
+  if (FLAG_trace_deopt) {
+    PrintF("    0x%08" V8PRIxPTR ": [top + %u] <- 0x%08" V8PRIxPTR
+           " ; context\n",
+           top_address + output_offset, output_offset, value);
+  }
+
+  // A marker value is used in place of the function.
+  output_offset -= kPointerSize;
+  value = reinterpret_cast<intptr_t>(Smi::FromInt(StackFrame::INTERNAL));
+  output_frame->SetFrameSlot(output_offset, value);
+  if (FLAG_trace_deopt) {
+    PrintF("    0x%08" V8PRIxPTR ": [top + %u] <- 0x%08" V8PRIxPTR
+           " ; function (setter sentinel)\n",
+           top_address + output_offset, output_offset, value);
+  }
+
+  // Get Code object from setter stub.
+  output_offset -= kPointerSize;
+  Code* setter_stub =
+      isolate_->builtins()->builtin(Builtins::kStoreIC_Setter_ForDeopt);
+  value = reinterpret_cast<intptr_t>(setter_stub);
+  output_frame->SetFrameSlot(output_offset, value);
+  if (FLAG_trace_deopt) {
+    PrintF("    0x%08" V8PRIxPTR ": [top + %u] <- 0x%08" V8PRIxPTR
+           " ; code object\n",
+           top_address + output_offset, output_offset, value);
+  }
+
+  // Skip receiver.
+  Translation::Opcode opcode =
+      static_cast<Translation::Opcode>(iterator->Next());
+  iterator->Skip(Translation::NumberOfOperandsFor(opcode));
+
+  // The implicit return value was part of the artificial setter stub
+  // environment.
+  output_offset -= kPointerSize;
+  DoTranslateCommand(iterator, frame_index, output_offset);
+
+  ASSERT(0 == output_offset);
+
+  intptr_t pc = reinterpret_cast<intptr_t>(
+      setter_stub->instruction_start() +
+      isolate_->heap()->setter_stub_deopt_pc_offset()->value());
+  output_frame->SetPc(pc);
+}
+
+
 void Deoptimizer::DoComputeJSFrame(TranslationIterator* iterator,
                                    int frame_index) {
   BailoutId node_id = BailoutId(iterator->Next());
index 65d9915..4e35b4e 100644 (file)
@@ -381,7 +381,9 @@ void LCodeGen::WriteTranslation(LEnvironment* environment,
       translation->BeginConstructStubFrame(closure_id, translation_size);
       break;
     case JS_SETTER:
-      // TODO(svenpanne) Implement me!
+      ASSERT(translation_size == 2);
+      ASSERT(height == 0);
+      translation->BeginSetterStubFrame(closure_id);
       break;
     case ARGUMENTS_ADAPTOR:
       translation->BeginArgumentsAdaptorFrame(closure_id, translation_size);
index 0caffac..5c23a1d 100644 (file)
@@ -2475,10 +2475,12 @@ Handle<Code> StoreStubCompiler::CompileStoreCallback(
 }
 
 
-Handle<Code> StoreStubCompiler::CompileStoreViaSetter(
-    Handle<String> name,
-    Handle<JSObject> receiver,
-    Handle<JSObject> holder,
+#undef __
+#define __ ACCESS_MASM(masm)
+
+
+void StoreStubCompiler::GenerateStoreViaSetter(
+    MacroAssembler* masm,
     Handle<JSFunction> setter) {
   // ----------- S t a t e -------------
   //  -- rax    : value
@@ -2486,24 +2488,24 @@ Handle<Code> StoreStubCompiler::CompileStoreViaSetter(
   //  -- rdx    : receiver
   //  -- rsp[0] : return address
   // -----------------------------------
-  Label miss;
-
-  // Check that the maps haven't changed.
-  __ JumpIfSmi(rdx, &miss);
-  CheckPrototypes(receiver, rdx, holder, rbx, r8, rdi, name, &miss);
-
   {
-    FrameScope scope(masm(), StackFrame::INTERNAL);
+    FrameScope scope(masm, StackFrame::INTERNAL);
 
     // Save value register, so we can restore it later.
     __ push(rax);
 
-    // Call the JavaScript setter with the receiver and the value on the stack.
-    __ push(rdx);
-    __ push(rax);
-    ParameterCount actual(1);
-    __ InvokeFunction(setter, actual, CALL_FUNCTION, NullCallWrapper(),
-                      CALL_AS_METHOD);
+    if (!setter.is_null()) {
+      // Call the JavaScript setter with receiver and value on the stack.
+      __ push(rdx);
+      __ push(rax);
+      ParameterCount actual(1);
+      __ InvokeFunction(setter, actual, CALL_FUNCTION, NullCallWrapper(),
+                        CALL_AS_METHOD);
+    } else {
+      // If we generate a global code snippet for deoptimization only, remember
+      // the place to continue after deoptimization.
+      masm->isolate()->heap()->SetSetterStubDeoptPCOffset(masm->pc_offset());
+    }
 
     // We have to return the passed value, not the return value of the setter.
     __ pop(rax);
@@ -2512,6 +2514,31 @@ Handle<Code> StoreStubCompiler::CompileStoreViaSetter(
     __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
   }
   __ ret(0);
+}
+
+
+#undef __
+#define __ ACCESS_MASM(masm())
+
+
+Handle<Code> StoreStubCompiler::CompileStoreViaSetter(
+    Handle<String> name,
+    Handle<JSObject> receiver,
+    Handle<JSObject> holder,
+    Handle<JSFunction> setter) {
+  // ----------- S t a t e -------------
+  //  -- rax    : value
+  //  -- rcx    : name
+  //  -- rdx    : receiver
+  //  -- rsp[0] : return address
+  // -----------------------------------
+  Label miss;
+
+  // Check that the maps haven't changed.
+  __ JumpIfSmi(rdx, &miss);
+  CheckPrototypes(receiver, rdx, holder, rbx, r8, rdi, name, &miss);
+
+  GenerateStoreViaSetter(masm(), setter);
 
   __ bind(&miss);
   Handle<Code> ic = isolate()->builtins()->StoreIC_Miss();
index 41302c1..a4cf7ae 100644 (file)
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-// Flags: --allow-natives-syntax --inline-accessors
+// Flags: --allow-natives-syntax --inline-accessors --max-opt-count=100
 
 var accessorCallCount, setterArgument, setterValue, obj, forceDeopt;
 
 // -----------------------------------------------------------------------------
 // Helpers for testing inlining of getters.
 
-function TestInlinedGetter(context, expected) {
-  forceDeopt = 0;
+function TestInlinedGetter(context, obj, expected) {
+  forceDeopt = { deopt: 0 };
   accessorCallCount = 0;
 
-  assertEquals(expected, context());
+  assertEquals(expected, context(obj));
   assertEquals(1, accessorCallCount);
 
-  assertEquals(expected, context());
+  assertEquals(expected, context(obj));
   assertEquals(2, accessorCallCount);
 
   %OptimizeFunctionOnNextCall(context);
-  assertEquals(expected, context());
+  assertEquals(expected, context(obj));
   assertEquals(3, accessorCallCount);
 
-  %DeoptimizeFunction(context);
-  %ClearFunctionTypeFeedback(context);
+  forceDeopt = { /* empty*/ };
+  assertEquals(expected, context(obj));
+  assertEquals(4, accessorCallCount);
 }
 
 
-function TestGetterInAllContexts(obj, expected) {
-  function value_context() {
-    return obj.getterProperty;
-  }
-  TestInlinedGetter(value_context, expected);
-
-  function test_context() {
-    if (obj.getterProperty) {
-      return 111;
-    } else {
-      return 222;
-    }
+function value_context_for_getter(obj) {
+  return obj.getterProperty;
+}
+
+function test_context_for_getter(obj) {
+  if (obj.getterProperty) {
+    return 111;
+  } else {
+    return 222;
   }
-  TestInlinedGetter(test_context, expected ? 111 : 222);
+}
 
-  function effect_context() {
-    obj.getterProperty;
-    return 5678;
+function effect_context_for_getter(obj) {
+  obj.getterProperty;
+  return 5678;
+}
+
+function TryGetter(context, getter, obj, expected, expectException) {
+  try {
+    TestInlinedGetter(context, obj, expected);
+    assertFalse(expectException);
+  } catch (exception) {
+    assertTrue(expectException);
+    assertEquals(7, exception.stack.split('\n').length);
   }
-  TestInlinedGetter(effect_context, 5678);
+  %DeoptimizeFunction(context);
+  %ClearFunctionTypeFeedback(context);
+  %ClearFunctionTypeFeedback(getter);
+}
+
+function TestGetterInAllContexts(getter, obj, expected, expectException) {
+  TryGetter(value_context_for_getter, getter, obj, expected, expectException);
+  TryGetter(test_context_for_getter, getter, obj, expected ? 111 : 222,
+            expectException);
+  TryGetter(effect_context_for_getter, getter, obj, 5678, expectException);
 }
 
 // -----------------------------------------------------------------------------
@@ -79,15 +95,15 @@ function TestGetterInAllContexts(obj, expected) {
 function getter1() {
   assertSame(obj, this);
   accessorCallCount++;
-  forceDeopt + 1;
+  forceDeopt.deopt;
   return 1234;
 }
 
 function ConstrG1() { }
 obj = Object.defineProperty(new ConstrG1(), "getterProperty", { get: getter1 });
-TestGetterInAllContexts(obj, 1234);
+TestGetterInAllContexts(getter1, obj, 1234, false);
 obj = Object.create(obj);
-TestGetterInAllContexts(obj, 1234);
+TestGetterInAllContexts(getter1, obj, 1234, false);
 
 // -----------------------------------------------------------------------------
 // Test getter returning false in all contexts.
@@ -95,15 +111,15 @@ TestGetterInAllContexts(obj, 1234);
 function getter2() {
   assertSame(obj, this);
   accessorCallCount++;
-  forceDeopt + 1;
+  forceDeopt.deopt;
   return false;
 }
 
 function ConstrG2() { }
 obj = Object.defineProperty(new ConstrG2(), "getterProperty", { get: getter2 });
-TestGetterInAllContexts(obj, false);
+TestGetterInAllContexts(getter2, obj, false, false);
 obj = Object.create(obj);
-TestGetterInAllContexts(obj, false);
+TestGetterInAllContexts(getter2, obj, false, false);
 
 // -----------------------------------------------------------------------------
 // Test getter without a return in all contexts.
@@ -111,14 +127,14 @@ TestGetterInAllContexts(obj, false);
 function getter3() {
   assertSame(obj, this);
   accessorCallCount++;
-  forceDeopt + 1;
+  forceDeopt.deopt;
 }
 
 function ConstrG3() { }
 obj = Object.defineProperty(new ConstrG3(), "getterProperty", { get: getter3 });
-TestGetterInAllContexts(obj, undefined);
+TestGetterInAllContexts(getter3, obj, undefined, false);
 obj = Object.create(obj);
-TestGetterInAllContexts(obj, undefined);
+TestGetterInAllContexts(getter3, obj, undefined, false);
 
 // -----------------------------------------------------------------------------
 // Test getter with too many arguments without a return in all contexts.
@@ -127,14 +143,14 @@ function getter4(a) {
   assertSame(obj, this);
   assertEquals(undefined, a);
   accessorCallCount++;
-  forceDeopt + 1;
+  forceDeopt.deopt;
 }
 
 function ConstrG4() { }
 obj = Object.defineProperty(new ConstrG4(), "getterProperty", { get: getter4 });
-TestGetterInAllContexts(obj, undefined);
+TestGetterInAllContexts(getter4, obj, undefined, false);
 obj = Object.create(obj);
-TestGetterInAllContexts(obj, undefined);
+TestGetterInAllContexts(getter4, obj, undefined, false);
 
 // -----------------------------------------------------------------------------
 // Test getter with too many arguments with a return in all contexts.
@@ -143,62 +159,95 @@ function getter5(a) {
   assertSame(obj, this);
   assertEquals(undefined, a);
   accessorCallCount++;
-  forceDeopt + 1;
+  forceDeopt.deopt;
   return 9876;
 }
 
 function ConstrG5() { }
 obj = Object.defineProperty(new ConstrG5(), "getterProperty", { get: getter5 });
-TestGetterInAllContexts(obj, 9876);
+TestGetterInAllContexts(getter5, obj, 9876, false);
+obj = Object.create(obj);
+TestGetterInAllContexts(getter5, obj, 9876, false);
+
+// -----------------------------------------------------------------------------
+// Test getter which throws from optimized code.
+
+function getter6() {
+  assertSame(obj, this);
+  accessorCallCount++;
+  forceDeopt.deopt;
+  if (accessorCallCount == 4) { 123 in null; }
+  return 13579;
+}
+
+function ConstrG6() { }
+obj = Object.defineProperty(new ConstrG6(), "getterProperty", { get: getter6 });
+TestGetterInAllContexts(getter6, obj, 13579, true);
 obj = Object.create(obj);
-TestGetterInAllContexts(obj, 9876);
+TestGetterInAllContexts(getter6, obj, 13579, true);
 
 // -----------------------------------------------------------------------------
 // Helpers for testing inlining of setters.
 
-function TestInlinedSetter(context, value, expected) {
-  forceDeopt = 0;
+function TestInlinedSetter(context, obj, value, expected) {
+  forceDeopt = { deopt: 0 };
   accessorCallCount = 0;
   setterArgument = value;
 
-  assertEquals(expected, context(value));
+  assertEquals(expected, context(obj, value));
   assertEquals(value, setterValue);
   assertEquals(1, accessorCallCount);
 
-  assertEquals(expected, context(value));
+  assertEquals(expected, context(obj, value));
   assertEquals(value, setterValue);
   assertEquals(2, accessorCallCount);
 
   %OptimizeFunctionOnNextCall(context);
-  assertEquals(expected, context(value));
+  assertEquals(expected, context(obj, value));
   assertEquals(value, setterValue);
   assertEquals(3, accessorCallCount);
 
-  %DeoptimizeFunction(context);
-  %ClearFunctionTypeFeedback(context);
+  forceDeopt = { /* empty*/ };
+  assertEquals(expected, context(obj, value));
+  assertEquals(value, setterValue);
+  assertEquals(4, accessorCallCount);
 }
 
-function TestSetterInAllContexts(obj) {
-  function value_context(value) {
-    return obj.setterProperty = value;
-  }
-  TestInlinedSetter(value_context, 111, 111);
-
-  function test_context(value) {
-    if (obj.setterProperty = value) {
-      return 333;
-    } else {
-      return 444;
-    }
+function value_context_for_setter(obj, value) {
+  return obj.setterProperty = value;
+}
+
+function test_context_for_setter(obj, value) {
+  if (obj.setterProperty = value) {
+    return 333;
+  } else {
+    return 444;
   }
-  TestInlinedSetter(test_context, true, 333);
-  TestInlinedSetter(test_context, false, 444);
+}
+
+function effect_context_for_setter(obj, value) {
+  obj.setterProperty = value;
+  return 666;
+}
 
-  function effect_context(value) {
-    obj.setterProperty = value;
-    return 666;
+function TrySetter(context, setter, obj, expectException, value, expected) {
+  try {
+    TestInlinedSetter(context, obj, value, expected);
+    assertFalse(expectException);
+  } catch (exception) {
+    assertTrue(expectException);
+    assertEquals(7, exception.stack.split('\n').length);
   }
-  TestInlinedSetter(effect_context, 555, 666);
+  %DeoptimizeFunction(context);
+  %ClearFunctionTypeFeedback(context);
+  %ClearFunctionTypeFeedback(setter);
+}
+
+function TestSetterInAllContexts(setter, obj, expectException) {
+  TrySetter(value_context_for_setter, setter, obj, expectException, 111, 111);
+  TrySetter(test_context_for_setter, setter, obj, expectException, true, 333);
+  TrySetter(test_context_for_setter, setter, obj, expectException, false, 444);
+  TrySetter(effect_context_for_setter, setter, obj, expectException, 555, 666);
 }
 
 // -----------------------------------------------------------------------------
@@ -207,15 +256,15 @@ function TestSetterInAllContexts(obj) {
 function setter1(value) {
   assertSame(obj, this);
   accessorCallCount++;
-  forceDeopt + 1;
+  forceDeopt.deopt;
   setterValue = value;
 }
 
 function ConstrS1() { }
 obj = Object.defineProperty(new ConstrS1(), "setterProperty", { set: setter1 });
-TestSetterInAllContexts(obj);
+TestSetterInAllContexts(setter1, obj, false);
 obj = Object.create(obj);
-TestSetterInAllContexts(obj);
+TestSetterInAllContexts(setter1, obj, false);
 
 // -----------------------------------------------------------------------------
 // Test setter returning something different than the RHS in all contexts.
@@ -223,16 +272,16 @@ TestSetterInAllContexts(obj);
 function setter2(value) {
   assertSame(obj, this);
   accessorCallCount++;
-  forceDeopt + 1;
+  forceDeopt.deopt;
   setterValue = value;
   return 1000000;
 }
 
 function ConstrS2() { }
 obj = Object.defineProperty(new ConstrS2(), "setterProperty", { set: setter2 });
-TestSetterInAllContexts(obj);
+TestSetterInAllContexts(setter2, obj, false);
 obj = Object.create(obj);
-TestSetterInAllContexts(obj);
+TestSetterInAllContexts(setter2, obj, false);
 
 // -----------------------------------------------------------------------------
 // Test setter with too few arguments without a return in all contexts.
@@ -240,15 +289,15 @@ TestSetterInAllContexts(obj);
 function setter3() {
   assertSame(obj, this);
   accessorCallCount++;
-  forceDeopt + 1;
+  forceDeopt.deopt;
   setterValue = setterArgument;
 }
 
 function ConstrS3() { }
 obj = Object.defineProperty(new ConstrS3(), "setterProperty", { set: setter3 });
-TestSetterInAllContexts(obj);
+TestSetterInAllContexts(setter3, obj, false);
 obj = Object.create(obj);
-TestSetterInAllContexts(obj);
+TestSetterInAllContexts(setter3, obj, false);
 
 // -----------------------------------------------------------------------------
 // Test setter with too few arguments with a return in all contexts.
@@ -256,16 +305,16 @@ TestSetterInAllContexts(obj);
 function setter4() {
   assertSame(obj, this);
   accessorCallCount++;
-  forceDeopt + 1;
+  forceDeopt.deopt;
   setterValue = setterArgument;
   return 2000000;
 }
 
 function ConstrS4() { }
 obj = Object.defineProperty(new ConstrS4(), "setterProperty", { set: setter4 });
-TestSetterInAllContexts(obj);
+TestSetterInAllContexts(setter4, obj, false);
 obj = Object.create(obj);
-TestSetterInAllContexts(obj);
+TestSetterInAllContexts(setter4, obj, false);
 
 // -----------------------------------------------------------------------------
 // Test setter with too many arguments without a return in all contexts.
@@ -274,15 +323,15 @@ function setter5(value, foo) {
   assertSame(obj, this);
   assertEquals(undefined, foo);
   accessorCallCount++;
-  forceDeopt + 1;
+  forceDeopt.deopt;
   setterValue = value;
 }
 
 function ConstrS5() { }
 obj = Object.defineProperty(new ConstrS5(), "setterProperty", { set: setter5 });
-TestSetterInAllContexts(obj);
+TestSetterInAllContexts(setter5, obj, false);
 obj = Object.create(obj);
-TestSetterInAllContexts(obj);
+TestSetterInAllContexts(setter5, obj, false);
 
 // -----------------------------------------------------------------------------
 // Test setter with too many arguments with a return in all contexts.
@@ -291,13 +340,29 @@ function setter6(value, foo) {
   assertSame(obj, this);
   assertEquals(undefined, foo);
   accessorCallCount++;
-  forceDeopt + 1;
+  forceDeopt.deopt;
   setterValue = value;
   return 3000000;
 }
 
 function ConstrS6() { }
 obj = Object.defineProperty(new ConstrS6(), "setterProperty", { set: setter6 });
-TestSetterInAllContexts(obj);
+TestSetterInAllContexts(setter6, obj, false);
+obj = Object.create(obj);
+TestSetterInAllContexts(setter6, obj, false);
+
+// -----------------------------------------------------------------------------
+// Test setter which throws from optimized code.
+
+function setter7(value) {
+  accessorCallCount++;
+  forceDeopt.deopt;
+  if (accessorCallCount == 4) { 123 in null; }
+  setterValue = value;
+}
+
+function ConstrS7() { }
+obj = Object.defineProperty(new ConstrS7(), "setterProperty", { set: setter7 });
+TestSetterInAllContexts(setter7, obj, true);
 obj = Object.create(obj);
-TestSetterInAllContexts(obj);
+TestSetterInAllContexts(setter7, obj, true);