From 376537c12acde3355c70e3b3fb1410aed439866f Mon Sep 17 00:00:00 2001 From: "sgjesse@chromium.org" Date: Wed, 2 Sep 2009 11:13:44 +0000 Subject: [PATCH] First step in allocating objects in generated code on ARM. Re-apply http://codereview.chromium.org/175045 with the single change that the line mov(scratch, Operand(new_space_allocation_top)); have been added to MacroAssembler::UndoAllocationInNewSpace after the #endif. Without this change the top was not reset. TBR=erik.corry@gmail.com Review URL: http://codereview.chromium.org/184009 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@2815 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/arm/builtins-arm.cc | 137 ++++++++++++++++++++++++++++++++++++++--- src/arm/codegen-arm.cc | 2 +- src/arm/macro-assembler-arm.cc | 75 +++++++++++++++++++++- src/arm/macro-assembler-arm.h | 31 ++++++++-- src/objects.h | 8 ++- 5 files changed, 235 insertions(+), 18 deletions(-) diff --git a/src/arm/builtins-arm.cc b/src/arm/builtins-arm.cc index daf2378..1c7552f 100644 --- a/src/arm/builtins-arm.cc +++ b/src/arm/builtins-arm.cc @@ -88,23 +88,145 @@ 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. + + // 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(al, "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. + // 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 +316,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); } diff --git a/src/arm/codegen-arm.cc b/src/arm/codegen-arm.cc index b94aa10..cd7adfe 100644 --- a/src/arm/codegen-arm.cc +++ b/src/arm/codegen-arm.cc @@ -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, diff --git a/src/arm/macro-assembler-arm.cc b/src/arm/macro-assembler-arm.cc index 2ca4898..bc94a2f 100644 --- a/src/arm/macro-assembler-arm.cc +++ b/src/arm/macro-assembler-arm.cc @@ -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,87 @@ 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 + // 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)); } diff --git a/src/arm/macro-assembler-arm.h b/src/arm/macro-assembler-arm.h index 4bbd980..381f3b3 100644 --- a/src/arm/macro-assembler-arm.h +++ b/src/arm/macro-assembler-arm.h @@ -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); diff --git a/src/objects.h b/src/objects.h index 5c2488d..910c1d7 100644 --- a/src/objects.h +++ b/src/objects.h @@ -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. -- 2.7.4