Push version 1.3.10 to trunk.
Fixed profiler on Mac in 64-bit mode.
Optimized creation of objects from simple constructor functions on ARM.
Fixed a number of debugger issues.
Reduced the amount of memory consumed by V8.
git-svn-id: http://v8.googlecode.com/svn/trunk@2866 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
diff --git a/src/arm/builtins-arm.cc b/src/arm/builtins-arm.cc
index daf2378..920110f 100644
--- a/src/arm/builtins-arm.cc
+++ b/src/arm/builtins-arm.cc
@@ -1,4 +1,4 @@
-// Copyright 2006-2008 the V8 project authors. All rights reserved.
+// Copyright 2006-2009 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:
@@ -88,23 +88,200 @@
// Enter a construct frame.
__ EnterConstructFrame();
- // Preserve the two incoming parameters
+ // Preserve the two incoming parameters on the stack.
__ mov(r0, Operand(r0, LSL, kSmiTagSize));
- __ push(r0); // smi-tagged arguments count
- __ push(r1); // constructor function
+ __ push(r0); // Smi-tagged arguments count.
+ __ push(r1); // Constructor function.
- // Allocate the new receiver object.
+ // Use r7 for holding undefined which is used in several places below.
+ __ LoadRoot(r7, Heap::kUndefinedValueRootIndex);
+
+ // Try to allocate the object without transitioning into C code. If any of the
+ // preconditions is not met, the code bails out to the runtime call.
+ Label rt_call, allocated;
+ if (FLAG_inline_new) {
+ Label undo_allocation;
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ ExternalReference debug_step_in_fp =
+ ExternalReference::debug_step_in_fp_address();
+ __ mov(r2, Operand(debug_step_in_fp));
+ __ ldr(r2, MemOperand(r2));
+ __ tst(r2, r2);
+ __ b(nz, &rt_call);
+#endif
+
+ // Load the initial map and verify that it is in fact a map.
+ // r1: constructor function
+ // r7: undefined
+ __ ldr(r2, FieldMemOperand(r1, JSFunction::kPrototypeOrInitialMapOffset));
+ __ tst(r2, Operand(kSmiTagMask));
+ __ b(eq, &rt_call);
+ __ CompareObjectType(r2, r3, r4, MAP_TYPE);
+ __ b(ne, &rt_call);
+
+ // Check that the constructor is not constructing a JSFunction (see comments
+ // in Runtime_NewObject in runtime.cc). In which case the initial map's
+ // instance type would be JS_FUNCTION_TYPE.
+ // r1: constructor function
+ // r2: initial map
+ // r7: undefined
+ __ CompareInstanceType(r2, r3, JS_FUNCTION_TYPE);
+ __ b(eq, &rt_call);
+
+ // Now allocate the JSObject on the heap.
+ // r1: constructor function
+ // r2: initial map
+ // r7: undefined
+ __ ldrb(r3, FieldMemOperand(r2, Map::kInstanceSizeOffset));
+ __ AllocateObjectInNewSpace(r3, r4, r5, r6, &rt_call, NO_ALLOCATION_FLAGS);
+
+ // Allocated the JSObject, now initialize the fields. Map is set to initial
+ // map and properties and elements are set to empty fixed array.
+ // r1: constructor function
+ // r2: initial map
+ // r3: object size
+ // r4: JSObject (not tagged)
+ // r7: undefined
+ __ LoadRoot(r6, Heap::kEmptyFixedArrayRootIndex);
+ __ mov(r5, r4);
+ ASSERT_EQ(0 * kPointerSize, JSObject::kMapOffset);
+ __ str(r2, MemOperand(r5, kPointerSize, PostIndex));
+ ASSERT_EQ(1 * kPointerSize, JSObject::kPropertiesOffset);
+ __ str(r6, MemOperand(r5, kPointerSize, PostIndex));
+ ASSERT_EQ(2 * kPointerSize, JSObject::kElementsOffset);
+ __ str(r6, MemOperand(r5, kPointerSize, PostIndex));
+
+ // Fill all the in-object properties with undefined.
+ // r1: constructor function
+ // r2: initial map
+ // r3: object size (in words)
+ // r4: JSObject (not tagged)
+ // r5: First in-object property of JSObject (not tagged)
+ // r7: undefined
+ __ add(r6, r4, Operand(r3, LSL, kPointerSizeLog2)); // End of object.
+ ASSERT_EQ(3 * kPointerSize, JSObject::kHeaderSize);
+ { Label loop, entry;
+ __ b(&entry);
+ __ bind(&loop);
+ __ str(r7, MemOperand(r5, kPointerSize, PostIndex));
+ __ bind(&entry);
+ __ cmp(r5, Operand(r6));
+ __ b(lt, &loop);
+ }
+
+ // Add the object tag to make the JSObject real, so that we can continue and
+ // jump into the continuation code at any time from now on. Any failures
+ // need to undo the allocation, so that the heap is in a consistent state
+ // and verifiable.
+ __ add(r4, r4, Operand(kHeapObjectTag));
+
+ // Check if a non-empty properties array is needed. Continue with allocated
+ // object if not fall through to runtime call if it is.
+ // r1: constructor function
+ // r4: JSObject
+ // r5: start of next object (not tagged)
+ // r7: undefined
+ __ ldrb(r3, FieldMemOperand(r2, Map::kUnusedPropertyFieldsOffset));
+ // The field instance sizes contains both pre-allocated property fields and
+ // in-object properties.
+ __ ldr(r0, FieldMemOperand(r2, Map::kInstanceSizesOffset));
+ __ and_(r6,
+ r0,
+ Operand(0x000000FF << Map::kPreAllocatedPropertyFieldsByte * 8));
+ __ add(r3, r3, Operand(r6, LSR, Map::kPreAllocatedPropertyFieldsByte * 8));
+ __ and_(r6, r0, Operand(0x000000FF << Map::kInObjectPropertiesByte * 8));
+ __ sub(r3, r3, Operand(r6, LSR, Map::kInObjectPropertiesByte * 8), SetCC);
+
+ // Done if no extra properties are to be allocated.
+ __ b(eq, &allocated);
+ __ Assert(pl, "Property allocation count failed.");
+
+ // Scale the number of elements by pointer size and add the header for
+ // FixedArrays to the start of the next object calculation from above.
+ // r1: constructor
+ // r3: number of elements in properties array
+ // r4: JSObject
+ // r5: start of next object
+ // r7: undefined
+ __ add(r0, r3, Operand(FixedArray::kHeaderSize / kPointerSize));
+ __ AllocateObjectInNewSpace(r0,
+ r5,
+ r6,
+ r2,
+ &undo_allocation,
+ RESULT_CONTAINS_TOP);
+
+ // Initialize the FixedArray.
+ // r1: constructor
+ // r3: number of elements in properties array
+ // r4: JSObject
+ // r5: FixedArray (not tagged)
+ // r7: undefined
+ __ LoadRoot(r6, Heap::kFixedArrayMapRootIndex);
+ __ mov(r2, r5);
+ ASSERT_EQ(0 * kPointerSize, JSObject::kMapOffset);
+ __ str(r6, MemOperand(r2, kPointerSize, PostIndex));
+ ASSERT_EQ(1 * kPointerSize, Array::kLengthOffset);
+ __ str(r3, MemOperand(r2, kPointerSize, PostIndex));
+
+ // Initialize the fields to undefined.
+ // r1: constructor function
+ // r2: First element of FixedArray (not tagged)
+ // r3: number of elements in properties array
+ // r4: JSObject
+ // r5: FixedArray (not tagged)
+ // r7: undefined
+ __ add(r6, r2, Operand(r3, LSL, kPointerSizeLog2)); // End of object.
+ ASSERT_EQ(2 * kPointerSize, FixedArray::kHeaderSize);
+ { Label loop, entry;
+ __ b(&entry);
+ __ bind(&loop);
+ __ str(r7, MemOperand(r2, kPointerSize, PostIndex));
+ __ bind(&entry);
+ __ cmp(r2, Operand(r6));
+ __ b(lt, &loop);
+ }
+
+ // Store the initialized FixedArray into the properties field of
+ // the JSObject
+ // r1: constructor function
+ // r4: JSObject
+ // r5: FixedArray (not tagged)
+ __ add(r5, r5, Operand(kHeapObjectTag)); // Add the heap tag.
+ __ str(r5, FieldMemOperand(r4, JSObject::kPropertiesOffset));
+
+ // Continue with JSObject being successfully allocated
+ // r1: constructor function
+ // r4: JSObject
+ __ jmp(&allocated);
+
+ // Undo the setting of the new top so that the heap is verifiable. For
+ // example, the map's unused properties potentially do not match the
+ // allocated objects unused properties.
+ // r4: JSObject (previous new top)
+ __ bind(&undo_allocation);
+ __ UndoAllocationInNewSpace(r4, r5);
+ }
+
+ // Allocate the new receiver object using the runtime call.
+ // r1: constructor function
+ __ bind(&rt_call);
__ push(r1); // argument for Runtime_NewObject
__ CallRuntime(Runtime::kNewObject, 1);
- __ push(r0); // save the receiver
+ __ mov(r4, r0);
+
+ // Receiver for constructor call allocated.
+ // r4: JSObject
+ __ bind(&allocated);
+ __ push(r4);
// Push the function and the allocated receiver from the stack.
// sp[0]: receiver (newly allocated object)
// sp[1]: constructor function
// sp[2]: number of arguments (smi-tagged)
__ ldr(r1, MemOperand(sp, kPointerSize));
- __ push(r1); // function
- __ push(r0); // receiver
+ __ push(r1); // Constructor function.
+ __ push(r4); // Receiver.
// Reload the number of arguments from the stack.
// r1: constructor function
@@ -194,6 +371,7 @@
__ LeaveConstructFrame();
__ add(sp, sp, Operand(r1, LSL, kPointerSizeLog2 - 1));
__ add(sp, sp, Operand(kPointerSize));
+ __ IncrementCounter(&Counters::constructed_objects, 1, r1, r2);
__ Jump(lr);
}
diff --git a/src/arm/codegen-arm.cc b/src/arm/codegen-arm.cc
index b94aa10..7b3662d 100644
--- a/src/arm/codegen-arm.cc
+++ b/src/arm/codegen-arm.cc
@@ -4950,12 +4950,12 @@
Register scratch2) { // Another scratch register.
// Allocate an object in the heap for the heap number and tag it as a heap
// object.
- __ AllocateObjectInNewSpace(HeapNumber::kSize,
+ __ AllocateObjectInNewSpace(HeapNumber::kSize / kPointerSize,
result,
scratch1,
scratch2,
need_gc,
- true);
+ TAG_OBJECT);
// Get heap number map and store it in the allocated object.
__ LoadRoot(scratch1, Heap::kHeapNumberMapRootIndex);
@@ -5623,7 +5623,7 @@
// argument, so give it a Smi.
__ mov(r0, Operand(Smi::FromInt(0)));
__ push(r0);
- __ TailCallRuntime(ExternalReference(Runtime::kStackGuard), 1);
+ __ TailCallRuntime(ExternalReference(Runtime::kStackGuard), 1, 1);
__ StubReturn(1);
}
@@ -5678,6 +5678,13 @@
}
+int CEntryStub::MinorKey() {
+ ASSERT(result_size_ <= 2);
+ // Result returned in r0 or r0+r1 by default.
+ return 0;
+}
+
+
void CEntryStub::GenerateThrowTOS(MacroAssembler* masm) {
// r0 holds the exception.
@@ -6195,7 +6202,7 @@
// by calling the runtime system.
__ bind(&slow);
__ push(r1);
- __ TailCallRuntime(ExternalReference(Runtime::kGetArgumentsProperty), 1);
+ __ TailCallRuntime(ExternalReference(Runtime::kGetArgumentsProperty), 1, 1);
}
@@ -6216,7 +6223,7 @@
// Do the runtime call to allocate the arguments object.
__ bind(&runtime);
- __ TailCallRuntime(ExternalReference(Runtime::kNewArgumentsFast), 3);
+ __ TailCallRuntime(ExternalReference(Runtime::kNewArgumentsFast), 3, 1);
}
diff --git a/src/arm/constants-arm.cc b/src/arm/constants-arm.cc
new file mode 100644
index 0000000..964bfe1
--- /dev/null
+++ b/src/arm/constants-arm.cc
@@ -0,0 +1,92 @@
+// Copyright 2009 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.
+
+#include "v8.h"
+
+#include "constants-arm.h"
+
+
+namespace assembler {
+namespace arm {
+
+namespace v8i = v8::internal;
+
+
+// These register names are defined in a way to match the native disassembler
+// formatting. See for example the command "objdump -d <binary file>".
+const char* Registers::names_[kNumRegisters] = {
+ "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
+ "r8", "r9", "r10", "fp", "ip", "sp", "lr", "pc",
+};
+
+
+// List of alias names which can be used when referring to ARM registers.
+const Registers::RegisterAlias Registers::aliases_[] = {
+ {10, "sl"},
+ {11, "r11"},
+ {12, "r12"},
+ {13, "r13"},
+ {14, "r14"},
+ {15, "r15"},
+ {kNoRegister, NULL}
+};
+
+
+const char* Registers::Name(int reg) {
+ const char* result;
+ if ((0 <= reg) && (reg < kNumRegisters)) {
+ result = names_[reg];
+ } else {
+ result = "noreg";
+ }
+ return result;
+}
+
+
+int Registers::Number(const char* name) {
+ // Look through the canonical names.
+ for (int i = 0; i < kNumRegisters; i++) {
+ if (strcmp(names_[i], name) == 0) {
+ return i;
+ }
+ }
+
+ // Look through the alias names.
+ int i = 0;
+ while (aliases_[i].reg != kNoRegister) {
+ if (strcmp(aliases_[i].name, name) == 0) {
+ return aliases_[i].reg;
+ }
+ i++;
+ }
+
+ // No register with the reguested name found.
+ return kNoRegister;
+}
+
+
+} } // namespace assembler::arm
diff --git a/src/arm/constants-arm.h b/src/arm/constants-arm.h
index f0311df..2f2b709 100644
--- a/src/arm/constants-arm.h
+++ b/src/arm/constants-arm.h
@@ -52,6 +52,13 @@
namespace assembler {
namespace arm {
+// Number of registers in normal ARM mode.
+static const int kNumRegisters = 16;
+
+// PC is register 15.
+static const int kPCRegister = 15;
+static const int kNoRegister = -1;
+
// Defines constants and accessor classes to assemble, disassemble and
// simulate ARM instructions.
//
@@ -269,6 +276,27 @@
};
+// Helper functions for converting between register numbers and names.
+class Registers {
+ public:
+ // Return the name of the register.
+ static const char* Name(int reg);
+
+ // Lookup the register number for the name provided.
+ static int Number(const char* name);
+
+ struct RegisterAlias {
+ int reg;
+ const char *name;
+ };
+
+ private:
+ static const char* names_[kNumRegisters];
+ static const RegisterAlias aliases_[];
+};
+
+
+
} } // namespace assembler::arm
#endif // V8_ARM_CONSTANTS_ARM_H_
diff --git a/src/arm/debug-arm.cc b/src/arm/debug-arm.cc
index bcfab6c..e142841 100644
--- a/src/arm/debug-arm.cc
+++ b/src/arm/debug-arm.cc
@@ -179,12 +179,6 @@
}
-void Debug::GenerateReturnDebugBreakEntry(MacroAssembler* masm) {
- // Generate nothing as this handling of debug break return is not done this
- // way on ARM - yet.
-}
-
-
void Debug::GenerateStubNoRegistersDebugBreak(MacroAssembler* masm) {
// Generate nothing as CodeStub CallFunction is not used on ARM.
}
diff --git a/src/arm/disasm-arm.cc b/src/arm/disasm-arm.cc
index 2638409..6431483 100644
--- a/src/arm/disasm-arm.cc
+++ b/src/arm/disasm-arm.cc
@@ -57,6 +57,7 @@
#include "v8.h"
+#include "constants-arm.h"
#include "disasm.h"
#include "macro-assembler.h"
#include "platform.h"
@@ -898,16 +899,6 @@
namespace v8i = v8::internal;
-static const int kMaxRegisters = 16;
-
-// These register names are defined in a way to match the native disassembler
-// formatting. See for example the command "objdump -d <binary file>".
-static const char* reg_names[kMaxRegisters] = {
- "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
- "r8", "r9", "r10", "fp", "ip", "sp", "lr", "pc",
-};
-
-
const char* NameConverter::NameOfAddress(byte* addr) const {
static v8::internal::EmbeddedVector<char, 32> tmp_buffer;
v8::internal::OS::SNPrintF(tmp_buffer, "%p", addr);
@@ -921,13 +912,7 @@
const char* NameConverter::NameOfCPURegister(int reg) const {
- const char* result;
- if ((0 <= reg) && (reg < kMaxRegisters)) {
- result = reg_names[reg];
- } else {
- result = "noreg";
- }
- return result;
+ return assembler::arm::Registers::Name(reg);
}
diff --git a/src/arm/ic-arm.cc b/src/arm/ic-arm.cc
index 848d04b..d230b45 100644
--- a/src/arm/ic-arm.cc
+++ b/src/arm/ic-arm.cc
@@ -391,7 +391,7 @@
__ mov(r0, Operand(2));
__ mov(r1, Operand(f));
- CEntryStub stub;
+ CEntryStub stub(1);
__ CallStub(&stub);
// Move result to r1 and leave the internal frame.
@@ -503,7 +503,7 @@
__ stm(db_w, sp, r2.bit() | r3.bit());
// Perform tail call to the entry.
- __ TailCallRuntime(f, 2);
+ __ TailCallRuntime(f, 2, 1);
}
@@ -543,7 +543,7 @@
__ ldm(ia, sp, r2.bit() | r3.bit());
__ stm(db_w, sp, r2.bit() | r3.bit());
- __ TailCallRuntime(f, 2);
+ __ TailCallRuntime(f, 2, 1);
}
@@ -599,7 +599,7 @@
__ ldm(ia, sp, r0.bit() | r1.bit());
__ stm(db_w, sp, r0.bit() | r1.bit());
// Do tail-call to runtime routine.
- __ TailCallRuntime(ExternalReference(Runtime::kGetProperty), 2);
+ __ TailCallRuntime(ExternalReference(Runtime::kGetProperty), 2, 1);
// Fast case: Do the load.
__ bind(&fast);
@@ -626,7 +626,7 @@
__ ldm(ia, sp, r2.bit() | r3.bit());
__ stm(db_w, sp, r0.bit() | r2.bit() | r3.bit());
- __ TailCallRuntime(f, 3);
+ __ TailCallRuntime(f, 3, 1);
}
@@ -684,7 +684,7 @@
__ ldm(ia, sp, r1.bit() | r3.bit()); // r0 == value, r1 == key, r3 == object
__ stm(db_w, sp, r0.bit() | r1.bit() | r3.bit());
// Do tail-call to runtime routine.
- __ TailCallRuntime(ExternalReference(Runtime::kSetProperty), 3);
+ __ TailCallRuntime(ExternalReference(Runtime::kSetProperty), 3, 1);
// Extra capacity case: Check if there is extra capacity to
// perform the store and update the length. Used for adding one
@@ -761,7 +761,7 @@
// Perform tail call to the entry.
__ TailCallRuntime(
- ExternalReference(IC_Utility(kSharedStoreIC_ExtendStorage)), 3);
+ ExternalReference(IC_Utility(kSharedStoreIC_ExtendStorage)), 3, 1);
}
@@ -798,7 +798,7 @@
// Perform tail call to the entry.
__ TailCallRuntime(
- ExternalReference(IC_Utility(kSharedStoreIC_ExtendStorage)), 3);
+ ExternalReference(IC_Utility(kSharedStoreIC_ExtendStorage)), 3, 1);
}
@@ -814,7 +814,7 @@
__ stm(db_w, sp, r0.bit() | r2.bit() | r3.bit());
// Perform tail call to the entry.
- __ TailCallRuntime(f, 3);
+ __ TailCallRuntime(f, 3, 1);
}
diff --git a/src/arm/macro-assembler-arm.cc b/src/arm/macro-assembler-arm.cc
index 2ca4898..de2db90 100644
--- a/src/arm/macro-assembler-arm.cc
+++ b/src/arm/macro-assembler-arm.cc
@@ -56,6 +56,7 @@
#if defined(USE_THUMB_INTERWORK)
#if !defined(__ARM_ARCH_5T__) && \
!defined(__ARM_ARCH_5TE__) && \
+ !defined(__ARM_ARCH_6__) && \
!defined(__ARM_ARCH_7A__) && \
!defined(__ARM_ARCH_7__)
// add tests for other versions above v5t as required
@@ -773,7 +774,7 @@
Register scratch1,
Register scratch2,
Label* gc_required,
- bool tag_allocated_object) {
+ AllocationFlags flags) {
ASSERT(!result.is(scratch1));
ASSERT(!scratch1.is(scratch2));
@@ -782,7 +783,18 @@
ExternalReference new_space_allocation_top =
ExternalReference::new_space_allocation_top_address();
mov(scratch1, Operand(new_space_allocation_top));
- ldr(result, MemOperand(scratch1));
+ if ((flags & RESULT_CONTAINS_TOP) == 0) {
+ ldr(result, MemOperand(scratch1));
+ } else {
+#ifdef DEBUG
+ // Assert that result actually contains top on entry. scratch2 is used
+ // immediately below so this use of scratch2 does not cause difference with
+ // respect to register content between debug and release mode.
+ ldr(scratch2, MemOperand(scratch1));
+ cmp(result, scratch2);
+ Check(eq, "Unexpected allocation top");
+#endif
+ }
// Calculate new top and bail out if new space is exhausted. Use result
// to calculate the new top.
@@ -790,7 +802,7 @@
ExternalReference::new_space_allocation_limit_address();
mov(scratch2, Operand(new_space_allocation_limit));
ldr(scratch2, MemOperand(scratch2));
- add(result, result, Operand(object_size));
+ add(result, result, Operand(object_size * kPointerSize));
cmp(result, Operand(scratch2));
b(hi, gc_required);
@@ -798,19 +810,98 @@
str(result, MemOperand(scratch1));
// Tag and adjust back to start of new object.
- if (tag_allocated_object) {
- sub(result, result, Operand(object_size - kHeapObjectTag));
+ if ((flags & TAG_OBJECT) != 0) {
+ sub(result, result, Operand((object_size * kPointerSize) -
+ kHeapObjectTag));
} else {
- sub(result, result, Operand(object_size));
+ sub(result, result, Operand(object_size * kPointerSize));
}
}
+void MacroAssembler::AllocateObjectInNewSpace(Register object_size,
+ Register result,
+ Register scratch1,
+ Register scratch2,
+ Label* gc_required,
+ AllocationFlags flags) {
+ ASSERT(!result.is(scratch1));
+ ASSERT(!scratch1.is(scratch2));
+
+ // Load address of new object into result and allocation top address into
+ // scratch1.
+ ExternalReference new_space_allocation_top =
+ ExternalReference::new_space_allocation_top_address();
+ mov(scratch1, Operand(new_space_allocation_top));
+ if ((flags & RESULT_CONTAINS_TOP) == 0) {
+ ldr(result, MemOperand(scratch1));
+ } else {
+#ifdef DEBUG
+ // Assert that result actually contains top on entry. scratch2 is used
+ // immediately below so this use of scratch2 does not cause difference with
+ // respect to register content between debug and release mode.
+ ldr(scratch2, MemOperand(scratch1));
+ cmp(result, scratch2);
+ Check(eq, "Unexpected allocation top");
+#endif
+ }
+
+ // Calculate new top and bail out if new space is exhausted. Use result
+ // to calculate the new top. Object size is in words so a shift is required to
+ // get the number of bytes
+ ExternalReference new_space_allocation_limit =
+ ExternalReference::new_space_allocation_limit_address();
+ mov(scratch2, Operand(new_space_allocation_limit));
+ ldr(scratch2, MemOperand(scratch2));
+ add(result, result, Operand(object_size, LSL, kPointerSizeLog2));
+ cmp(result, Operand(scratch2));
+ b(hi, gc_required);
+
+ // Update allocation top. result temporarily holds the new top,
+ str(result, MemOperand(scratch1));
+
+ // Adjust back to start of new object.
+ sub(result, result, Operand(object_size, LSL, kPointerSizeLog2));
+
+ // Tag object if requested.
+ if ((flags & TAG_OBJECT) != 0) {
+ add(result, result, Operand(kHeapObjectTag));
+ }
+}
+
+
+void MacroAssembler::UndoAllocationInNewSpace(Register object,
+ Register scratch) {
+ ExternalReference new_space_allocation_top =
+ ExternalReference::new_space_allocation_top_address();
+
+ // Make sure the object has no tag before resetting top.
+ and_(object, object, Operand(~kHeapObjectTagMask));
+#ifdef DEBUG
+ // Check that the object un-allocated is below the current top.
+ mov(scratch, Operand(new_space_allocation_top));
+ ldr(scratch, MemOperand(scratch));
+ cmp(object, scratch);
+ Check(lt, "Undo allocation of non allocated memory");
+#endif
+ // Write the address of the object to un-allocate as the current top.
+ mov(scratch, Operand(new_space_allocation_top));
+ str(object, MemOperand(scratch));
+}
+
+
void MacroAssembler::CompareObjectType(Register function,
Register map,
Register type_reg,
InstanceType type) {
ldr(map, FieldMemOperand(function, HeapObject::kMapOffset));
+ CompareInstanceType(map, type_reg, type);
+}
+
+
+void MacroAssembler::CompareInstanceType(Register map,
+ Register type_reg,
+ InstanceType type) {
ldrb(type_reg, FieldMemOperand(map, Map::kInstanceTypeOffset));
cmp(type_reg, Operand(type));
}
@@ -909,7 +1000,8 @@
void MacroAssembler::TailCallRuntime(const ExternalReference& ext,
- int num_arguments) {
+ int num_arguments,
+ int result_size) {
// TODO(1236192): Most runtime routines don't need the number of
// arguments passed in because it is constant. At some point we
// should remove this need and make the runtime routine entry code
@@ -925,7 +1017,7 @@
ASSERT((reinterpret_cast<intptr_t>(builtin.address()) & 1) == 1);
#endif
mov(r1, Operand(builtin));
- CEntryStub stub;
+ CEntryStub stub(1);
Jump(stub.GetCode(), RelocInfo::CODE_TARGET);
}
diff --git a/src/arm/macro-assembler-arm.h b/src/arm/macro-assembler-arm.h
index 4bbd980..f45cce5 100644
--- a/src/arm/macro-assembler-arm.h
+++ b/src/arm/macro-assembler-arm.h
@@ -1,4 +1,4 @@
-// Copyright 2006-2008 the V8 project authors. All rights reserved.
+// Copyright 2006-2009 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:
@@ -38,34 +38,11 @@
const Register cp = { 8 }; // JavaScript context pointer
-// Helper types to make boolean flag easier to read at call-site.
-enum InvokeFlag {
- CALL_FUNCTION,
- JUMP_FUNCTION
-};
-
enum InvokeJSFlags {
CALL_JS,
JUMP_JS
};
-enum ExitJSFlag {
- RETURN,
- DO_NOT_RETURN
-};
-
-enum CodeLocation {
- IN_JAVASCRIPT,
- IN_JS_ENTRY,
- IN_C_ENTRY
-};
-
-enum HandlerType {
- TRY_CATCH_HANDLER,
- TRY_FINALLY_HANDLER,
- JS_ENTRY_HANDLER
-};
-
// MacroAssembler implements a collection of frequently used macros.
class MacroAssembler: public Assembler {
@@ -190,16 +167,28 @@
// ---------------------------------------------------------------------------
// Allocation support
- // Allocate an object in new space. If the new space is exhausted control
- // continues at the gc_required label. The allocated object is returned in
- // result. If the flag tag_allocated_object is true the result is tagged as
- // as a heap object.
+ // Allocate an object in new space. The object_size is specified in words (not
+ // bytes). If the new space is exhausted control continues at the gc_required
+ // label. The allocated object is returned in result. If the flag
+ // tag_allocated_object is true the result is tagged as as a heap object.
void AllocateObjectInNewSpace(int object_size,
Register result,
Register scratch1,
Register scratch2,
Label* gc_required,
- bool tag_allocated_object);
+ AllocationFlags flags);
+ void AllocateObjectInNewSpace(Register object_size,
+ Register result,
+ Register scratch1,
+ Register scratch2,
+ Label* gc_required,
+ AllocationFlags flags);
+
+ // Undo allocation in new space. The object passed and objects allocated after
+ // it will no longer be allocated. The caller must make sure that no pointers
+ // are left to the object(s) no longer allocated as they would be invalid when
+ // allocation is undone.
+ void UndoAllocationInNewSpace(Register object, Register scratch);
// ---------------------------------------------------------------------------
// Support functions.
@@ -220,12 +209,21 @@
// It leaves the map in the map register (unless the type_reg and map register
// are the same register). It leaves the heap object in the heap_object
// register unless the heap_object register is the same register as one of the
- // other // registers.
+ // other registers.
void CompareObjectType(Register heap_object,
Register map,
Register type_reg,
InstanceType type);
+ // Compare instance type in a map. map contains a valid map object whose
+ // object type should be compared with the given type. This both
+ // sets the flags and leaves the object type in the type_reg register. It
+ // leaves the heap object in the heap_object register unless the heap_object
+ // register is the same register as type_reg.
+ void CompareInstanceType(Register map,
+ Register type_reg,
+ InstanceType type);
+
inline void BranchOnSmi(Register value, Label* smi_label) {
tst(value, Operand(kSmiTagMask));
b(eq, smi_label);
@@ -261,7 +259,9 @@
// Tail call of a runtime routine (jump).
// Like JumpToBuiltin, but also takes care of passing the number
// of parameters.
- void TailCallRuntime(const ExternalReference& ext, int num_arguments);
+ void TailCallRuntime(const ExternalReference& ext,
+ int num_arguments,
+ int result_size);
// Jump to the builtin routine.
void JumpToBuiltin(const ExternalReference& builtin);
diff --git a/src/arm/regexp-macro-assembler-arm.cc b/src/arm/regexp-macro-assembler-arm.cc
index 252d783..2e75a61 100644
--- a/src/arm/regexp-macro-assembler-arm.cc
+++ b/src/arm/regexp-macro-assembler-arm.cc
@@ -216,25 +216,29 @@
int cp_offset,
Label* on_failure,
bool check_end_of_string) {
- int byte_length = str.length() * char_size();
- int byte_offset = cp_offset * char_size();
- if (check_end_of_string) {
- // Check that there are at least str.length() characters left in the input.
- __ cmp(end_of_input_address(), Operand(-(byte_offset + byte_length)));
- BranchOrBacktrack(gt, on_failure);
- }
-
if (on_failure == NULL) {
- // Instead of inlining a backtrack, (re)use the global backtrack target.
+ // Instead of inlining a backtrack for each test, (re)use the global
+ // backtrack target.
on_failure = &backtrack_label_;
}
+ if (check_end_of_string) {
+ // Is last character of required match inside string.
+ CheckPosition(cp_offset + str.length() - 1, on_failure);
+ }
+
__ add(r0, end_of_input_address(), Operand(current_input_offset()));
+ if (cp_offset != 0) {
+ int byte_offset = cp_offset * char_size();
+ __ add(r0, r0, Operand(byte_offset));
+ }
+
+ // r0 : Address of characters to match against str.
int stored_high_byte = 0;
for (int i = 0; i < str.length(); i++) {
if (mode_ == ASCII) {
__ ldrb(r1, MemOperand(r0, char_size(), PostIndex));
- // str[i] is known to be an ASCII character.
+ ASSERT(str[i] <= String::kMaxAsciiCharCode);
__ cmp(r1, Operand(str[i]));
} else {
__ ldrh(r1, MemOperand(r0, char_size(), PostIndex));
diff --git a/src/arm/simulator-arm.cc b/src/arm/simulator-arm.cc
index e258e5a..70dfcd2 100644
--- a/src/arm/simulator-arm.cc
+++ b/src/arm/simulator-arm.cc
@@ -70,7 +70,8 @@
Simulator* sim_;
- bool GetValue(char* desc, int32_t* value);
+ int32_t GetRegisterValue(int regnum);
+ bool GetValue(const char* desc, int32_t* value);
// Set or delete a breakpoint. Returns true if successful.
bool SetBreakpoint(Instr* breakpc);
@@ -132,41 +133,19 @@
#endif
-static const char* reg_names[] = { "r0", "r1", "r2", "r3",
- "r4", "r5", "r6", "r7",
- "r8", "r9", "r10", "r11",
- "r12", "r13", "r14", "r15",
- "pc", "lr", "sp", "ip",
- "fp", "sl", ""};
-
-static int reg_nums[] = { 0, 1, 2, 3,
- 4, 5, 6, 7,
- 8, 9, 10, 11,
- 12, 13, 14, 15,
- 15, 14, 13, 12,
- 11, 10};
-
-
-static int RegNameToRegNum(char* name) {
- int reg = 0;
- while (*reg_names[reg] != 0) {
- if (strcmp(reg_names[reg], name) == 0) {
- return reg_nums[reg];
- }
- reg++;
+int32_t Debugger::GetRegisterValue(int regnum) {
+ if (regnum == kPCRegister) {
+ return sim_->get_pc();
+ } else {
+ return sim_->get_register(regnum);
}
- return -1;
}
-bool Debugger::GetValue(char* desc, int32_t* value) {
- int regnum = RegNameToRegNum(desc);
- if (regnum >= 0) {
- if (regnum == 15) {
- *value = sim_->get_pc();
- } else {
- *value = sim_->get_register(regnum);
- }
+bool Debugger::GetValue(const char* desc, int32_t* value) {
+ int regnum = Registers::Number(desc);
+ if (regnum != kNoRegister) {
+ *value = GetRegisterValue(regnum);
return true;
} else {
return SScanF(desc, "%i", value) == 1;
@@ -246,7 +225,7 @@
v8::internal::EmbeddedVector<char, 256> buffer;
dasm.InstructionDecode(buffer,
reinterpret_cast<byte*>(sim_->get_pc()));
- PrintF(" 0x%x %s\n", sim_->get_pc(), buffer.start());
+ PrintF(" 0x%08x %s\n", sim_->get_pc(), buffer.start());
last_pc = sim_->get_pc();
}
char* line = ReadLine("sim> ");
@@ -270,13 +249,20 @@
} else if ((strcmp(cmd, "p") == 0) || (strcmp(cmd, "print") == 0)) {
if (args == 2) {
int32_t value;
- if (GetValue(arg1, &value)) {
- PrintF("%s: %d 0x%x\n", arg1, value, value);
+ if (strcmp(arg1, "all") == 0) {
+ for (int i = 0; i < kNumRegisters; i++) {
+ value = GetRegisterValue(i);
+ PrintF("%3s: 0x%08x %10d\n", Registers::Name(i), value, value);
+ }
} else {
- PrintF("%s unrecognized\n", arg1);
+ if (GetValue(arg1, &value)) {
+ PrintF("%s: 0x%08x %d \n", arg1, value, value);
+ } else {
+ PrintF("%s unrecognized\n", arg1);
+ }
}
} else {
- PrintF("print value\n");
+ PrintF("print <register>\n");
}
} else if ((strcmp(cmd, "po") == 0)
|| (strcmp(cmd, "printobject") == 0)) {
@@ -284,16 +270,18 @@
int32_t value;
if (GetValue(arg1, &value)) {
Object* obj = reinterpret_cast<Object*>(value);
- USE(obj);
PrintF("%s: \n", arg1);
-#if defined(DEBUG)
+#ifdef DEBUG
obj->PrintLn();
-#endif // defined(DEBUG)
+#else
+ obj->ShortPrint();
+ PrintF("\n");
+#endif
} else {
PrintF("%s unrecognized\n", arg1);
}
} else {
- PrintF("printobject value\n");
+ PrintF("printobject <value>\n");
}
} else if (strcmp(cmd, "disasm") == 0) {
disasm::NameConverter converter;
@@ -325,7 +313,7 @@
while (cur < end) {
dasm.InstructionDecode(buffer, cur);
- PrintF(" 0x%x %s\n", cur, buffer.start());
+ PrintF(" 0x%08x %s\n", cur, buffer.start());
cur += Instr::kInstrSize;
}
} else if (strcmp(cmd, "gdb") == 0) {
@@ -343,7 +331,7 @@
PrintF("%s unrecognized\n", arg1);
}
} else {
- PrintF("break addr\n");
+ PrintF("break <address>\n");
}
} else if (strcmp(cmd, "del") == 0) {
if (!DeleteBreakpoint(NULL)) {
@@ -362,6 +350,30 @@
} else {
PrintF("Not at debugger stop.");
}
+ } else if ((strcmp(cmd, "h") == 0) || (strcmp(cmd, "help") == 0)) {
+ PrintF("cont\n");
+ PrintF(" continue execution (alias 'c')\n");
+ PrintF("stepi\n");
+ PrintF(" step one instruction (alias 'si')\n");
+ PrintF("print <register>\n");
+ PrintF(" print register content (alias 'p')\n");
+ PrintF(" use register name 'all' to print all registers\n");
+ PrintF("printobject <register>\n");
+ PrintF(" print an object from a register (alias 'po')\n");
+ PrintF("flags\n");
+ PrintF(" print flags\n");
+ PrintF("disasm [<instructions>]\n");
+ PrintF("disasm [[<address>] <instructions>]\n");
+ PrintF(" disassemble code, default is 10 instructions from pc\n");
+ PrintF("gdb\n");
+ PrintF(" enter gdb\n");
+ PrintF("break <address>\n");
+ PrintF(" set a break point on the address\n");
+ PrintF("del\n");
+ PrintF(" delete the breakpoint\n");
+ PrintF("unstop\n");
+ PrintF(" ignore the stop instruction at the current location");
+ PrintF(" from now on\n");
} else {
PrintF("Unknown command: %s\n", cmd);
}
@@ -576,7 +588,7 @@
intptr_t* ptr = reinterpret_cast<intptr_t*>(addr);
return *ptr;
}
- PrintF("Unaligned read at %x\n", addr);
+ PrintF("Unaligned read at 0x%08x\n", addr);
UNIMPLEMENTED();
return 0;
}
@@ -588,7 +600,7 @@
*ptr = value;
return;
}
- PrintF("Unaligned write at %x, pc=%p\n", addr, instr);
+ PrintF("Unaligned write at 0x%08x, pc=%p\n", addr, instr);
UNIMPLEMENTED();
}
@@ -598,7 +610,7 @@
uint16_t* ptr = reinterpret_cast<uint16_t*>(addr);
return *ptr;
}
- PrintF("Unaligned unsigned halfword read at %x, pc=%p\n", addr, instr);
+ PrintF("Unaligned unsigned halfword read at 0x%08x, pc=%p\n", addr, instr);
UNIMPLEMENTED();
return 0;
}
@@ -609,7 +621,7 @@
int16_t* ptr = reinterpret_cast<int16_t*>(addr);
return *ptr;
}
- PrintF("Unaligned signed halfword read at %x\n", addr);
+ PrintF("Unaligned signed halfword read at 0x%08x\n", addr);
UNIMPLEMENTED();
return 0;
}
@@ -621,7 +633,7 @@
*ptr = value;
return;
}
- PrintF("Unaligned unsigned halfword write at %x, pc=%p\n", addr, instr);
+ PrintF("Unaligned unsigned halfword write at 0x%08x, pc=%p\n", addr, instr);
UNIMPLEMENTED();
}
@@ -632,7 +644,7 @@
*ptr = value;
return;
}
- PrintF("Unaligned halfword write at %x, pc=%p\n", addr, instr);
+ PrintF("Unaligned halfword write at 0x%08x, pc=%p\n", addr, instr);
UNIMPLEMENTED();
}
@@ -671,7 +683,7 @@
// Unsupported instructions use Format to print an error and stop execution.
void Simulator::Format(Instr* instr, const char* format) {
- PrintF("Simulator found unsupported instruction:\n 0x%x: %s\n",
+ PrintF("Simulator found unsupported instruction:\n 0x%08x: %s\n",
instr, format);
UNIMPLEMENTED();
}
@@ -1726,7 +1738,8 @@
uint16_t halfword = ReadH(addr, instr);
set_register(rd, halfword);
} else {
- UNIMPLEMENTED();
+ Debugger dbg(this);
+ dbg.Stop(instr);
}
}
@@ -1741,7 +1754,7 @@
v8::internal::EmbeddedVector<char, 256> buffer;
dasm.InstructionDecode(buffer,
reinterpret_cast<byte*>(instr));
- PrintF(" 0x%x %s\n", instr, buffer.start());
+ PrintF(" 0x%08x %s\n", instr, buffer.start());
}
if (instr->ConditionField() == special_condition) {
DecodeUnconditional(instr);
diff --git a/src/arm/stub-cache-arm.cc b/src/arm/stub-cache-arm.cc
index 745b541..9e44cfa 100644
--- a/src/arm/stub-cache-arm.cc
+++ b/src/arm/stub-cache-arm.cc
@@ -478,7 +478,7 @@
// Do tail-call to the runtime system.
ExternalReference load_callback_property =
ExternalReference(IC_Utility(IC::kLoadCallbackProperty));
- __ TailCallRuntime(load_callback_property, 5);
+ __ TailCallRuntime(load_callback_property, 5, 1);
}
@@ -514,7 +514,7 @@
// Do tail-call to the runtime system.
ExternalReference load_ic_property =
ExternalReference(IC_Utility(IC::kLoadPropertyWithInterceptorForLoad));
- __ TailCallRuntime(load_ic_property, 5);
+ __ TailCallRuntime(load_ic_property, 5, 1);
}
@@ -884,7 +884,7 @@
// Do tail-call to the runtime system.
ExternalReference store_callback_property =
ExternalReference(IC_Utility(IC::kStoreCallbackProperty));
- __ TailCallRuntime(store_callback_property, 4);
+ __ TailCallRuntime(store_callback_property, 4, 1);
// Handle store cache miss.
__ bind(&miss);
@@ -936,7 +936,7 @@
// Do tail-call to the runtime system.
ExternalReference store_ic_property =
ExternalReference(IC_Utility(IC::kStoreInterceptorProperty));
- __ TailCallRuntime(store_ic_property, 3);
+ __ TailCallRuntime(store_ic_property, 3, 1);
// Handle store cache miss.
__ bind(&miss);
@@ -1344,7 +1344,138 @@
Object* ConstructStubCompiler::CompileConstructStub(
SharedFunctionInfo* shared) {
- // Not implemented yet - just jump to generic stub.
+ // ----------- S t a t e -------------
+ // -- r0 : argc
+ // -- r1 : constructor
+ // -- lr : return address
+ // -- [sp] : last argument
+ // -----------------------------------
+ Label generic_stub_call;
+
+ // Use r7 for holding undefined which is used in several places below.
+ __ LoadRoot(r7, Heap::kUndefinedValueRootIndex);
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ // Check to see whether there are any break points in the function code. If
+ // there are jump to the generic constructor stub which calls the actual
+ // code for the function thereby hitting the break points.
+ __ ldr(r2, FieldMemOperand(r1, JSFunction::kSharedFunctionInfoOffset));
+ __ ldr(r2, FieldMemOperand(r2, SharedFunctionInfo::kDebugInfoOffset));
+ __ cmp(r2, r7);
+ __ b(ne, &generic_stub_call);
+#endif
+
+ // Load the initial map and verify that it is in fact a map.
+ // r1: constructor function
+ // r7: undefined
+ __ ldr(r2, FieldMemOperand(r1, JSFunction::kPrototypeOrInitialMapOffset));
+ __ tst(r2, Operand(kSmiTagMask));
+ __ b(eq, &generic_stub_call);
+ __ CompareObjectType(r2, r3, r4, MAP_TYPE);
+ __ b(ne, &generic_stub_call);
+
+#ifdef DEBUG
+ // Cannot construct functions this way.
+ // r0: argc
+ // r1: constructor function
+ // r2: initial map
+ // r7: undefined
+ __ CompareInstanceType(r2, r3, JS_FUNCTION_TYPE);
+ __ Check(ne, "Function constructed by construct stub.");
+#endif
+
+ // Now allocate the JSObject in new space.
+ // r0: argc
+ // r1: constructor function
+ // r2: initial map
+ // r7: undefined
+ __ ldrb(r3, FieldMemOperand(r2, Map::kInstanceSizeOffset));
+ __ AllocateObjectInNewSpace(r3,
+ r4,
+ r5,
+ r6,
+ &generic_stub_call,
+ NO_ALLOCATION_FLAGS);
+
+ // Allocated the JSObject, now initialize the fields. Map is set to initial
+ // map and properties and elements are set to empty fixed array.
+ // r0: argc
+ // r1: constructor function
+ // r2: initial map
+ // r3: object size (in words)
+ // r4: JSObject (not tagged)
+ // r7: undefined
+ __ LoadRoot(r6, Heap::kEmptyFixedArrayRootIndex);
+ __ mov(r5, r4);
+ ASSERT_EQ(0 * kPointerSize, JSObject::kMapOffset);
+ __ str(r2, MemOperand(r5, kPointerSize, PostIndex));
+ ASSERT_EQ(1 * kPointerSize, JSObject::kPropertiesOffset);
+ __ str(r6, MemOperand(r5, kPointerSize, PostIndex));
+ ASSERT_EQ(2 * kPointerSize, JSObject::kElementsOffset);
+ __ str(r6, MemOperand(r5, kPointerSize, PostIndex));
+
+ // Calculate the location of the first argument. The stack contains only the
+ // argc arguments.
+ __ add(r1, sp, Operand(r0, LSL, kPointerSizeLog2));
+
+ // Fill all the in-object properties with undefined.
+ // r0: argc
+ // r1: first argument
+ // r3: object size (in words)
+ // r4: JSObject (not tagged)
+ // r5: First in-object property of JSObject (not tagged)
+ // r7: undefined
+ // Fill the initialized properties with a constant value or a passed argument
+ // depending on the this.x = ...; assignment in the function.
+ for (int i = 0; i < shared->this_property_assignments_count(); i++) {
+ if (shared->IsThisPropertyAssignmentArgument(i)) {
+ Label not_passed, next;
+ // Check if the argument assigned to the property is actually passed.
+ int arg_number = shared->GetThisPropertyAssignmentArgument(i);
+ __ cmp(r0, Operand(arg_number));
+ __ b(le, ¬_passed);
+ // Argument passed - find it on the stack.
+ __ ldr(r2, MemOperand(r1, (arg_number + 1) * -kPointerSize));
+ __ str(r2, MemOperand(r5, kPointerSize, PostIndex));
+ __ b(&next);
+ __ bind(¬_passed);
+ // Set the property to undefined.
+ __ str(r7, MemOperand(r5, kPointerSize, PostIndex));
+ __ bind(&next);
+ } else {
+ // Set the property to the constant value.
+ Handle<Object> constant(shared->GetThisPropertyAssignmentConstant(i));
+ __ mov(r2, Operand(constant));
+ __ str(r2, MemOperand(r5, kPointerSize, PostIndex));
+ }
+ }
+
+ // Fill the unused in-object property fields with undefined.
+ for (int i = shared->this_property_assignments_count();
+ i < shared->CalculateInObjectProperties();
+ i++) {
+ __ str(r7, MemOperand(r5, kPointerSize, PostIndex));
+ }
+
+ // r0: argc
+ // r4: JSObject (not tagged)
+ // Move argc to r1 and the JSObject to return to r0 and tag it.
+ __ mov(r1, r0);
+ __ mov(r0, r4);
+ __ orr(r0, r0, Operand(kHeapObjectTag));
+
+ // r0: JSObject
+ // r1: argc
+ // Remove caller arguments and receiver from the stack and return.
+ __ add(sp, sp, Operand(r1, LSL, kPointerSizeLog2));
+ __ add(sp, sp, Operand(kPointerSize));
+ __ IncrementCounter(&Counters::constructed_objects, 1, r1, r2);
+ __ IncrementCounter(&Counters::constructed_objects_stub, 1, r1, r2);
+ __ Jump(lr);
+
+ // Jump to the generic stub in case the specialized code cannot handle the
+ // construction.
+ __ bind(&generic_stub_call);
Code* code = Builtins::builtin(Builtins::JSConstructStubGeneric);
Handle<Code> generic_construct_stub(code);
__ Jump(generic_construct_stub, RelocInfo::CODE_TARGET);