Adding more missing deoptimization points in Turbofan.
authorjarin@chromium.org <jarin@chromium.org>
Mon, 29 Sep 2014 13:37:58 +0000 (13:37 +0000)
committerjarin@chromium.org <jarin@chromium.org>
Mon, 29 Sep 2014 13:37:58 +0000 (13:37 +0000)
BUG=
R=bmeurer@chromium.org

Review URL: https://codereview.chromium.org/595863002

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

25 files changed:
src/arm/full-codegen-arm.cc
src/arm64/full-codegen-arm64.cc
src/ast.h
src/compiler/ast-graph-builder.cc
src/compiler/ast-graph-builder.h
src/compiler/code-generator.cc
src/compiler/common-operator.cc
src/compiler/common-operator.h
src/compiler/instruction-selector-unittest.cc
src/compiler/instruction.h
src/compiler/js-generic-lowering.cc
src/compiler/js-inlining.cc
src/compiler/linkage.cc
src/compiler/operator-properties-inl.h
src/ia32/full-codegen-ia32.cc
src/mips/full-codegen-mips.cc
src/mips64/full-codegen-mips64.cc
src/x64/full-codegen-x64.cc
src/x87/full-codegen-x87.cc
test/cctest/compiler/test-codegen-deopt.cc
test/cctest/compiler/test-js-typed-lowering.cc
test/mjsunit/big-array-literal.js
test/mjsunit/debug-references.js
test/mjsunit/regress/regress-78270.js
test/mjsunit/strict-mode.js

index 42a605c..076dc16 100644 (file)
@@ -2892,6 +2892,8 @@ void FullCodeGenerator::VisitCall(Call* expr) {
       // r1 (receiver). Touch up the stack with the right values.
       __ str(r0, MemOperand(sp, (arg_count + 1) * kPointerSize));
       __ str(r1, MemOperand(sp, arg_count * kPointerSize));
+
+      PrepareForBailoutForId(expr->EvalOrLookupId(), NO_REGISTERS);
     }
 
     // Record source position for debugger.
@@ -2925,6 +2927,7 @@ void FullCodeGenerator::VisitCall(Call* expr) {
     __ Push(context_register(), r2);
     __ CallRuntime(Runtime::kLoadLookupSlot, 2);
     __ Push(r0, r1);  // Function, receiver.
+    PrepareForBailoutForId(expr->EvalOrLookupId(), NO_REGISTERS);
 
     // If fast case code has been generated, emit code to push the
     // function and receiver and have the slow path jump around this
index 9d54482..3cb58cd 100644 (file)
@@ -2557,6 +2557,8 @@ void FullCodeGenerator::VisitCall(Call* expr) {
       // The runtime call returns a pair of values in x0 (function) and
       // x1 (receiver). Touch up the stack with the right values.
       __ PokePair(x1, x0, arg_count * kPointerSize);
+
+      PrepareForBailoutForId(expr->EvalOrLookupId(), NO_REGISTERS);
     }
 
     // Record source position for debugger.
@@ -2592,6 +2594,7 @@ void FullCodeGenerator::VisitCall(Call* expr) {
     __ Push(context_register(), x10);
     __ CallRuntime(Runtime::kLoadLookupSlot, 2);
     __ Push(x0, x1);  // Receiver, function.
+    PrepareForBailoutForId(expr->EvalOrLookupId(), NO_REGISTERS);
 
     // If fast case code has been generated, emit code to push the
     // function and receiver and have the slow path jump around this
index 63055ea..28b0fec 100644 (file)
--- a/src/ast.h
+++ b/src/ast.h
@@ -1796,6 +1796,7 @@ class Call FINAL : public Expression, public FeedbackSlotInterface {
   bool ComputeGlobalTarget(Handle<GlobalObject> global, LookupIterator* it);
 
   BailoutId ReturnId() const { return return_id_; }
+  BailoutId EvalOrLookupId() const { return eval_or_lookup_id_; }
 
   enum CallType {
     POSSIBLY_EVAL_CALL,
@@ -1821,7 +1822,8 @@ class Call FINAL : public Expression, public FeedbackSlotInterface {
         expression_(expression),
         arguments_(arguments),
         call_feedback_slot_(kInvalidFeedbackSlot),
-        return_id_(id_gen->GetNextId()) {
+        return_id_(id_gen->GetNextId()),
+        eval_or_lookup_id_(id_gen->GetNextId()) {
     if (expression->IsProperty()) {
       expression->AsProperty()->mark_for_call();
     }
@@ -1837,6 +1839,9 @@ class Call FINAL : public Expression, public FeedbackSlotInterface {
   int call_feedback_slot_;
 
   const BailoutId return_id_;
+  // TODO(jarin) Only allocate the bailout id for the POSSIBLY_EVAL_CALL and
+  // LOOKUP_SLOT_CALL types.
+  const BailoutId eval_or_lookup_id_;
 };
 
 
index 0364078..01cde01 100644 (file)
@@ -861,6 +861,7 @@ void AstGraphBuilder::VisitRegExpLiteral(RegExpLiteral* expr) {
   const Operator* op =
       javascript()->Runtime(Runtime::kMaterializeRegExpLiteral, 4);
   Node* literal = NewNode(op, literals_array, literal_index, pattern, flags);
+  PrepareFrameState(literal, expr->id(), ast_context()->GetStateCombine());
   ast_context()->ProduceValue(literal);
 }
 
@@ -1095,14 +1096,16 @@ void AstGraphBuilder::VisitAssignment(Assignment* expr) {
         Unique<Name> name =
             MakeUnique(property->key()->AsLiteral()->AsPropertyName());
         old_value = NewNode(javascript()->LoadNamed(name), object);
-        PrepareFrameState(old_value, property->LoadId(), kPushOutput);
+        PrepareFrameState(old_value, property->LoadId(),
+                          OutputFrameStateCombine::Push());
         break;
       }
       case KEYED_PROPERTY: {
         Node* key = environment()->Top();
         Node* object = environment()->Peek(1);
         old_value = NewNode(javascript()->LoadProperty(), object, key);
-        PrepareFrameState(old_value, property->LoadId(), kPushOutput);
+        PrepareFrameState(old_value, property->LoadId(),
+                          OutputFrameStateCombine::Push());
         break;
       }
     }
@@ -1111,7 +1114,8 @@ void AstGraphBuilder::VisitAssignment(Assignment* expr) {
     Node* right = environment()->Pop();
     Node* left = environment()->Pop();
     Node* value = BuildBinaryOp(left, right, expr->binary_op());
-    PrepareFrameState(value, expr->binary_operation()->id(), kPushOutput);
+    PrepareFrameState(value, expr->binary_operation()->id(),
+                      OutputFrameStateCombine::Push());
     environment()->Push(value);
   } else {
     VisitForValue(expr->value());
@@ -1164,6 +1168,7 @@ void AstGraphBuilder::VisitThrow(Throw* expr) {
   Node* exception = environment()->Pop();
   const Operator* op = javascript()->Runtime(Runtime::kThrow, 1);
   Node* value = NewNode(op, exception);
+  PrepareFrameState(value, expr->id(), ast_context()->GetStateCombine());
   ast_context()->ProduceValue(value);
 }
 
@@ -1212,6 +1217,9 @@ void AstGraphBuilder::VisitCall(Call* expr) {
       Node* pair = NewNode(op, current_context(), name);
       callee_value = NewNode(common()->Projection(0), pair);
       receiver_value = NewNode(common()->Projection(1), pair);
+
+      PrepareFrameState(pair, expr->EvalOrLookupId(),
+                        OutputFrameStateCombine::Push());
       break;
     }
     case Call::PROPERTY_CALL: {
@@ -1227,7 +1235,8 @@ void AstGraphBuilder::VisitCall(Call* expr) {
         Node* key = environment()->Pop();
         callee_value = NewNode(javascript()->LoadProperty(), object, key);
       }
-      PrepareFrameState(callee_value, property->LoadId(), kPushOutput);
+      PrepareFrameState(callee_value, property->LoadId(),
+                        OutputFrameStateCombine::Push());
       receiver_value = environment()->Pop();
       // Note that a PROPERTY_CALL requires the receiver to be wrapped into an
       // object for sloppy callees. This could also be modeled explicitly here,
@@ -1271,6 +1280,8 @@ void AstGraphBuilder::VisitCall(Call* expr) {
     const Operator* op =
         javascript()->Runtime(Runtime::kResolvePossiblyDirectEval, 5);
     Node* pair = NewNode(op, callee, source, receiver, strict, position);
+    PrepareFrameState(pair, expr->EvalOrLookupId(),
+                      OutputFrameStateCombine::PokeAt(arg_count + 1));
     Node* new_callee = NewNode(common()->Projection(0), pair);
     Node* new_receiver = NewNode(common()->Projection(1), pair);
 
@@ -1313,7 +1324,8 @@ void AstGraphBuilder::VisitCallJSRuntime(CallRuntime* expr) {
   Node* callee_value = NewNode(javascript()->LoadNamed(unique), receiver_value);
   // TODO(jarin): Find/create a bailout id to deoptimize to (crankshaft
   // refuses to optimize functions with jsruntime calls).
-  PrepareFrameState(callee_value, BailoutId::None(), kPushOutput);
+  PrepareFrameState(callee_value, BailoutId::None(),
+                    OutputFrameStateCombine::Push());
   environment()->Push(callee_value);
   environment()->Push(receiver_value);
 
@@ -1395,7 +1407,8 @@ void AstGraphBuilder::VisitCountOperation(CountOperation* expr) {
       Unique<Name> name =
           MakeUnique(property->key()->AsLiteral()->AsPropertyName());
       old_value = NewNode(javascript()->LoadNamed(name), object);
-      PrepareFrameState(old_value, property->LoadId(), kPushOutput);
+      PrepareFrameState(old_value, property->LoadId(),
+                        OutputFrameStateCombine::Push());
       stack_depth = 1;
       break;
     }
@@ -1405,7 +1418,8 @@ void AstGraphBuilder::VisitCountOperation(CountOperation* expr) {
       Node* key = environment()->Top();
       Node* object = environment()->Peek(1);
       old_value = NewNode(javascript()->LoadProperty(), object, key);
-      PrepareFrameState(old_value, property->LoadId(), kPushOutput);
+      PrepareFrameState(old_value, property->LoadId(),
+                        OutputFrameStateCombine::Push());
       stack_depth = 2;
       break;
     }
@@ -1583,7 +1597,8 @@ void AstGraphBuilder::VisitDelete(UnaryOperation* expr) {
     // deleting "this" is allowed in all language modes.
     Variable* variable = expr->expression()->AsVariableProxy()->var();
     DCHECK(strict_mode() == SLOPPY || variable->is_this());
-    value = BuildVariableDelete(variable);
+    value = BuildVariableDelete(variable, expr->id(),
+                                ast_context()->GetStateCombine());
   } else if (expr->expression()->IsProperty()) {
     Property* property = expr->expression()->AsProperty();
     VisitForValue(property->obj());
@@ -1591,6 +1606,7 @@ void AstGraphBuilder::VisitDelete(UnaryOperation* expr) {
     Node* key = environment()->Pop();
     Node* object = environment()->Pop();
     value = NewNode(javascript()->DeleteProperty(strict_mode()), object, key);
+    PrepareFrameState(value, expr->id(), ast_context()->GetStateCombine());
   } else {
     VisitForEffect(expr->expression());
     value = jsgraph()->TrueConstant();
@@ -1736,13 +1752,14 @@ Node* AstGraphBuilder::BuildHoleCheckSilent(Node* value, Node* for_hole,
 
 
 Node* AstGraphBuilder::BuildHoleCheckThrow(Node* value, Variable* variable,
-                                           Node* not_hole) {
+                                           Node* not_hole,
+                                           BailoutId bailout_id) {
   IfBuilder hole_check(this);
   Node* the_hole = jsgraph()->TheHoleConstant();
   Node* check = NewNode(javascript()->StrictEqual(), value, the_hole);
   hole_check.If(check);
   hole_check.Then();
-  environment()->Push(BuildThrowReferenceError(variable));
+  environment()->Push(BuildThrowReferenceError(variable, bailout_id));
   hole_check.Else();
   environment()->Push(not_hole);
   hole_check.End();
@@ -1762,7 +1779,7 @@ Node* AstGraphBuilder::BuildVariableLoad(Variable* variable,
       Unique<Name> name = MakeUnique(variable->name());
       const Operator* op = javascript()->LoadNamed(name, contextual_mode);
       Node* node = NewNode(op, global);
-      PrepareFrameState(node, bailout_id, kPushOutput);
+      PrepareFrameState(node, bailout_id, OutputFrameStateCombine::Push());
       return node;
     }
     case Variable::PARAMETER:
@@ -1780,9 +1797,9 @@ Node* AstGraphBuilder::BuildVariableLoad(Variable* variable,
       } else if (mode == LET || mode == CONST) {
         // Perform check for uninitialized let/const variables.
         if (value->op() == the_hole->op()) {
-          value = BuildThrowReferenceError(variable);
+          value = BuildThrowReferenceError(variable, bailout_id);
         } else if (value->opcode() == IrOpcode::kPhi) {
-          value = BuildHoleCheckThrow(value, variable, value);
+          value = BuildHoleCheckThrow(value, variable, value, bailout_id);
         }
       }
       return value;
@@ -1803,7 +1820,7 @@ Node* AstGraphBuilder::BuildVariableLoad(Variable* variable,
         value = BuildHoleCheckSilent(value, undefined, value);
       } else if (mode == LET || mode == CONST) {
         // Perform check for uninitialized let/const variables.
-        value = BuildHoleCheckThrow(value, variable, value);
+        value = BuildHoleCheckThrow(value, variable, value, bailout_id);
       }
       return value;
     }
@@ -1816,6 +1833,7 @@ Node* AstGraphBuilder::BuildVariableLoad(Variable* variable,
               : Runtime::kLoadLookupSlotNoReferenceError;
       const Operator* op = javascript()->Runtime(function_id, 2);
       Node* pair = NewNode(op, current_context(), name);
+      PrepareFrameState(pair, bailout_id, OutputFrameStateCombine::Push(1));
       return NewNode(common()->Projection(0), pair);
     }
   }
@@ -1824,14 +1842,18 @@ Node* AstGraphBuilder::BuildVariableLoad(Variable* variable,
 }
 
 
-Node* AstGraphBuilder::BuildVariableDelete(Variable* variable) {
+Node* AstGraphBuilder::BuildVariableDelete(
+    Variable* variable, BailoutId bailout_id,
+    OutputFrameStateCombine state_combine) {
   switch (variable->location()) {
     case Variable::UNALLOCATED: {
       // Global var, const, or let variable.
       Node* global = BuildLoadGlobalObject();
       Node* name = jsgraph()->Constant(variable->name());
       const Operator* op = javascript()->DeleteProperty(strict_mode());
-      return NewNode(op, global, name);
+      Node* result = NewNode(op, global, name);
+      PrepareFrameState(result, bailout_id, state_combine);
+      return result;
     }
     case Variable::PARAMETER:
     case Variable::LOCAL:
@@ -1843,7 +1865,9 @@ Node* AstGraphBuilder::BuildVariableDelete(Variable* variable) {
       // Dynamic lookup of context variable (anywhere in the chain).
       Node* name = jsgraph()->Constant(variable->name());
       const Operator* op = javascript()->Runtime(Runtime::kDeleteLookupSlot, 2);
-      return NewNode(op, current_context(), name);
+      Node* result = NewNode(op, current_context(), name);
+      PrepareFrameState(result, bailout_id, state_combine);
+      return result;
     }
   }
   UNREACHABLE();
@@ -1885,9 +1909,9 @@ Node* AstGraphBuilder::BuildVariableAssignment(Variable* variable, Node* value,
         // temporal dead zone of a let declared variable.
         Node* current = environment()->Lookup(variable);
         if (current->op() == the_hole->op()) {
-          value = BuildThrowReferenceError(variable);
+          value = BuildThrowReferenceError(variable, bailout_id);
         } else if (value->opcode() == IrOpcode::kPhi) {
-          value = BuildHoleCheckThrow(current, variable, value);
+          value = BuildHoleCheckThrow(current, variable, value, bailout_id);
         }
       } else if (mode == CONST && op != Token::INIT_CONST) {
         // All assignments to const variables are early errors.
@@ -1912,7 +1936,7 @@ Node* AstGraphBuilder::BuildVariableAssignment(Variable* variable, Node* value,
         const Operator* op =
             javascript()->LoadContext(depth, variable->index(), false);
         Node* current = NewNode(op, current_context());
-        value = BuildHoleCheckThrow(current, variable, value);
+        value = BuildHoleCheckThrow(current, variable, value, bailout_id);
       } else if (mode == CONST && op != Token::INIT_CONST) {
         // All assignments to const variables are early errors.
         UNREACHABLE();
@@ -1927,7 +1951,9 @@ Node* AstGraphBuilder::BuildVariableAssignment(Variable* variable, Node* value,
       // TODO(mstarzinger): Use Runtime::kInitializeLegacyConstLookupSlot for
       // initializations of const declarations.
       const Operator* op = javascript()->Runtime(Runtime::kStoreLookupSlot, 4);
-      return NewNode(op, value, current_context(), name, strict);
+      Node* store = NewNode(op, value, current_context(), name, strict);
+      PrepareFrameState(store, bailout_id);
+      return store;
     }
   }
   UNREACHABLE();
@@ -1965,11 +1991,14 @@ Node* AstGraphBuilder::BuildToBoolean(Node* value) {
 }
 
 
-Node* AstGraphBuilder::BuildThrowReferenceError(Variable* variable) {
+Node* AstGraphBuilder::BuildThrowReferenceError(Variable* variable,
+                                                BailoutId bailout_id) {
   // TODO(mstarzinger): Should be unified with the VisitThrow implementation.
   Node* variable_name = jsgraph()->Constant(variable->name());
   const Operator* op = javascript()->Runtime(Runtime::kThrowReferenceError, 1);
-  return NewNode(op, variable_name);
+  Node* call = NewNode(op, variable_name);
+  PrepareFrameState(call, bailout_id);
+  return call;
 }
 
 
index 6a7e3db..b622355 100644 (file)
@@ -80,7 +80,8 @@ class AstGraphBuilder : public StructuredGraphBuilder, public AstVisitor {
   // Builders for variable load and assignment.
   Node* BuildVariableAssignment(Variable* var, Node* value, Token::Value op,
                                 BailoutId bailout_id);
-  Node* BuildVariableDelete(Variable* var);
+  Node* BuildVariableDelete(Variable* var, BailoutId bailout_id,
+                            OutputFrameStateCombine state_combine);
   Node* BuildVariableLoad(Variable* var, BailoutId bailout_id,
                           ContextualMode mode = CONTEXTUAL);
 
@@ -94,11 +95,12 @@ class AstGraphBuilder : public StructuredGraphBuilder, public AstVisitor {
   Node* BuildToBoolean(Node* value);
 
   // Builders for error reporting at runtime.
-  Node* BuildThrowReferenceError(Variable* var);
+  Node* BuildThrowReferenceError(Variable* var, BailoutId bailout_id);
 
   // Builders for dynamic hole-checks at runtime.
   Node* BuildHoleCheckSilent(Node* value, Node* for_hole, Node* not_hole);
-  Node* BuildHoleCheckThrow(Node* value, Variable* var, Node* not_hole);
+  Node* BuildHoleCheckThrow(Node* value, Variable* var, Node* not_hole,
+                            BailoutId bailout_id);
 
   // Builders for binary operations.
   Node* BuildBinaryOp(Node* left, Node* right, Token::Value op);
@@ -173,8 +175,9 @@ class AstGraphBuilder : public StructuredGraphBuilder, public AstVisitor {
   void VisitForInAssignment(Expression* expr, Node* value);
 
   // Builds deoptimization for a given node.
-  void PrepareFrameState(Node* node, BailoutId ast_id,
-                         OutputFrameStateCombine combine = kIgnoreOutput);
+  void PrepareFrameState(
+      Node* node, BailoutId ast_id,
+      OutputFrameStateCombine combine = OutputFrameStateCombine::Ignore());
 
   OutputFrameStateCombine StateCombineFromAstContext();
 
@@ -288,7 +291,8 @@ class AstGraphBuilder::AstContext BASE_EMBEDDED {
   // Determines how to combine the frame state with the value
   // that is about to be plugged into this AstContext.
   OutputFrameStateCombine GetStateCombine() {
-    return IsEffect() ? kIgnoreOutput : kPushOutput;
+    return IsEffect() ? OutputFrameStateCombine::Ignore()
+                      : OutputFrameStateCombine::Push();
   }
 
   // Plug a node into this expression context.  Call this function in tail
index f22c479..ea1466d 100644 (file)
@@ -260,15 +260,15 @@ void CodeGenerator::AddSafepointAndDeopt(Instruction* instr) {
     // because it is only used to get locals and arguments (by the debugger and
     // f.arguments), and those are the same in the pre-call and post-call
     // states.
-    if (descriptor->state_combine() != kIgnoreOutput) {
-      deopt_state_id =
-          BuildTranslation(instr, -1, frame_state_offset, kIgnoreOutput);
+    if (!descriptor->state_combine().IsOutputIgnored()) {
+      deopt_state_id = BuildTranslation(instr, -1, frame_state_offset,
+                                        OutputFrameStateCombine::Ignore());
     }
 #if DEBUG
     // Make sure all the values live in stack slots or they are immediates.
     // (The values should not live in register because registers are clobbered
     // by calls.)
-    for (size_t i = 0; i < descriptor->size(); i++) {
+    for (size_t i = 0; i < descriptor->GetSize(); i++) {
       InstructionOperand* op = instr->InputAt(frame_state_offset + 1 + i);
       CHECK(op->IsStackSlot() || op->IsImmediate());
     }
@@ -296,6 +296,33 @@ FrameStateDescriptor* CodeGenerator::GetFrameStateDescriptor(
   return code()->GetFrameStateDescriptor(state_id);
 }
 
+static InstructionOperand* OperandForFrameState(
+    FrameStateDescriptor* descriptor, Instruction* instr,
+    size_t frame_state_offset, size_t index, OutputFrameStateCombine combine) {
+  DCHECK(index < descriptor->GetSize(combine));
+  switch (combine.kind()) {
+    case OutputFrameStateCombine::kPushOutput: {
+      DCHECK(combine.GetPushCount() <= instr->OutputCount());
+      size_t size_without_output =
+          descriptor->GetSize(OutputFrameStateCombine::Ignore());
+      // If the index is past the existing stack items, return the output.
+      if (index >= size_without_output) {
+        return instr->OutputAt(index - size_without_output);
+      }
+      break;
+    }
+    case OutputFrameStateCombine::kPokeAt:
+      size_t index_from_top =
+          descriptor->GetSize(combine) - 1 - combine.GetOffsetToPokeAt();
+      if (index >= index_from_top &&
+          index < index_from_top + instr->OutputCount()) {
+        return instr->OutputAt(index - index_from_top);
+      }
+      break;
+  }
+  return instr->InputAt(frame_state_offset + index);
+}
+
 
 void CodeGenerator::BuildTranslationForFrameStateDescriptor(
     FrameStateDescriptor* descriptor, Instruction* instr,
@@ -305,7 +332,7 @@ void CodeGenerator::BuildTranslationForFrameStateDescriptor(
   if (descriptor->outer_state() != NULL) {
     BuildTranslationForFrameStateDescriptor(descriptor->outer_state(), instr,
                                             translation, frame_state_offset,
-                                            kIgnoreOutput);
+                                            OutputFrameStateCombine::Ignore());
   }
 
   int id = Translation::kSelfLiteralId;
@@ -318,7 +345,8 @@ void CodeGenerator::BuildTranslationForFrameStateDescriptor(
     case JS_FRAME:
       translation->BeginJSFrame(
           descriptor->bailout_id(), id,
-          static_cast<unsigned int>(descriptor->GetHeight(state_combine)));
+          static_cast<unsigned int>(descriptor->GetSize(state_combine) -
+                                    descriptor->parameters_count()));
       break;
     case ARGUMENTS_ADAPTOR:
       translation->BeginArgumentsAdaptorFrame(
@@ -327,19 +355,10 @@ void CodeGenerator::BuildTranslationForFrameStateDescriptor(
   }
 
   frame_state_offset += descriptor->outer_state()->GetTotalSize();
-  for (size_t i = 0; i < descriptor->size(); i++) {
-    AddTranslationForOperand(
-        translation, instr,
-        instr->InputAt(static_cast<int>(frame_state_offset + i)));
-  }
-
-  switch (state_combine) {
-    case kPushOutput:
-      DCHECK(instr->OutputCount() == 1);
-      AddTranslationForOperand(translation, instr, instr->OutputAt(0));
-      break;
-    case kIgnoreOutput:
-      break;
+  for (size_t i = 0; i < descriptor->GetSize(state_combine); i++) {
+    InstructionOperand* op = OperandForFrameState(
+        descriptor, instr, frame_state_offset, i, state_combine);
+    AddTranslationForOperand(translation, instr, op);
   }
 }
 
index 19792bd..323ad9a 100644 (file)
@@ -247,6 +247,50 @@ const Operator* CommonOperatorBuilder::Projection(size_t index) {
                                         1, 1, "Projection", index);
 }
 
+
+OutputFrameStateCombine::OutputFrameStateCombine(CombineKind kind,
+                                                 size_t parameter)
+    : kind_(kind), parameter_(parameter) {}
+
+// static
+OutputFrameStateCombine OutputFrameStateCombine::Ignore() {
+  return OutputFrameStateCombine(kPushOutput, 0);
+}
+
+
+// static
+OutputFrameStateCombine OutputFrameStateCombine::Push(size_t count) {
+  return OutputFrameStateCombine(kPushOutput, count);
+}
+
+
+// static
+OutputFrameStateCombine OutputFrameStateCombine::PokeAt(size_t index) {
+  return OutputFrameStateCombine(kPokeAt, index);
+}
+
+
+OutputFrameStateCombine::CombineKind OutputFrameStateCombine::kind() {
+  return kind_;
+}
+
+
+size_t OutputFrameStateCombine::GetPushCount() {
+  DCHECK(kind() == kPushOutput);
+  return parameter_;
+}
+
+
+size_t OutputFrameStateCombine::GetOffsetToPokeAt() {
+  DCHECK(kind() == kPokeAt);
+  return parameter_;
+}
+
+
+bool OutputFrameStateCombine::IsOutputIgnored() {
+  return kind() == kPushOutput && GetPushCount() == 0;
+}
+
 }  // namespace compiler
 }  // namespace internal
 }  // namespace v8
index a3659ad..0e45b32 100644 (file)
@@ -26,9 +26,29 @@ class Operator;
 
 // Flag that describes how to combine the current environment with
 // the output of a node to obtain a framestate for lazy bailout.
-enum OutputFrameStateCombine {
-  kPushOutput,   // Push the output on the expression stack.
-  kIgnoreOutput  // Use the frame state as-is.
+class OutputFrameStateCombine {
+ public:
+  enum CombineKind {
+    kPushOutput,  // Push the output on the expression stack.
+    kPokeAt       // Poke at the given environment location,
+                  // counting from the top of the stack.
+  };
+
+  static OutputFrameStateCombine Ignore();
+  static OutputFrameStateCombine Push(size_t count = 1);
+  static OutputFrameStateCombine PokeAt(size_t index);
+
+  CombineKind kind();
+  size_t GetPushCount();
+  size_t GetOffsetToPokeAt();
+
+  bool IsOutputIgnored();
+
+ private:
+  OutputFrameStateCombine(CombineKind kind, size_t parameter);
+
+  CombineKind kind_;
+  size_t parameter_;
 };
 
 
index 75159b0..08a920c 100644 (file)
@@ -320,8 +320,9 @@ TARGET_TEST_F(InstructionSelectorTest, CallJSFunctionWithDeopt) {
   Node* context_dummy = m.Int32Constant(0);
 
   Node* state_node = m.NewNode(
-      m.common()->FrameState(JS_FRAME, bailout_id, kPushOutput), parameters,
-      locals, stack, context_dummy, m.UndefinedConstant());
+      m.common()->FrameState(JS_FRAME, bailout_id,
+                             OutputFrameStateCombine::Push()),
+      parameters, locals, stack, context_dummy, m.UndefinedConstant());
   Node* call = m.CallJS0(function_node, receiver, context, state_node);
   m.Return(call);
 
@@ -360,7 +361,8 @@ TARGET_TEST_F(InstructionSelectorTest, CallFunctionStubWithDeopt) {
 
   Node* context_sentinel = m.Int32Constant(0);
   Node* frame_state_before = m.NewNode(
-      m.common()->FrameState(JS_FRAME, bailout_id_before, kPushOutput),
+      m.common()->FrameState(JS_FRAME, bailout_id_before,
+                             OutputFrameStateCombine::Push()),
       parameters, locals, stack, context_sentinel, m.UndefinedConstant());
 
   // Build the call.
@@ -398,7 +400,8 @@ TARGET_TEST_F(InstructionSelectorTest, CallFunctionStubWithDeopt) {
   FrameStateDescriptor* desc_before =
       s.GetFrameStateDescriptor(deopt_id_before);
   EXPECT_EQ(bailout_id_before, desc_before->bailout_id());
-  EXPECT_EQ(kPushOutput, desc_before->state_combine());
+  EXPECT_EQ(OutputFrameStateCombine::kPushOutput,
+            desc_before->state_combine().kind());
   EXPECT_EQ(1u, desc_before->parameters_count());
   EXPECT_EQ(1u, desc_before->locals_count());
   EXPECT_EQ(1u, desc_before->stack_count());
@@ -435,18 +438,20 @@ TARGET_TEST_F(InstructionSelectorTest,
   Node* parameters = m.NewNode(m.common()->StateValues(1), m.Int32Constant(63));
   Node* locals = m.NewNode(m.common()->StateValues(1), m.Int32Constant(64));
   Node* stack = m.NewNode(m.common()->StateValues(1), m.Int32Constant(65));
-  Node* frame_state_parent = m.NewNode(
-      m.common()->FrameState(JS_FRAME, bailout_id_parent, kIgnoreOutput),
-      parameters, locals, stack, context, m.UndefinedConstant());
+  Node* frame_state_parent =
+      m.NewNode(m.common()->FrameState(JS_FRAME, bailout_id_parent,
+                                       OutputFrameStateCombine::Ignore()),
+                parameters, locals, stack, context, m.UndefinedConstant());
 
   Node* context2 = m.Int32Constant(46);
   Node* parameters2 =
       m.NewNode(m.common()->StateValues(1), m.Int32Constant(43));
   Node* locals2 = m.NewNode(m.common()->StateValues(1), m.Int32Constant(44));
   Node* stack2 = m.NewNode(m.common()->StateValues(1), m.Int32Constant(45));
-  Node* frame_state_before = m.NewNode(
-      m.common()->FrameState(JS_FRAME, bailout_id_before, kPushOutput),
-      parameters2, locals2, stack2, context2, frame_state_parent);
+  Node* frame_state_before =
+      m.NewNode(m.common()->FrameState(JS_FRAME, bailout_id_before,
+                                       OutputFrameStateCombine::Push()),
+                parameters2, locals2, stack2, context2, frame_state_parent);
 
   // Build the call.
   Node* call = m.CallFunctionStub0(function_node, receiver, context2,
index f8ad55e..4b18120 100644 (file)
@@ -736,32 +736,29 @@ class FrameStateDescriptor : public ZoneObject {
   FrameStateDescriptor* outer_state() const { return outer_state_; }
   MaybeHandle<JSFunction> jsfunction() const { return jsfunction_; }
 
-  size_t size() const {
-    return parameters_count_ + locals_count_ + stack_count_ +
-           (HasContext() ? 1 : 0);
+  size_t GetSize(OutputFrameStateCombine combine =
+                     OutputFrameStateCombine::Ignore()) const {
+    size_t size = parameters_count_ + locals_count_ + stack_count_ +
+                  (HasContext() ? 1 : 0);
+    switch (combine.kind()) {
+      case OutputFrameStateCombine::kPushOutput:
+        size += combine.GetPushCount();
+        break;
+      case OutputFrameStateCombine::kPokeAt:
+        break;
+    }
+    return size;
   }
 
   size_t GetTotalSize() const {
     size_t total_size = 0;
     for (const FrameStateDescriptor* iter = this; iter != NULL;
          iter = iter->outer_state_) {
-      total_size += iter->size();
+      total_size += iter->GetSize();
     }
     return total_size;
   }
 
-  size_t GetHeight(OutputFrameStateCombine override) const {
-    size_t height = size() - parameters_count();
-    switch (override) {
-      case kPushOutput:
-        ++height;
-        break;
-      case kIgnoreOutput:
-        break;
-    }
-    return height;
-  }
-
   size_t GetFrameCount() const {
     size_t count = 0;
     for (const FrameStateDescriptor* iter = this; iter != NULL;
index 300604e..146185e 100644 (file)
@@ -198,8 +198,8 @@ void JSGenericLowering::ReplaceWithBuiltinCall(Node* node,
                                                int nargs) {
   Callable callable =
       CodeFactory::CallFunction(isolate(), nargs - 1, NO_CALL_FUNCTION_FLAGS);
-  CallDescriptor* desc =
-      linkage()->GetStubCallDescriptor(callable.descriptor(), nargs);
+  CallDescriptor* desc = linkage()->GetStubCallDescriptor(
+      callable.descriptor(), nargs, FlagsForNode(node));
   // TODO(mstarzinger): Accessing the builtins object this way prevents sharing
   // of code across native contexts. Fix this by loading from given context.
   Handle<JSFunction> function(
@@ -260,7 +260,7 @@ void JSGenericLowering::LowerJSToBoolean(Node* node) {
 
 void JSGenericLowering::LowerJSToNumber(Node* node) {
   Callable callable = CodeFactory::ToNumber(isolate());
-  ReplaceWithStubCall(node, callable, CallDescriptor::kNoFlags);
+  ReplaceWithStubCall(node, callable, FlagsForNode(node));
 }
 
 
@@ -321,7 +321,8 @@ void JSGenericLowering::LowerJSInstanceOf(Node* node) {
       InstanceofStub::kArgsInRegisters);
   InstanceofStub stub(isolate(), flags);
   CallInterfaceDescriptor d = stub.GetCallInterfaceDescriptor();
-  CallDescriptor* desc = linkage()->GetStubCallDescriptor(d, 0);
+  CallDescriptor* desc =
+      linkage()->GetStubCallDescriptor(d, 0, FlagsForNode(node));
   Node* stub_code = CodeConstant(stub.GetCode());
   PatchInsertInput(node, 0, stub_code);
   PatchOperator(node, common()->Call(desc));
index af02145..00e4011 100644 (file)
@@ -348,9 +348,9 @@ void JSInliner::AddClosureToFrameState(Node* frame_state,
 Node* JSInliner::CreateArgumentsAdaptorFrameState(JSCallFunctionAccessor* call,
                                                   Handle<JSFunction> jsfunction,
                                                   Zone* temp_zone) {
-  const Operator* op =
-      jsgraph_->common()->FrameState(FrameStateType::ARGUMENTS_ADAPTOR,
-                                     BailoutId(-1), kIgnoreOutput, jsfunction);
+  const Operator* op = jsgraph_->common()->FrameState(
+      FrameStateType::ARGUMENTS_ADAPTOR, BailoutId(-1),
+      OutputFrameStateCombine::Ignore(), jsfunction);
   const Operator* op0 = jsgraph_->common()->StateValues(0);
   Node* node0 = jsgraph_->graph()->NewNode(op0);
   NodeVector params(temp_zone);
index 465a667..89786dc 100644 (file)
@@ -116,16 +116,67 @@ bool Linkage::NeedsFrameState(Runtime::FunctionId function) {
   // TODO(jarin) At the moment, we only add frame state for
   // few chosen runtime functions.
   switch (function) {
+    case Runtime::kApply:
+    case Runtime::kArrayBufferNeuter:
+    case Runtime::kArrayConcat:
+    case Runtime::kBasicJSONStringify:
+    case Runtime::kCheckExecutionState:
+    case Runtime::kCollectStackTrace:
+    case Runtime::kCompileLazy:
+    case Runtime::kCompileOptimized:
+    case Runtime::kCompileString:
     case Runtime::kDebugBreak:
+    case Runtime::kDataViewSetInt8:
+    case Runtime::kDataViewSetUint8:
+    case Runtime::kDataViewSetInt16:
+    case Runtime::kDataViewSetUint16:
+    case Runtime::kDataViewSetInt32:
+    case Runtime::kDataViewSetUint32:
+    case Runtime::kDataViewSetFloat32:
+    case Runtime::kDataViewSetFloat64:
+    case Runtime::kDataViewGetInt8:
+    case Runtime::kDataViewGetUint8:
+    case Runtime::kDataViewGetInt16:
+    case Runtime::kDataViewGetUint16:
+    case Runtime::kDataViewGetInt32:
+    case Runtime::kDataViewGetUint32:
+    case Runtime::kDataViewGetFloat32:
+    case Runtime::kDataViewGetFloat64:
+    case Runtime::kDebugEvaluate:
     case Runtime::kDebugGetLoadedScripts:
+    case Runtime::kDebugGetPropertyDetails:
+    case Runtime::kDebugPromiseRejectEvent:
+    case Runtime::kDebugPromiseEvent:
+    case Runtime::kDeleteProperty:
     case Runtime::kDeoptimizeFunction:
+    case Runtime::kFunctionBindArguments:
+    case Runtime::kGetFrameCount:
+    case Runtime::kGetOwnProperty:
     case Runtime::kInlineCallFunction:
+    case Runtime::kInlineDateField:
+    case Runtime::kInlineRegExpExec:
+    case Runtime::kLiveEditGatherCompileInfo:
+    case Runtime::kLoadLookupSlot:
+    case Runtime::kLoadLookupSlotNoReferenceError:
+    case Runtime::kMaterializeRegExpLiteral:
+    case Runtime::kNewObjectFromBound:
+    case Runtime::kObjectFreeze:
+    case Runtime::kParseJson:
     case Runtime::kPrepareStep:
+    case Runtime::kPreventExtensions:
+    case Runtime::kRegExpCompile:
+    case Runtime::kRegExpExecMultiple:
+    case Runtime::kResolvePossiblyDirectEval:
+    // case Runtime::kSetPrototype:
     case Runtime::kSetScriptBreakPoint:
     case Runtime::kStackGuard:
-    case Runtime::kCheckExecutionState:
-    case Runtime::kDebugEvaluate:
-    case Runtime::kCollectStackTrace:
+    case Runtime::kStoreLookupSlot:
+    case Runtime::kStringBuilderConcat:
+    case Runtime::kStringReplaceGlobalRegExpWithString:
+    case Runtime::kThrowReferenceError:
+    case Runtime::kThrow:
+    case Runtime::kTypedArraySetFastCases:
+    case Runtime::kTypedArrayInitializeFromArrayLike:
       return true;
     default:
       return false;
index 9dae106..cb598aa 100644 (file)
@@ -55,28 +55,33 @@ inline bool OperatorProperties::HasFrameStateInput(const Operator* op) {
 
     // Compare operations
     case IrOpcode::kJSEqual:
-    case IrOpcode::kJSNotEqual:
-    case IrOpcode::kJSLessThan:
     case IrOpcode::kJSGreaterThan:
-    case IrOpcode::kJSLessThanOrEqual:
     case IrOpcode::kJSGreaterThanOrEqual:
+    case IrOpcode::kJSHasProperty:
+    case IrOpcode::kJSInstanceOf:
+    case IrOpcode::kJSLessThan:
+    case IrOpcode::kJSLessThanOrEqual:
+    case IrOpcode::kJSNotEqual:
 
     // Binary operations
+    case IrOpcode::kJSAdd:
+    case IrOpcode::kJSBitwiseAnd:
     case IrOpcode::kJSBitwiseOr:
     case IrOpcode::kJSBitwiseXor:
-    case IrOpcode::kJSBitwiseAnd:
+    case IrOpcode::kJSDivide:
+    case IrOpcode::kJSLoadNamed:
+    case IrOpcode::kJSLoadProperty:
+    case IrOpcode::kJSModulus:
+    case IrOpcode::kJSMultiply:
     case IrOpcode::kJSShiftLeft:
     case IrOpcode::kJSShiftRight:
     case IrOpcode::kJSShiftRightLogical:
-    case IrOpcode::kJSAdd:
-    case IrOpcode::kJSSubtract:
-    case IrOpcode::kJSMultiply:
-    case IrOpcode::kJSDivide:
-    case IrOpcode::kJSModulus:
-    case IrOpcode::kJSLoadProperty:
-    case IrOpcode::kJSStoreProperty:
-    case IrOpcode::kJSLoadNamed:
     case IrOpcode::kJSStoreNamed:
+    case IrOpcode::kJSStoreProperty:
+    case IrOpcode::kJSSubtract:
+
+    // Other
+    case IrOpcode::kJSDeleteProperty:
       return true;
 
     default:
index a382dad..c5351a4 100644 (file)
@@ -2798,6 +2798,8 @@ void FullCodeGenerator::VisitCall(Call* expr) {
       // edx (receiver). Touch up the stack with the right values.
       __ mov(Operand(esp, (arg_count + 0) * kPointerSize), edx);
       __ mov(Operand(esp, (arg_count + 1) * kPointerSize), eax);
+
+      PrepareForBailoutForId(expr->EvalOrLookupId(), NO_REGISTERS);
     }
     // Record source position for debugger.
     SetSourcePosition(expr->position());
@@ -2829,6 +2831,7 @@ void FullCodeGenerator::VisitCall(Call* expr) {
     __ CallRuntime(Runtime::kLoadLookupSlot, 2);
     __ push(eax);  // Function.
     __ push(edx);  // Receiver.
+    PrepareForBailoutForId(expr->EvalOrLookupId(), NO_REGISTERS);
 
     // If fast case code has been generated, emit code to push the function
     // and receiver and have the slow path jump around this code.
index 8b20639..f1a2ced 100644 (file)
@@ -2871,6 +2871,8 @@ void FullCodeGenerator::VisitCall(Call* expr) {
       // v1 (receiver). Touch up the stack with the right values.
       __ sw(v0, MemOperand(sp, (arg_count + 1) * kPointerSize));
       __ sw(v1, MemOperand(sp, arg_count * kPointerSize));
+
+      PrepareForBailoutForId(expr->EvalOrLookupId(), NO_REGISTERS);
     }
     // Record source position for debugger.
     SetSourcePosition(expr->position());
@@ -2902,6 +2904,7 @@ void FullCodeGenerator::VisitCall(Call* expr) {
     __ Push(context_register(), a2);
     __ CallRuntime(Runtime::kLoadLookupSlot, 2);
     __ Push(v0, v1);  // Function, receiver.
+    PrepareForBailoutForId(expr->EvalOrLookupId(), NO_REGISTERS);
 
     // If fast case code has been generated, emit code to push the
     // function and receiver and have the slow path jump around this
index 1d4e63e..a6b5249 100644 (file)
@@ -2870,6 +2870,8 @@ void FullCodeGenerator::VisitCall(Call* expr) {
       // v1 (receiver). Touch up the stack with the right values.
       __ sd(v0, MemOperand(sp, (arg_count + 1) * kPointerSize));
       __ sd(v1, MemOperand(sp, arg_count * kPointerSize));
+
+      PrepareForBailoutForId(expr->EvalOrLookupId(), NO_REGISTERS);
     }
     // Record source position for debugger.
     SetSourcePosition(expr->position());
@@ -2901,6 +2903,7 @@ void FullCodeGenerator::VisitCall(Call* expr) {
     __ Push(context_register(), a2);
     __ CallRuntime(Runtime::kLoadLookupSlot, 2);
     __ Push(v0, v1);  // Function, receiver.
+    PrepareForBailoutForId(expr->EvalOrLookupId(), NO_REGISTERS);
 
     // If fast case code has been generated, emit code to push the
     // function and receiver and have the slow path jump around this
index 02c2d9c..574cea0 100644 (file)
@@ -2795,6 +2795,8 @@ void FullCodeGenerator::VisitCall(Call* expr) {
       // rdx (receiver). Touch up the stack with the right values.
       __ movp(Operand(rsp, (arg_count + 0) * kPointerSize), rdx);
       __ movp(Operand(rsp, (arg_count + 1) * kPointerSize), rax);
+
+      PrepareForBailoutForId(expr->EvalOrLookupId(), NO_REGISTERS);
     }
     // Record source position for debugger.
     SetSourcePosition(expr->position());
@@ -2826,6 +2828,7 @@ void FullCodeGenerator::VisitCall(Call* expr) {
     __ CallRuntime(Runtime::kLoadLookupSlot, 2);
     __ Push(rax);  // Function.
     __ Push(rdx);  // Receiver.
+    PrepareForBailoutForId(expr->EvalOrLookupId(), NO_REGISTERS);
 
     // If fast case code has been generated, emit code to push the function
     // and receiver and have the slow path jump around this code.
index 0507604..a59ac17 100644 (file)
@@ -2785,6 +2785,8 @@ void FullCodeGenerator::VisitCall(Call* expr) {
       // edx (receiver). Touch up the stack with the right values.
       __ mov(Operand(esp, (arg_count + 0) * kPointerSize), edx);
       __ mov(Operand(esp, (arg_count + 1) * kPointerSize), eax);
+
+      PrepareForBailoutForId(expr->EvalOrLookupId(), NO_REGISTERS);
     }
     // Record source position for debugger.
     SetSourcePosition(expr->position());
@@ -2816,6 +2818,7 @@ void FullCodeGenerator::VisitCall(Call* expr) {
     __ CallRuntime(Runtime::kLoadLookupSlot, 2);
     __ push(eax);  // Function.
     __ push(edx);  // Receiver.
+    PrepareForBailoutForId(expr->EvalOrLookupId(), NO_REGISTERS);
 
     // If fast case code has been generated, emit code to push the function
     // and receiver and have the slow path jump around this code.
index 8217229..189fddb 100644 (file)
@@ -145,8 +145,9 @@ class TrivialDeoptCodegenTester : public DeoptCodegenTester {
     Node* stack = m.NewNode(common.StateValues(0));
 
     Node* state_node = m.NewNode(
-        common.FrameState(JS_FRAME, bailout_id, kIgnoreOutput), parameters,
-        locals, stack, caller_context_node, m.UndefinedConstant());
+        common.FrameState(JS_FRAME, bailout_id,
+                          OutputFrameStateCombine::Ignore()),
+        parameters, locals, stack, caller_context_node, m.UndefinedConstant());
 
     Handle<Context> context(deopt_function->context(), CcTest::i_isolate());
     Unique<Object> context_constant =
@@ -259,8 +260,9 @@ class TrivialRuntimeDeoptCodegenTester : public DeoptCodegenTester {
     Node* stack = m.NewNode(common.StateValues(0));
 
     Node* state_node = m.NewNode(
-        common.FrameState(JS_FRAME, bailout_id, kIgnoreOutput), parameters,
-        locals, stack, context_node, m.UndefinedConstant());
+        common.FrameState(JS_FRAME, bailout_id,
+                          OutputFrameStateCombine::Ignore()),
+        parameters, locals, stack, context_node, m.UndefinedConstant());
 
     m.CallRuntime1(Runtime::kDeoptimizeFunction, this_fun_node, context_node,
                    state_node);
index cf126c2..306ec48 100644 (file)
@@ -65,7 +65,8 @@ class JSTypedLoweringTester : public HandleAndZoneScope {
     Node* stack = graph.NewNode(common.StateValues(0));
 
     Node* state_node =
-        graph.NewNode(common.FrameState(JS_FRAME, BailoutId(0), kIgnoreOutput),
+        graph.NewNode(common.FrameState(JS_FRAME, BailoutId(0),
+                                        OutputFrameStateCombine::Ignore()),
                       parameters, locals, stack, context, UndefinedConstant());
 
     return state_node;
index 13f91f8..401807f 100644 (file)
@@ -27,6 +27,7 @@
 
 // On MacOS X 10.7.5, this test needs a stack size of at least 788 kBytes.
 // Flags: --stack-size=800
+// Flags: --turbo-deoptimization
 
 // Test that we can make large object literals that work.
 // Also test that we can attempt to make even larger object literals without
index 763e354..bb33976 100644 (file)
@@ -25,7 +25,7 @@
 // (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: --expose-debug-as debug
+// Flags: --expose-debug-as debug --turbo-deoptimization
 // Get the Debug object exposed from the debug context global object.
 Debug = debug.Debug
 
index b9ce286..02c4b14 100644 (file)
@@ -25,6 +25,8 @@
 // (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: --turbo-deoptimization
+
 for (var i = 0; i < 10000; i++) {
   try {
     var object = { };
index 5fb404a..0c27a1c 100644 (file)
@@ -25,6 +25,8 @@
 // (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: --turbo-deoptimization
+
 function CheckStrictMode(code, exception) {
   assertDoesNotThrow(code);
   assertThrows("'use strict';\n" + code, exception);