From 16837c1f33bf221c254a48f21d862019a748eb9b Mon Sep 17 00:00:00 2001 From: "karlklose@chromium.org" Date: Tue, 21 Dec 2010 10:52:50 +0000 Subject: [PATCH] Implement inlining of instanceof tests on ARM. TBR=sgjesse BUG= TEST= Review URL: http://codereview.chromium.org/6004005 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@6093 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/arm/code-stubs-arm.cc | 103 +++++++++++++++++++-------------- src/arm/lithium-arm.cc | 3 +- src/arm/lithium-codegen-arm.cc | 9 ++- src/arm/macro-assembler-arm.cc | 38 ++++++++++++ src/arm/macro-assembler-arm.h | 13 +++++ 5 files changed, 121 insertions(+), 45 deletions(-) diff --git a/src/arm/code-stubs-arm.cc b/src/arm/code-stubs-arm.cc index 3670765a61..5ec8584f9e 100644 --- a/src/arm/code-stubs-arm.cc +++ b/src/arm/code-stubs-arm.cc @@ -2893,80 +2893,97 @@ void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) { } -// This stub performs an instanceof, calling the builtin function if -// necessary. Uses r1 for the object, r0 for the function that it may -// be an instance of (these are fetched from the stack). +// Uses registers r0 to r4. Expected input is +// function in r0 (or at sp+1*ptrsz) and object in +// r1 (or at sp), depending on whether or not +// args_in_registers() is true. void InstanceofStub::Generate(MacroAssembler* masm) { - // Get the object - slow case for smis (we may need to throw an exception - // depending on the rhs). - Label slow, loop, is_instance, is_not_instance; - __ ldr(r0, MemOperand(sp, 1 * kPointerSize)); - __ BranchOnSmi(r0, &slow); + // Fixed register usage throughout the stub: + const Register object = r1; // Object (lhs). + const Register map = r3; // Map of the object. + const Register function = r0; // Function (rhs). + const Register prototype = r4; // Prototype of the function. + const Register scratch = r2; + Label slow, loop, is_instance, is_not_instance, not_js_object; + if (!args_in_registers()) { + __ ldr(function, MemOperand(sp, 1 * kPointerSize)); + __ ldr(object, MemOperand(sp, 0)); + } - // Check that the left hand is a JS object and put map in r3. - __ CompareObjectType(r0, r3, r2, FIRST_JS_OBJECT_TYPE); - __ b(lt, &slow); - __ cmp(r2, Operand(LAST_JS_OBJECT_TYPE)); - __ b(gt, &slow); - - // Get the prototype of the function (r4 is result, r2 is scratch). - __ ldr(r1, MemOperand(sp, 0)); - // r1 is function, r3 is map. + // Check that the left hand is a JS object and load map. + __ BranchOnSmi(object, &slow); + __ IsObjectJSObjectType(object, map, scratch, &slow); // Look up the function and the map in the instanceof cache. Label miss; __ LoadRoot(ip, Heap::kInstanceofCacheFunctionRootIndex); - __ cmp(r1, ip); + __ cmp(object, ip); __ b(ne, &miss); __ LoadRoot(ip, Heap::kInstanceofCacheMapRootIndex); - __ cmp(r3, ip); + __ cmp(map, ip); __ b(ne, &miss); - __ LoadRoot(r0, Heap::kInstanceofCacheAnswerRootIndex); - __ pop(); - __ pop(); - __ mov(pc, Operand(lr)); + __ LoadRoot(function, Heap::kInstanceofCacheAnswerRootIndex); + __ Ret(args_in_registers() ? 0 : 2); __ bind(&miss); - __ TryGetFunctionPrototype(r1, r4, r2, &slow); + __ TryGetFunctionPrototype(object, prototype, scratch, &slow); // Check that the function prototype is a JS object. - __ BranchOnSmi(r4, &slow); - __ CompareObjectType(r4, r5, r5, FIRST_JS_OBJECT_TYPE); - __ b(lt, &slow); - __ cmp(r5, Operand(LAST_JS_OBJECT_TYPE)); - __ b(gt, &slow); + __ BranchOnSmi(prototype, &slow); + __ IsObjectJSObjectType(prototype, scratch, scratch, &slow); - __ StoreRoot(r1, Heap::kInstanceofCacheFunctionRootIndex); - __ StoreRoot(r3, Heap::kInstanceofCacheMapRootIndex); + __ StoreRoot(object, Heap::kInstanceofCacheFunctionRootIndex); + __ StoreRoot(map, Heap::kInstanceofCacheMapRootIndex); // Register mapping: r3 is object map and r4 is function prototype. // Get prototype of object into r2. - __ ldr(r2, FieldMemOperand(r3, Map::kPrototypeOffset)); + __ ldr(scratch, FieldMemOperand(map, Map::kPrototypeOffset)); // Loop through the prototype chain looking for the function prototype. __ bind(&loop); - __ cmp(r2, Operand(r4)); + __ cmp(scratch, Operand(prototype)); __ b(eq, &is_instance); __ LoadRoot(ip, Heap::kNullValueRootIndex); - __ cmp(r2, ip); + __ cmp(scratch, ip); __ b(eq, &is_not_instance); - __ ldr(r2, FieldMemOperand(r2, HeapObject::kMapOffset)); - __ ldr(r2, FieldMemOperand(r2, Map::kPrototypeOffset)); + __ ldr(scratch, FieldMemOperand(scratch, HeapObject::kMapOffset)); + __ ldr(scratch, FieldMemOperand(scratch, Map::kPrototypeOffset)); __ jmp(&loop); __ bind(&is_instance); __ mov(r0, Operand(Smi::FromInt(0))); __ StoreRoot(r0, Heap::kInstanceofCacheAnswerRootIndex); - __ pop(); - __ pop(); - __ mov(pc, Operand(lr)); // Return. + __ Ret(args_in_registers() ? 0 : 2); __ bind(&is_not_instance); __ mov(r0, Operand(Smi::FromInt(1))); - __ StoreRoot(r0, Heap::kInstanceofCacheAnswerRootIndex); - __ pop(); - __ pop(); - __ mov(pc, Operand(lr)); // Return. + __ Ret(args_in_registers() ? 0 : 2); + + Label object_not_null, object_not_null_or_smi; + __ bind(¬_js_object); + // Before null, smi and string value checks, check that the rhs is a function + // as for a non-function rhs an exception needs to be thrown. + __ BranchOnSmi(function, &slow); + __ CompareObjectType(function, map, scratch, JS_FUNCTION_TYPE); + __ b(ne, &slow); + + // Null is not instance of anything. + __ cmp(scratch, Operand(Factory::null_value())); + __ b(ne, &object_not_null); + __ mov(r0, Operand(Smi::FromInt(1))); + __ Ret(args_in_registers() ? 0 : 2); + + __ bind(&object_not_null); + // Smi values are not instances of anything. + __ BranchOnNotSmi(object, &object_not_null_or_smi); + __ mov(r0, Operand(Smi::FromInt(1))); + __ Ret(args_in_registers() ? 0 : 2); + + __ bind(&object_not_null_or_smi); + // String values are not instances of anything. + __ IsObjectJSStringType(object, scratch, &slow); + __ mov(r0, Operand(Smi::FromInt(1))); + __ Ret(args_in_registers() ? 0 : 2); // Slow-case. Tail call builtin. __ bind(&slow); diff --git a/src/arm/lithium-arm.cc b/src/arm/lithium-arm.cc index 82f188b856..e31d2e1d8a 100644 --- a/src/arm/lithium-arm.cc +++ b/src/arm/lithium-arm.cc @@ -1316,7 +1316,8 @@ LInstruction* LChunkBuilder::DoArgumentsElements(HArgumentsElements* elems) { LInstruction* LChunkBuilder::DoInstanceOf(HInstanceOf* instr) { LInstruction* result = - new LInstanceOf(Use(instr->left()), Use(instr->right())); + new LInstanceOf(UseFixed(instr->left(), r1), + UseFixed(instr->right(), r0)); return MarkAsCall(DefineFixed(result, r0), instr); } diff --git a/src/arm/lithium-codegen-arm.cc b/src/arm/lithium-codegen-arm.cc index 90899430c2..dfc48917da 100644 --- a/src/arm/lithium-codegen-arm.cc +++ b/src/arm/lithium-codegen-arm.cc @@ -1337,7 +1337,14 @@ void LCodeGen::DoCmpMapAndBranch(LCmpMapAndBranch* instr) { void LCodeGen::DoInstanceOf(LInstanceOf* instr) { - Abort("DoInstanceOf unimplemented."); + // We expect object and function in registers r1 and r0. + InstanceofStub stub(InstanceofStub::kArgsInRegisters); + CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr); + + Label true_value, done; + __ tst(r0, r0); + __ mov(r0, Operand(Factory::false_value()), LeaveCC, eq); + __ mov(r0, Operand(Factory::true_value()), LeaveCC, ne); } diff --git a/src/arm/macro-assembler-arm.cc b/src/arm/macro-assembler-arm.cc index 6effec1e31..4a13146590 100644 --- a/src/arm/macro-assembler-arm.cc +++ b/src/arm/macro-assembler-arm.cc @@ -178,6 +178,12 @@ void MacroAssembler::Drop(int count, Condition cond) { } +void MacroAssembler::Ret(int drop, Condition cond) { + Drop(drop, cond); + Ret(cond); +} + + void MacroAssembler::Swap(Register reg1, Register reg2, Register scratch, @@ -821,6 +827,38 @@ void MacroAssembler::InvokeFunction(JSFunction* function, } +void MacroAssembler::IsObjectJSObjectType(Register heap_object, + Register map, + Register scratch, + Label* fail) { + ldr(map, FieldMemOperand(heap_object, HeapObject::kMapOffset)); + IsInstanceJSObjectType(map, scratch, fail); +} + + +void MacroAssembler::IsInstanceJSObjectType(Register map, + Register scratch, + Label* fail) { + ldrb(scratch, FieldMemOperand(map, Map::kInstanceTypeOffset)); + cmp(scratch, Operand(FIRST_JS_OBJECT_TYPE)); + b(lt, fail); + cmp(scratch, Operand(LAST_JS_OBJECT_TYPE)); + b(gt, fail); +} + + +void MacroAssembler::IsObjectJSStringType(Register object, + Register scratch, + Label* fail) { + ASSERT(kNotStringTag != 0); + + ldr(scratch, FieldMemOperand(object, HeapObject::kMapOffset)); + ldrb(scratch, FieldMemOperand(scratch, Map::kInstanceTypeOffset)); + tst(scratch, Operand(kIsNotStringMask)); + b(nz, fail); +} + + #ifdef ENABLE_DEBUGGER_SUPPORT void MacroAssembler::DebugBreak() { ASSERT(allow_stub_calls()); diff --git a/src/arm/macro-assembler-arm.h b/src/arm/macro-assembler-arm.h index 3da8726777..97bbb2fb67 100644 --- a/src/arm/macro-assembler-arm.h +++ b/src/arm/macro-assembler-arm.h @@ -96,6 +96,7 @@ class MacroAssembler: public Assembler { // from the stack, clobbering only the sp register. void Drop(int count, Condition cond = al); + void Ret(int drop, Condition cond = al); // Swap two registers. If the scratch register is omitted then a slightly // less efficient form using xor instead of mov is emitted. @@ -298,6 +299,18 @@ class MacroAssembler: public Assembler { const ParameterCount& actual, InvokeFlag flag); + void IsObjectJSObjectType(Register heap_object, + Register map, + Register scratch, + Label* fail); + + void IsInstanceJSObjectType(Register map, + Register scratch, + Label* fail); + + void IsObjectJSStringType(Register object, + Register scratch, + Label* fail); #ifdef ENABLE_DEBUGGER_SUPPORT // ---------------------------------------------------------------------------