First step in allocating objects in generated code on ARM
This change moves the allocation of new objects into generated code. The allocation will bail out into the runtime system if the number of properties to allocate for the object exceeds the number of in-object properties. Review URL: http://codereview.chromium.org/175045 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@2797 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
d8af3528dd
commit
00a2afeaa6
@ -88,23 +88,144 @@ void Builtins::Generate_JSConstructStubGeneric(MacroAssembler* masm) {
|
||||
// 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));
|
||||
// Make sure that the maximum heap object size will never cause us
|
||||
// problem here, because it is always greater than the maximum
|
||||
// instance size that can be represented in a byte.
|
||||
ASSERT(Heap::MaxObjectSizeInPagedSpace() >= JSObject::kMaxInstanceSize);
|
||||
__ AllocateObjectInNewSpace(r3, r4, r5, r6, &rt_call, false);
|
||||
// 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(12, 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
|
||||
// r2: initial map
|
||||
// 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.");
|
||||
|
||||
// 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.
|
||||
__ 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 +315,7 @@ void Builtins::Generate_JSConstructStubGeneric(MacroAssembler* masm) {
|
||||
__ LeaveConstructFrame();
|
||||
__ add(sp, sp, Operand(r1, LSL, kPointerSizeLog2 - 1));
|
||||
__ add(sp, sp, Operand(kPointerSize));
|
||||
__ IncrementCounter(&Counters::constructed_objects, 1, r1, r2);
|
||||
__ Jump(lr);
|
||||
}
|
||||
|
||||
|
@ -4950,7 +4950,7 @@ static void AllocateHeapNumber(
|
||||
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,
|
||||
|
@ -790,7 +790,7 @@ void MacroAssembler::AllocateObjectInNewSpace(int object_size,
|
||||
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);
|
||||
|
||||
@ -799,18 +799,84 @@ void MacroAssembler::AllocateObjectInNewSpace(int object_size,
|
||||
|
||||
// Tag and adjust back to start of new object.
|
||||
if (tag_allocated_object) {
|
||||
sub(result, result, Operand(object_size - kHeapObjectTag));
|
||||
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,
|
||||
bool tag_allocated_object) {
|
||||
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));
|
||||
ldr(result, MemOperand(scratch1));
|
||||
|
||||
// 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));
|
||||
|
||||
// Tag and adjust back to start of new object.
|
||||
if (tag_allocated_object) {
|
||||
sub(result, result, Operand(object_size, LSL, kPointerSizeLog2));
|
||||
add(result, result, Operand(kHeapObjectTag));
|
||||
} else {
|
||||
sub(result, result, Operand(object_size, LSL, kPointerSizeLog2));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
mov(scratch, Operand(new_space_allocation_top));
|
||||
ldr(scratch, MemOperand(scratch));
|
||||
cmp(object, scratch);
|
||||
Check(lt, "Undo allocation of non allocated memory");
|
||||
#endif
|
||||
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));
|
||||
}
|
||||
|
@ -190,16 +190,28 @@ class MacroAssembler: public Assembler {
|
||||
// ---------------------------------------------------------------------------
|
||||
// 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);
|
||||
void AllocateObjectInNewSpace(Register object_size,
|
||||
Register result,
|
||||
Register scratch1,
|
||||
Register scratch2,
|
||||
Label* gc_required,
|
||||
bool tag_allocated_object);
|
||||
|
||||
// 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 +232,21 @@ class MacroAssembler: public Assembler {
|
||||
// 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);
|
||||
|
@ -2891,8 +2891,12 @@ class Map: public HeapObject {
|
||||
|
||||
// Byte offsets within kInstanceSizesOffset.
|
||||
static const int kInstanceSizeOffset = kInstanceSizesOffset + 0;
|
||||
static const int kInObjectPropertiesOffset = kInstanceSizesOffset + 1;
|
||||
static const int kPreAllocatedPropertyFieldsOffset = kInstanceSizesOffset + 2;
|
||||
static const int kInObjectPropertiesByte = 1;
|
||||
static const int kInObjectPropertiesOffset =
|
||||
kInstanceSizesOffset + kInObjectPropertiesByte;
|
||||
static const int kPreAllocatedPropertyFieldsByte = 2;
|
||||
static const int kPreAllocatedPropertyFieldsOffset =
|
||||
kInstanceSizesOffset + kPreAllocatedPropertyFieldsByte;
|
||||
// The byte at position 3 is not in use at the moment.
|
||||
|
||||
// Byte offsets within kInstanceAttributesOffset attributes.
|
||||
|
Loading…
Reference in New Issue
Block a user