From 6230f5397d372dfd3671602db312c69a93cf5444 Mon Sep 17 00:00:00 2001 From: "ager@chromium.org" Date: Tue, 4 May 2010 11:06:59 +0000 Subject: [PATCH] Port inline swapping of elements for the sort function in array.js from ia32 to arm. Original change: http://codereview.chromium.org/1709008 Review URL: http://codereview.chromium.org/1944001 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@4575 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/arm/codegen-arm.cc | 100 ++++++++++++++++++++++++++++++- src/arm/codegen-arm.h | 37 ++++++++++++ src/arm/macro-assembler-arm.cc | 61 +++++++++++++------ src/arm/macro-assembler-arm.h | 14 +++++ src/ia32/codegen-ia32.h | 4 +- src/ia32/macro-assembler-ia32.cc | 10 +++- src/ia32/macro-assembler-ia32.h | 4 +- src/x64/codegen-x64.h | 4 +- src/x64/macro-assembler-x64.cc | 10 +++- src/x64/macro-assembler-x64.h | 3 + 10 files changed, 220 insertions(+), 27 deletions(-) diff --git a/src/arm/codegen-arm.cc b/src/arm/codegen-arm.cc index 9ac79ceee..33c9eb821 100644 --- a/src/arm/codegen-arm.cc +++ b/src/arm/codegen-arm.cc @@ -4507,6 +4507,28 @@ void CodeGenerator::GenerateNumberToString(ZoneList* args) { } +class DeferredSwapElements: public DeferredCode { + public: + DeferredSwapElements(Register object, Register index1, Register index2) + : object_(object), index1_(index1), index2_(index2) { + set_comment("[ DeferredSwapElements"); + } + + virtual void Generate(); + + private: + Register object_, index1_, index2_; +}; + + +void DeferredSwapElements::Generate() { + __ push(object_); + __ push(index1_); + __ push(index2_); + __ CallRuntime(Runtime::kSwapElements, 3); +} + + void CodeGenerator::GenerateSwapElements(ZoneList* args) { Comment cmnt(masm_, "[ GenerateSwapElements"); @@ -4516,8 +4538,76 @@ void CodeGenerator::GenerateSwapElements(ZoneList* args) { Load(args->at(1)); Load(args->at(2)); - frame_->CallRuntime(Runtime::kSwapElements, 3); - frame_->EmitPush(r0); + Register index2 = r2; + Register index1 = r1; + Register object = r0; + Register tmp1 = r3; + Register tmp2 = r4; + + frame_->EmitPop(index2); + frame_->EmitPop(index1); + frame_->EmitPop(object); + + DeferredSwapElements* deferred = + new DeferredSwapElements(object, index1, index2); + + // Fetch the map and check if array is in fast case. + // Check that object doesn't require security checks and + // has no indexed interceptor. + __ CompareObjectType(object, tmp1, tmp2, FIRST_JS_OBJECT_TYPE); + deferred->Branch(lt); + __ ldrb(tmp2, FieldMemOperand(tmp1, Map::kBitFieldOffset)); + __ tst(tmp2, Operand(KeyedLoadIC::kSlowCaseBitFieldMask)); + deferred->Branch(nz); + + // Check the object's elements are in fast case. + __ ldr(tmp1, FieldMemOperand(object, JSObject::kElementsOffset)); + __ ldr(tmp2, FieldMemOperand(tmp1, HeapObject::kMapOffset)); + __ LoadRoot(ip, Heap::kFixedArrayMapRootIndex); + __ cmp(tmp2, ip); + deferred->Branch(ne); + + // Smi-tagging is equivalent to multiplying by 2. + STATIC_ASSERT(kSmiTag == 0); + STATIC_ASSERT(kSmiTagSize == 1); + + // Check that both indices are smis. + __ mov(tmp2, index1); + __ orr(tmp2, tmp2, index2); + __ tst(tmp2, Operand(kSmiTagMask)); + deferred->Branch(nz); + + // Bring the offsets into the fixed array in tmp1 into index1 and + // index2. + __ mov(tmp2, Operand(FixedArray::kHeaderSize - kHeapObjectTag)); + __ add(index1, tmp2, Operand(index1, LSL, kPointerSizeLog2 - kSmiTagSize)); + __ add(index2, tmp2, Operand(index2, LSL, kPointerSizeLog2 - kSmiTagSize)); + + // Swap elements. + Register tmp3 = object; + object = no_reg; + __ ldr(tmp3, MemOperand(tmp1, index1)); + __ ldr(tmp2, MemOperand(tmp1, index2)); + __ str(tmp3, MemOperand(tmp1, index2)); + __ str(tmp2, MemOperand(tmp1, index1)); + + Label done; + __ InNewSpace(tmp1, tmp2, eq, &done); + // Possible optimization: do a check that both values are Smis + // (or them and test against Smi mask.) + + __ mov(tmp2, tmp1); + RecordWriteStub recordWrite1(tmp1, index1, tmp3); + __ CallStub(&recordWrite1); + + RecordWriteStub recordWrite2(tmp2, index2, tmp3); + __ CallStub(&recordWrite2); + + __ bind(&done); + + deferred->BindExit(); + __ LoadRoot(tmp1, Heap::kUndefinedValueRootIndex); + frame_->EmitPush(tmp1); } @@ -6503,6 +6593,12 @@ void NumberToStringStub::Generate(MacroAssembler* masm) { } +void RecordWriteStub::Generate(MacroAssembler* masm) { + __ RecordWriteHelper(object_, offset_, scratch_); + __ Ret(); +} + + // On entry r0 (rhs) and r1 (lhs) are the values to be compared. // On exit r0 is 0, positive or negative to indicate the result of // the comparison. diff --git a/src/arm/codegen-arm.h b/src/arm/codegen-arm.h index 8b63923f9..bb76b633b 100644 --- a/src/arm/codegen-arm.h +++ b/src/arm/codegen-arm.h @@ -904,6 +904,43 @@ class NumberToStringStub: public CodeStub { }; +class RecordWriteStub : public CodeStub { + public: + RecordWriteStub(Register object, Register offset, Register scratch) + : object_(object), offset_(offset), scratch_(scratch) { } + + void Generate(MacroAssembler* masm); + + private: + Register object_; + Register offset_; + Register scratch_; + +#ifdef DEBUG + void Print() { + PrintF("RecordWriteStub (object reg %d), (offset reg %d)," + " (scratch reg %d)\n", + object_.code(), offset_.code(), scratch_.code()); + } +#endif + + // Minor key encoding in 12 bits. 4 bits for each of the three + // registers (object, offset and scratch) OOOOAAAASSSS. + class ScratchBits: public BitField {}; + class OffsetBits: public BitField {}; + class ObjectBits: public BitField {}; + + Major MajorKey() { return RecordWrite; } + + int MinorKey() { + // Encode the registers. + return ObjectBits::encode(object_.code()) | + OffsetBits::encode(offset_.code()) | + ScratchBits::encode(scratch_.code()); + } +}; + + } } // namespace v8::internal #endif // V8_ARM_CODEGEN_ARM_H_ diff --git a/src/arm/macro-assembler-arm.cc b/src/arm/macro-assembler-arm.cc index c20c77ee2..935e7918b 100644 --- a/src/arm/macro-assembler-arm.cc +++ b/src/arm/macro-assembler-arm.cc @@ -232,30 +232,23 @@ void MacroAssembler::LoadRoot(Register destination, } -// Will clobber 4 registers: object, offset, scratch, ip. The -// register 'object' contains a heap object pointer. The heap object -// tag is shifted away. -void MacroAssembler::RecordWrite(Register object, Register offset, - Register scratch) { - // The compiled code assumes that record write doesn't change the - // context register, so we check that none of the clobbered - // registers are cp. - ASSERT(!object.is(cp) && !offset.is(cp) && !scratch.is(cp)); +void MacroAssembler::RecordWriteHelper(Register object, + Register offset, + Register scratch) { + if (FLAG_debug_code) { + // Check that the object is not in new space. + Label not_in_new_space; + InNewSpace(object, scratch, ne, ¬_in_new_space); + Abort("new-space object passed to RecordWriteHelper"); + bind(¬_in_new_space); + } // This is how much we shift the remembered set bit offset to get the // offset of the word in the remembered set. We divide by kBitsPerInt (32, // shift right 5) and then multiply by kIntSize (4, shift left 2). const int kRSetWordShift = 3; - Label fast, done; - - // First, test that the object is not in the new space. We cannot set - // remembered set bits in the new space. - // object: heap object pointer (with tag) - // offset: offset to store location from the object - and_(scratch, object, Operand(ExternalReference::new_space_mask())); - cmp(scratch, Operand(ExternalReference::new_space_start())); - b(eq, &done); + Label fast; // Compute the bit offset in the remembered set. // object: heap object pointer (with tag) @@ -307,6 +300,38 @@ void MacroAssembler::RecordWrite(Register object, Register offset, mov(ip, Operand(1)); orr(scratch, scratch, Operand(ip, LSL, offset)); str(scratch, MemOperand(object)); +} + + +void MacroAssembler::InNewSpace(Register object, + Register scratch, + Condition cc, + Label* branch) { + ASSERT(cc == eq || cc == ne); + and_(scratch, object, Operand(ExternalReference::new_space_mask())); + cmp(scratch, Operand(ExternalReference::new_space_start())); + b(cc, branch); +} + + +// Will clobber 4 registers: object, offset, scratch, ip. The +// register 'object' contains a heap object pointer. The heap object +// tag is shifted away. +void MacroAssembler::RecordWrite(Register object, Register offset, + Register scratch) { + // The compiled code assumes that record write doesn't change the + // context register, so we check that none of the clobbered + // registers are cp. + ASSERT(!object.is(cp) && !offset.is(cp) && !scratch.is(cp)); + + Label done; + + // First, test that the object is not in the new space. We cannot set + // remembered set bits in the new space. + InNewSpace(object, scratch, eq, &done); + + // Record the actual write. + RecordWriteHelper(object, offset, scratch); bind(&done); diff --git a/src/arm/macro-assembler-arm.h b/src/arm/macro-assembler-arm.h index 1c0464816..f252ae942 100644 --- a/src/arm/macro-assembler-arm.h +++ b/src/arm/macro-assembler-arm.h @@ -86,6 +86,20 @@ class MacroAssembler: public Assembler { Heap::RootListIndex index, Condition cond = al); + + // Check if object is in new space. + // scratch can be object itself, but it will be clobbered. + void InNewSpace(Register object, + Register scratch, + Condition cc, // eq for new space, ne otherwise + Label* branch); + + + // Set the remebered set bit for an offset into an + // object. RecordWriteHelper only works if the object is not in new + // space. + void RecordWriteHelper(Register object, Register offset, Register scracth); + // Sets the remembered set bit for [address+offset], where address is the // address of the heap object 'object'. The address must be in the first 8K // of an allocated page. The 'scratch' register is used in the diff --git a/src/ia32/codegen-ia32.h b/src/ia32/codegen-ia32.h index 2bbae9255..0d3fee592 100644 --- a/src/ia32/codegen-ia32.h +++ b/src/ia32/codegen-ia32.h @@ -1082,8 +1082,8 @@ class RecordWriteStub : public CodeStub { } #endif - // Minor key encoding in 12 bits of three registers (object, address and - // scratch) OOOOAAAASSSS. + // Minor key encoding in 12 bits. 4 bits for each of the three + // registers (object, address and scratch) OOOOAAAASSSS. class ScratchBits: public BitField {}; class AddressBits: public BitField {}; class ObjectBits: public BitField {}; diff --git a/src/ia32/macro-assembler-ia32.cc b/src/ia32/macro-assembler-ia32.cc index c44cbbfa3..0deec8f26 100644 --- a/src/ia32/macro-assembler-ia32.cc +++ b/src/ia32/macro-assembler-ia32.cc @@ -50,6 +50,14 @@ MacroAssembler::MacroAssembler(void* buffer, int size) void MacroAssembler::RecordWriteHelper(Register object, Register addr, Register scratch) { + if (FLAG_debug_code) { + // Check that the object is not in new space. + Label not_in_new_space; + InNewSpace(object, scratch, not_equal, ¬_in_new_space); + Abort("new-space object passed to RecordWriteHelper"); + bind(¬_in_new_space); + } + Label fast; // Compute the page start address from the heap object pointer, and reuse @@ -134,7 +142,7 @@ void MacroAssembler::RecordWrite(Register object, int offset, // First, check if a remembered set write is even needed. The tests below // catch stores of Smis and stores into young gen (which does not have space - // for the remembered set bits. + // for the remembered set bits). Label done; // Skip barrier if writing a smi. diff --git a/src/ia32/macro-assembler-ia32.h b/src/ia32/macro-assembler-ia32.h index b11a6977e..c3a019ba4 100644 --- a/src/ia32/macro-assembler-ia32.h +++ b/src/ia32/macro-assembler-ia32.h @@ -48,7 +48,9 @@ class MacroAssembler: public Assembler { // --------------------------------------------------------------------------- // GC Support - + // Set the remebered set bit for an address which points into an + // object. RecordWriteHelper only works if the object is not in new + // space. void RecordWriteHelper(Register object, Register addr, Register scratch); diff --git a/src/x64/codegen-x64.h b/src/x64/codegen-x64.h index b67369fcf..c85d6a199 100644 --- a/src/x64/codegen-x64.h +++ b/src/x64/codegen-x64.h @@ -1033,8 +1033,8 @@ class RecordWriteStub : public CodeStub { } #endif - // Minor key encoding in 12 bits of three registers (object, address and - // scratch) OOOOAAAASSSS. + // Minor key encoding in 12 bits. 4 bits for each of the three + // registers (object, address and scratch) OOOOAAAASSSS. class ScratchBits : public BitField {}; class AddressBits : public BitField {}; class ObjectBits : public BitField {}; diff --git a/src/x64/macro-assembler-x64.cc b/src/x64/macro-assembler-x64.cc index fdb850b08..e085792c1 100644 --- a/src/x64/macro-assembler-x64.cc +++ b/src/x64/macro-assembler-x64.cc @@ -75,6 +75,14 @@ void MacroAssembler::StackLimitCheck(Label* on_stack_overflow) { void MacroAssembler::RecordWriteHelper(Register object, Register addr, Register scratch) { + if (FLAG_debug_code) { + // Check that the object is not in new space. + Label not_in_new_space; + InNewSpace(object, scratch, not_equal, ¬_in_new_space); + Abort("new-space object passed to RecordWriteHelper"); + bind(¬_in_new_space); + } + Label fast; // Compute the page start address from the heap object pointer, and reuse @@ -157,7 +165,7 @@ void MacroAssembler::RecordWrite(Register object, // First, check if a remembered set write is even needed. The tests below // catch stores of Smis and stores into young gen (which does not have space - // for the remembered set bits. + // for the remembered set bits). Label done; JumpIfSmi(value, &done); diff --git a/src/x64/macro-assembler-x64.h b/src/x64/macro-assembler-x64.h index a21637ebd..e2a4f326f 100644 --- a/src/x64/macro-assembler-x64.h +++ b/src/x64/macro-assembler-x64.h @@ -66,6 +66,9 @@ class MacroAssembler: public Assembler { // --------------------------------------------------------------------------- // GC Support + // Set the remebered set bit for an address which points into an + // object. RecordWriteHelper only works if the object is not in new + // space. void RecordWriteHelper(Register object, Register addr, Register scratch); -- 2.34.1