Fix SlotRef::SlotAddress for parameters indices.
authorvegorov@chromium.org <vegorov@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Fri, 1 Apr 2011 11:41:36 +0000 (11:41 +0000)
committervegorov@chromium.org <vegorov@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Fri, 1 Apr 2011 11:41:36 +0000 (11:41 +0000)
Fix %NewObjectFromBound to correctly handle optimized frames (including those with inlined functions).

Fix %_IsConstructCall handling in hydrogen: when called from inlined function return false constant directly instead of emiting HIsConstructCall.

Fix success case in TraceInline.

BUG=v8:1229
TEST=test/mjsunit/regress/regress-1229.js

Review URL: http://codereview.chromium.org/6740023

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

12 files changed:
src/accessors.cc
src/arm/frames-arm.h
src/deoptimizer.cc
src/deoptimizer.h
src/frames.cc
src/hydrogen.cc
src/hydrogen.h
src/ia32/frames-ia32.h
src/mips/frames-mips.h
src/runtime.cc
src/x64/frames-x64.h
test/mjsunit/regress/regress-1229.js [new file with mode: 0644]

index e33b4d794077954ac9ef701d9e42b64d5c5474d8..ee7636ec4b3ebbd7ef60b389026b488f2e1542f7 100644 (file)
@@ -568,172 +568,6 @@ const AccessorDescriptor Accessors::FunctionName = {
 // Accessors::FunctionArguments
 //
 
-static Address SlotAddress(JavaScriptFrame* frame, int slot_index) {
-  if (slot_index >= 0) {
-    const int offset = JavaScriptFrameConstants::kLocal0Offset;
-    return frame->fp() + offset - (slot_index * kPointerSize);
-  } else {
-    const int offset = JavaScriptFrameConstants::kReceiverOffset;
-    return frame->caller_sp() + offset + (slot_index * kPointerSize);
-  }
-}
-
-
-// We can't intermix stack decoding and allocations because
-// deoptimization infrastracture is not GC safe.
-// Thus we build a temporary structure in malloced space.
-class SlotRef BASE_EMBEDDED {
- public:
-  enum SlotRepresentation {
-    UNKNOWN,
-    TAGGED,
-    INT32,
-    DOUBLE,
-    LITERAL
-  };
-
-  SlotRef()
-      : addr_(NULL), representation_(UNKNOWN) { }
-
-  SlotRef(Address addr, SlotRepresentation representation)
-      : addr_(addr), representation_(representation) { }
-
-  explicit SlotRef(Object* literal)
-      : literal_(literal), representation_(LITERAL) { }
-
-  Handle<Object> GetValue() {
-    switch (representation_) {
-      case TAGGED:
-        return Handle<Object>(Memory::Object_at(addr_));
-
-      case INT32: {
-        int value = Memory::int32_at(addr_);
-        if (Smi::IsValid(value)) {
-          return Handle<Object>(Smi::FromInt(value));
-        } else {
-          return Isolate::Current()->factory()->NewNumberFromInt(value);
-        }
-      }
-
-      case DOUBLE: {
-        double value = Memory::double_at(addr_);
-        return Isolate::Current()->factory()->NewNumber(value);
-      }
-
-      case LITERAL:
-        return literal_;
-
-      default:
-        UNREACHABLE();
-        return Handle<Object>::null();
-    }
-  }
-
- private:
-  Address addr_;
-  Handle<Object> literal_;
-  SlotRepresentation representation_;
-};
-
-
-static SlotRef ComputeSlotForNextArgument(TranslationIterator* iterator,
-                                          DeoptimizationInputData* data,
-                                          JavaScriptFrame* frame) {
-  Translation::Opcode opcode =
-      static_cast<Translation::Opcode>(iterator->Next());
-
-  switch (opcode) {
-    case Translation::BEGIN:
-    case Translation::FRAME:
-      // Peeled off before getting here.
-      break;
-
-    case Translation::ARGUMENTS_OBJECT:
-      // This can be only emitted for local slots not for argument slots.
-      break;
-
-    case Translation::REGISTER:
-    case Translation::INT32_REGISTER:
-    case Translation::DOUBLE_REGISTER:
-    case Translation::DUPLICATE:
-      // We are at safepoint which corresponds to call.  All registers are
-      // saved by caller so there would be no live registers at this
-      // point. Thus these translation commands should not be used.
-      break;
-
-    case Translation::STACK_SLOT: {
-      int slot_index = iterator->Next();
-      Address slot_addr = SlotAddress(frame, slot_index);
-      return SlotRef(slot_addr, SlotRef::TAGGED);
-    }
-
-    case Translation::INT32_STACK_SLOT: {
-      int slot_index = iterator->Next();
-      Address slot_addr = SlotAddress(frame, slot_index);
-      return SlotRef(slot_addr, SlotRef::INT32);
-    }
-
-    case Translation::DOUBLE_STACK_SLOT: {
-      int slot_index = iterator->Next();
-      Address slot_addr = SlotAddress(frame, slot_index);
-      return SlotRef(slot_addr, SlotRef::DOUBLE);
-    }
-
-    case Translation::LITERAL: {
-      int literal_index = iterator->Next();
-      return SlotRef(data->LiteralArray()->get(literal_index));
-    }
-  }
-
-  UNREACHABLE();
-  return SlotRef();
-}
-
-
-
-
-
-static void ComputeSlotMappingForArguments(JavaScriptFrame* frame,
-                                           int inlined_frame_index,
-                                           Vector<SlotRef>* args_slots) {
-  AssertNoAllocation no_gc;
-  int deopt_index = AstNode::kNoNumber;
-  DeoptimizationInputData* data =
-      static_cast<OptimizedFrame*>(frame)->GetDeoptimizationData(&deopt_index);
-  TranslationIterator it(data->TranslationByteArray(),
-                         data->TranslationIndex(deopt_index)->value());
-  Translation::Opcode opcode = static_cast<Translation::Opcode>(it.Next());
-  ASSERT(opcode == Translation::BEGIN);
-  int frame_count = it.Next();
-  USE(frame_count);
-  ASSERT(frame_count > inlined_frame_index);
-  int frames_to_skip = inlined_frame_index;
-  while (true) {
-    opcode = static_cast<Translation::Opcode>(it.Next());
-    // Skip over operands to advance to the next opcode.
-    it.Skip(Translation::NumberOfOperandsFor(opcode));
-    if (opcode == Translation::FRAME) {
-      if (frames_to_skip == 0) {
-        // We reached the frame corresponding to the inlined function
-        // in question.  Process the translation commands for the
-        // arguments.
-        //
-        // Skip the translation command for the receiver.
-        it.Skip(Translation::NumberOfOperandsFor(
-            static_cast<Translation::Opcode>(it.Next())));
-        // Compute slots for arguments.
-        for (int i = 0; i < args_slots->length(); ++i) {
-          (*args_slots)[i] = ComputeSlotForNextArgument(&it, data, frame);
-        }
-        return;
-      }
-      frames_to_skip--;
-    }
-  }
-
-  UNREACHABLE();
-}
-
 
 static MaybeObject* ConstructArgumentsObjectForInlinedFunction(
     JavaScriptFrame* frame,
@@ -742,7 +576,9 @@ static MaybeObject* ConstructArgumentsObjectForInlinedFunction(
   Factory* factory = Isolate::Current()->factory();
   int args_count = inlined_function->shared()->formal_parameter_count();
   ScopedVector<SlotRef> args_slots(args_count);
-  ComputeSlotMappingForArguments(frame, inlined_frame_index, &args_slots);
+  SlotRef::ComputeSlotMappingForArguments(frame,
+                                          inlined_frame_index,
+                                          &args_slots);
   Handle<JSObject> arguments =
       factory->NewArgumentsObject(inlined_function, args_count);
   Handle<FixedArray> array = factory->NewFixedArray(args_count);
index 4aa8d6aa9ac1340237b7317a0d94c837c7c947cb..d6846c850da3d60a4f1eea45b32488d029fa7141 100644 (file)
@@ -136,7 +136,7 @@ class JavaScriptFrameConstants : public AllStatic {
  public:
   // FP-relative.
   static const int kLocal0Offset = StandardFrameConstants::kExpressionsOffset;
-  static const int kSavedRegistersOffset = +2 * kPointerSize;
+  static const int kLastParameterOffset = +2 * kPointerSize;
   static const int kFunctionOffset = StandardFrameConstants::kMarkerOffset;
 
   // Caller SP-relative.
index d70cc28ae0a9c9b330e05c44ce6ad89d8be88ec2..63b82b79e9ed0f217535a5d05f7b91494389bd1a 100644 (file)
@@ -1194,4 +1194,103 @@ DeoptimizingCodeListNode::~DeoptimizingCodeListNode() {
 }
 
 
+// We can't intermix stack decoding and allocations because
+// deoptimization infrastracture is not GC safe.
+// Thus we build a temporary structure in malloced space.
+SlotRef SlotRef::ComputeSlotForNextArgument(TranslationIterator* iterator,
+                                            DeoptimizationInputData* data,
+                                            JavaScriptFrame* frame) {
+  Translation::Opcode opcode =
+      static_cast<Translation::Opcode>(iterator->Next());
+
+  switch (opcode) {
+    case Translation::BEGIN:
+    case Translation::FRAME:
+      // Peeled off before getting here.
+      break;
+
+    case Translation::ARGUMENTS_OBJECT:
+      // This can be only emitted for local slots not for argument slots.
+      break;
+
+    case Translation::REGISTER:
+    case Translation::INT32_REGISTER:
+    case Translation::DOUBLE_REGISTER:
+    case Translation::DUPLICATE:
+      // We are at safepoint which corresponds to call.  All registers are
+      // saved by caller so there would be no live registers at this
+      // point. Thus these translation commands should not be used.
+      break;
+
+    case Translation::STACK_SLOT: {
+      int slot_index = iterator->Next();
+      Address slot_addr = SlotAddress(frame, slot_index);
+      return SlotRef(slot_addr, SlotRef::TAGGED);
+    }
+
+    case Translation::INT32_STACK_SLOT: {
+      int slot_index = iterator->Next();
+      Address slot_addr = SlotAddress(frame, slot_index);
+      return SlotRef(slot_addr, SlotRef::INT32);
+    }
+
+    case Translation::DOUBLE_STACK_SLOT: {
+      int slot_index = iterator->Next();
+      Address slot_addr = SlotAddress(frame, slot_index);
+      return SlotRef(slot_addr, SlotRef::DOUBLE);
+    }
+
+    case Translation::LITERAL: {
+      int literal_index = iterator->Next();
+      return SlotRef(data->LiteralArray()->get(literal_index));
+    }
+  }
+
+  UNREACHABLE();
+  return SlotRef();
+}
+
+
+void SlotRef::ComputeSlotMappingForArguments(JavaScriptFrame* frame,
+                                             int inlined_frame_index,
+                                             Vector<SlotRef>* args_slots) {
+  AssertNoAllocation no_gc;
+  int deopt_index = AstNode::kNoNumber;
+  DeoptimizationInputData* data =
+      static_cast<OptimizedFrame*>(frame)->GetDeoptimizationData(&deopt_index);
+  TranslationIterator it(data->TranslationByteArray(),
+                         data->TranslationIndex(deopt_index)->value());
+  Translation::Opcode opcode = static_cast<Translation::Opcode>(it.Next());
+  ASSERT(opcode == Translation::BEGIN);
+  int frame_count = it.Next();
+  USE(frame_count);
+  ASSERT(frame_count > inlined_frame_index);
+  int frames_to_skip = inlined_frame_index;
+  while (true) {
+    opcode = static_cast<Translation::Opcode>(it.Next());
+    // Skip over operands to advance to the next opcode.
+    it.Skip(Translation::NumberOfOperandsFor(opcode));
+    if (opcode == Translation::FRAME) {
+      if (frames_to_skip == 0) {
+        // We reached the frame corresponding to the inlined function
+        // in question.  Process the translation commands for the
+        // arguments.
+        //
+        // Skip the translation command for the receiver.
+        it.Skip(Translation::NumberOfOperandsFor(
+            static_cast<Translation::Opcode>(it.Next())));
+        // Compute slots for arguments.
+        for (int i = 0; i < args_slots->length(); ++i) {
+          (*args_slots)[i] = ComputeSlotForNextArgument(&it, data, frame);
+        }
+        return;
+      }
+      frames_to_skip--;
+    }
+  }
+
+  UNREACHABLE();
+}
+
+
 } }  // namespace v8::internal
index 64e0cdf2e83f822e3e124ff4d3bea71f8b67383f..514de05f267ae4dc42328badd6732a4862fb2364 100644 (file)
@@ -552,6 +552,78 @@ class DeoptimizingCodeListNode : public Malloced {
 };
 
 
+class SlotRef BASE_EMBEDDED {
+ public:
+  enum SlotRepresentation {
+    UNKNOWN,
+    TAGGED,
+    INT32,
+    DOUBLE,
+    LITERAL
+  };
+
+  SlotRef()
+      : addr_(NULL), representation_(UNKNOWN) { }
+
+  SlotRef(Address addr, SlotRepresentation representation)
+      : addr_(addr), representation_(representation) { }
+
+  explicit SlotRef(Object* literal)
+      : literal_(literal), representation_(LITERAL) { }
+
+  Handle<Object> GetValue() {
+    switch (representation_) {
+      case TAGGED:
+        return Handle<Object>(Memory::Object_at(addr_));
+
+      case INT32: {
+        int value = Memory::int32_at(addr_);
+        if (Smi::IsValid(value)) {
+          return Handle<Object>(Smi::FromInt(value));
+        } else {
+          return Isolate::Current()->factory()->NewNumberFromInt(value);
+        }
+      }
+
+      case DOUBLE: {
+        double value = Memory::double_at(addr_);
+        return Isolate::Current()->factory()->NewNumber(value);
+      }
+
+      case LITERAL:
+        return literal_;
+
+      default:
+        UNREACHABLE();
+        return Handle<Object>::null();
+    }
+  }
+
+  static void ComputeSlotMappingForArguments(JavaScriptFrame* frame,
+                                             int inlined_frame_index,
+                                             Vector<SlotRef>* args_slots);
+
+ private:
+  Address addr_;
+  Handle<Object> literal_;
+  SlotRepresentation representation_;
+
+  static Address SlotAddress(JavaScriptFrame* frame, int slot_index) {
+    if (slot_index >= 0) {
+      const int offset = JavaScriptFrameConstants::kLocal0Offset;
+      return frame->fp() + offset - (slot_index * kPointerSize);
+    } else {
+      const int offset = JavaScriptFrameConstants::kLastParameterOffset;
+      return frame->fp() + offset - ((slot_index + 1) * kPointerSize);
+    }
+  }
+
+  static SlotRef ComputeSlotForNextArgument(TranslationIterator* iterator,
+                                            DeoptimizationInputData* data,
+                                            JavaScriptFrame* frame);
+};
+
+
 } }  // namespace v8::internal
 
 #endif  // V8_DEOPTIMIZER_H_
index 79aa2507fba0c31230192a6bb6754a3d5599ce95..8acc1e6ffe9fce2fbc0cfc27cc375169a6a4aa59 100644 (file)
@@ -617,7 +617,7 @@ Object* JavaScriptFrame::GetParameter(int index) const {
 
 int JavaScriptFrame::ComputeParametersCount() const {
   Address base  = caller_sp() + JavaScriptFrameConstants::kReceiverOffset;
-  Address limit = fp() + JavaScriptFrameConstants::kSavedRegistersOffset;
+  Address limit = fp() + JavaScriptFrameConstants::kLastParameterOffset;
   return static_cast<int>((base - limit) / kPointerSize);
 }
 
@@ -1084,7 +1084,7 @@ void JavaScriptFrame::Iterate(ObjectVisitor* v) const {
 
 void JavaScriptFrame::IterateArguments(ObjectVisitor* v) const {
   // Traverse callee-saved registers, receiver, and parameters.
-  const int kBaseOffset = JavaScriptFrameConstants::kSavedRegistersOffset;
+  const int kBaseOffset = JavaScriptFrameConstants::kLastParameterOffset;
   const int kLimitOffset = JavaScriptFrameConstants::kReceiverOffset;
   Object** base = &Memory::Object_at(fp() + kBaseOffset);
   Object** limit = &Memory::Object_at(caller_sp() + kLimitOffset) + 1;
index 8573ba1eb3090fc3eab8d7b01019fd9d9295517e..5fe47db4239144c3be2ecb90b4972822d5cadab8 100644 (file)
@@ -3852,12 +3852,18 @@ void HGraphBuilder::HandlePolymorphicCallNamed(Call* expr,
 
 void HGraphBuilder::TraceInline(Handle<JSFunction> target, const char* reason) {
   if (FLAG_trace_inlining) {
-    SmartPointer<char> callee = target->shared()->DebugName()->ToCString();
-    SmartPointer<char> caller =
-        info()->function()->debug_name()->ToCString();
     if (reason == NULL) {
+      // We are currently in the context of inlined function thus we have
+      // to go to an outer FunctionState to get caller.
+      SmartPointer<char> callee = target->shared()->DebugName()->ToCString();
+      SmartPointer<char> caller =
+          function_state()->outer()->compilation_info()->function()->
+              debug_name()->ToCString();
       PrintF("Inlined %s called from %s.\n", *callee, *caller);
     } else {
+      SmartPointer<char> callee = target->shared()->DebugName()->ToCString();
+      SmartPointer<char> caller =
+          info()->function()->debug_name()->ToCString();
       PrintF("Did not inline %s called from %s (%s).\n",
              *callee, *caller, reason);
     }
@@ -5117,7 +5123,14 @@ void HGraphBuilder::GenerateIsStringWrapperSafeForDefaultValueOf(
 // Support for construct call checks.
 void HGraphBuilder::GenerateIsConstructCall(CallRuntime* call) {
   ASSERT(call->arguments()->length() == 0);
-  ast_context()->ReturnInstruction(new HIsConstructCall, call->id());
+  if (function_state()->outer() != NULL) {
+    // We are generating graph for inlined function. Currently
+    // constructor inlining is not supported and we can just return
+    // false from %_IsConstructCall().
+    ast_context()->ReturnValue(graph()->GetConstantFalse());
+  } else {
+    ast_context()->ReturnInstruction(new HIsConstructCall, call->id());
+  }
 }
 
 
index e14799a24c8c388d2404eba0c4dffb696d5388d9..28a8dfefe1bf664c8715d83df40c2d4c22e78b4b 100644 (file)
@@ -544,6 +544,8 @@ class FunctionState BASE_EMBEDDED {
     test_context_ = NULL;
   }
 
+  FunctionState* outer() { return outer_; }
+
  private:
   HGraphBuilder* owner_;
 
index 80846949a0d67960c07394fcfc6c8cb2b493806c..0f95abd874f7cb328b589651fbd311b10a0f4565 100644 (file)
@@ -108,7 +108,7 @@ class JavaScriptFrameConstants : public AllStatic {
  public:
   // FP-relative.
   static const int kLocal0Offset = StandardFrameConstants::kExpressionsOffset;
-  static const int kSavedRegistersOffset = +2 * kPointerSize;
+  static const int kLastParameterOffset = +2 * kPointerSize;
   static const int kFunctionOffset = StandardFrameConstants::kMarkerOffset;
 
   // Caller SP-relative.
index 644147030d6534c6a8638ae07625ea248d00530a..f507590bb90a764aa6ef14e04b52f72aa166e3c7 100644 (file)
@@ -147,7 +147,7 @@ class JavaScriptFrameConstants : public AllStatic {
  public:
   // FP-relative.
   static const int kLocal0Offset = StandardFrameConstants::kExpressionsOffset;
-  static const int kSavedRegistersOffset = +2 * kPointerSize;
+  static const int kLastParameterOffset = +2 * kPointerSize;
   static const int kFunctionOffset = StandardFrameConstants::kMarkerOffset;
 
   // Caller SP-relative.
index bd3b8233a03a9a4042ff0f31fb9ecd5d96c9ad68..ddfdb7fd0c7d7f5f980c619c6e2a117bd9753d5d 100644 (file)
@@ -7066,6 +7066,46 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_NewClosure) {
   return *result;
 }
 
+
+static SmartPointer<Object**> GetNonBoundArguments(int bound_argc,
+                                                   int* total_argc) {
+  // Find frame containing arguments passed to the caller.
+  JavaScriptFrameIterator it;
+  JavaScriptFrame* frame = it.frame();
+  List<JSFunction*> functions(2);
+  frame->GetFunctions(&functions);
+  if (functions.length() > 1) {
+    int inlined_frame_index = functions.length() - 1;
+    JSFunction* inlined_function = functions[inlined_frame_index];
+    int args_count = inlined_function->shared()->formal_parameter_count();
+    ScopedVector<SlotRef> args_slots(args_count);
+    SlotRef::ComputeSlotMappingForArguments(frame,
+                                            inlined_frame_index,
+                                            &args_slots);
+
+    *total_argc = bound_argc + args_count;
+    SmartPointer<Object**> param_data(NewArray<Object**>(*total_argc));
+    for (int i = 0; i < args_count; i++) {
+      Handle<Object> val = args_slots[i].GetValue();
+      param_data[bound_argc + i] = val.location();
+    }
+    return param_data;
+  } else {
+    it.AdvanceToArgumentsFrame();
+    frame = it.frame();
+    int args_count = frame->ComputeParametersCount();
+
+    *total_argc = bound_argc + args_count;
+    SmartPointer<Object**> param_data(NewArray<Object**>(*total_argc));
+    for (int i = 0; i < args_count; i++) {
+      Handle<Object> val = Handle<Object>(frame->GetParameter(i));
+      param_data[bound_argc + i] = val.location();
+    }
+    return param_data;
+  }
+}
+
+
 RUNTIME_FUNCTION(MaybeObject*, Runtime_NewObjectFromBound) {
   HandleScope scope(isolate);
   ASSERT(args.length() == 2);
@@ -7073,34 +7113,22 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_NewObjectFromBound) {
   CONVERT_ARG_CHECKED(JSFunction, function, 0);
 
   // Second argument is either null or an array of bound arguments.
-  FixedArray* bound_args = NULL;
+  Handle<FixedArray> bound_args;
   int bound_argc = 0;
   if (!args[1]->IsNull()) {
     CONVERT_ARG_CHECKED(JSArray, params, 1);
     RUNTIME_ASSERT(params->HasFastElements());
-    bound_args = FixedArray::cast(params->elements());
+    bound_args = Handle<FixedArray>(FixedArray::cast(params->elements()));
     bound_argc = Smi::cast(params->length())->value();
   }
 
-  // Find frame containing arguments passed to the caller.
-  JavaScriptFrameIterator it;
-  JavaScriptFrame* frame = it.frame();
-  ASSERT(!frame->is_optimized());
-  it.AdvanceToArgumentsFrame();
-  frame = it.frame();
-  int argc = frame->ComputeParametersCount();
-
-  // Prepend bound arguments to caller's arguments.
-  int total_argc = bound_argc + argc;
-  SmartPointer<Object**> param_data(NewArray<Object**>(total_argc));
+  int total_argc = 0;
+  SmartPointer<Object**> param_data =
+      GetNonBoundArguments(bound_argc, &total_argc);
   for (int i = 0; i < bound_argc; i++) {
     Handle<Object> val = Handle<Object>(bound_args->get(i));
     param_data[i] = val.location();
   }
-  for (int i = 0; i < argc; i++) {
-    Handle<Object> val = Handle<Object>(frame->GetParameter(i));
-    param_data[bound_argc + i] = val.location();
-  }
 
   bool exception = false;
   Handle<Object> result =
index 81be8191962eaa9ab617fccd0effac3163d8cb58..b14267c82d8c4ed9ca45166388c1d2fe7b96e97a 100644 (file)
@@ -99,7 +99,7 @@ class JavaScriptFrameConstants : public AllStatic {
  public:
   // FP-relative.
   static const int kLocal0Offset = StandardFrameConstants::kExpressionsOffset;
-  static const int kSavedRegistersOffset = +2 * kPointerSize;
+  static const int kLastParameterOffset = +2 * kPointerSize;
   static const int kFunctionOffset = StandardFrameConstants::kMarkerOffset;
 
   // Caller SP-relative.
diff --git a/test/mjsunit/regress/regress-1229.js b/test/mjsunit/regress/regress-1229.js
new file mode 100644 (file)
index 0000000..4afb964
--- /dev/null
@@ -0,0 +1,79 @@
+// Copyright 2011 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+//       notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+//       copyright notice, this list of conditions and the following
+//       disclaimer in the documentation and/or other materials provided
+//       with the distribution.
+//     * Neither the name of Google Inc. nor the names of its
+//       contributors may be used to endorse or promote products derived
+//       from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (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
+
+// Check that %NewObjectFromBound works correctly when called from optimized
+// frame.
+function foo(x, y, z) {
+  assertEquals(1, x);
+  assertEquals(2, y);
+  assertEquals(3, z);
+}
+
+var bound_arg = [1];
+
+function f(y, z) {
+  return %NewObjectFromBound(foo, bound_arg);
+}
+
+// Check that %NewObjectFromBound looks at correct frame for inlined function.
+function g(z, y) {
+  return f(y, z); /* f should be inlined into g, note rotated arguments */
+}
+
+// Check that %NewObjectFromBound looks at correct frame for inlined function.
+function ff(x) { }
+function h(z2, y2) {
+  var local_z = z2 >> 1;
+  ff(local_z);
+  var local_y = y2 >> 1;
+  ff(local_y);
+  return f(local_y, local_z); /* f should be inlined into h */
+}
+
+for (var i = 0; i < 100000; i++) f(2, 3);
+
+for (var i = 0; i < 100000; i++) g(3, 2);
+
+for (var i = 0; i < 100000; i++) h(6, 4);
+
+// Check that %_IsConstructCall returns correct value when inlined
+var NON_CONSTRUCT_MARKER = {};
+var CONSTRUCT_MARKER = {};
+function baz() {
+  return (!%_IsConstructCall()) ? NON_CONSTRUCT_MARKER : CONSTRUCT_MARKER;
+}
+
+function bar(x, y, z) {
+  var non_construct = baz(); /* baz should be inlined */
+  assertEquals(non_construct, NON_CONSTRUCT_MARKER);
+  var construct = new baz();
+  assertEquals(construct, CONSTRUCT_MARKER);
+}
+
+for (var i = 0; i < 100000; i++) new bar(1, 2, 3);