Index: src/arm64/code-stubs-arm64.cc |
diff --git a/src/arm64/code-stubs-arm64.cc b/src/arm64/code-stubs-arm64.cc |
index 716910ea91b3573e0d664a92552c43adb0141c4a..78383c3248349e610d6e36703402ff15f5f9860d 100644 |
--- a/src/arm64/code-stubs-arm64.cc |
+++ b/src/arm64/code-stubs-arm64.cc |
@@ -1505,191 +1505,107 @@ void LoadIndexedStringStub::Generate(MacroAssembler* masm) { |
} |
-void InstanceofStub::Generate(MacroAssembler* masm) { |
- // Stack on entry: |
- // jssp[0]: function. |
- // jssp[8]: object. |
- // |
- // Returns result in x0. Zero indicates instanceof, smi 1 indicates not |
- // instanceof. |
- |
- Register result = x0; |
- Register function = right(); |
- Register object = left(); |
- Register scratch1 = x6; |
- Register scratch2 = x7; |
- Register res_true = x8; |
- Register res_false = x9; |
- // Only used if there was an inline map check site. (See |
- // LCodeGen::DoInstanceOfKnownGlobal().) |
- Register map_check_site = x4; |
- // Delta for the instructions generated between the inline map check and the |
- // instruction setting the result. |
- const int32_t kDeltaToLoadBoolResult = 4 * kInstructionSize; |
- |
- Label not_js_object, slow; |
- |
- if (!HasArgsInRegisters()) { |
- __ Pop(function, object); |
- } |
- |
- if (ReturnTrueFalseObject()) { |
- __ LoadTrueFalseRoots(res_true, res_false); |
- } else { |
- // This is counter-intuitive, but correct. |
- __ Mov(res_true, Smi::FromInt(0)); |
- __ Mov(res_false, Smi::FromInt(1)); |
- } |
- |
- // Check that the left hand side is a JS object and load its map as a side |
- // effect. |
- Register map = x12; |
- __ JumpIfSmi(object, ¬_js_object); |
- __ IsObjectJSObjectType(object, map, scratch2, ¬_js_object); |
- |
- // If there is a call site cache, don't look in the global cache, but do the |
- // real lookup and update the call site cache. |
- if (!HasCallSiteInlineCheck() && !ReturnTrueFalseObject()) { |
- Label miss; |
- __ JumpIfNotRoot(function, Heap::kInstanceofCacheFunctionRootIndex, &miss); |
- __ JumpIfNotRoot(map, Heap::kInstanceofCacheMapRootIndex, &miss); |
- __ LoadRoot(result, Heap::kInstanceofCacheAnswerRootIndex); |
- __ Ret(); |
- __ Bind(&miss); |
- } |
- |
- // Get the prototype of the function. |
- Register prototype = x13; |
- __ TryGetFunctionPrototype(function, prototype, scratch2, &slow, |
- MacroAssembler::kMissOnBoundFunction); |
- |
- // Check that the function prototype is a JS object. |
- __ JumpIfSmi(prototype, &slow); |
- __ IsObjectJSObjectType(prototype, scratch1, scratch2, &slow); |
- |
- // Update the global instanceof or call site inlined cache with the current |
- // map and function. The cached answer will be set when it is known below. |
- if (HasCallSiteInlineCheck()) { |
- // Patch the (relocated) inlined map check. |
- __ GetRelocatedValueLocation(map_check_site, scratch1); |
- // We have a cell, so need another level of dereferencing. |
- __ Ldr(scratch1, MemOperand(scratch1)); |
- __ Str(map, FieldMemOperand(scratch1, Cell::kValueOffset)); |
- |
- __ Mov(x14, map); |
- // |scratch1| points at the beginning of the cell. Calculate the |
- // field containing the map. |
- __ Add(function, scratch1, Operand(Cell::kValueOffset - 1)); |
- __ RecordWriteField(scratch1, Cell::kValueOffset, x14, function, |
- kLRHasNotBeenSaved, kDontSaveFPRegs, |
- OMIT_REMEMBERED_SET, OMIT_SMI_CHECK); |
- } else { |
- __ StoreRoot(function, Heap::kInstanceofCacheFunctionRootIndex); |
- __ StoreRoot(map, Heap::kInstanceofCacheMapRootIndex); |
- } |
- |
- Label return_true, return_result; |
- Register smi_value = scratch1; |
- { |
- // Loop through the prototype chain looking for the function prototype. |
- Register chain_map = x1; |
- Register chain_prototype = x14; |
- Register null_value = x15; |
- Label loop; |
- __ Ldr(chain_prototype, FieldMemOperand(map, Map::kPrototypeOffset)); |
- __ LoadRoot(null_value, Heap::kNullValueRootIndex); |
- // Speculatively set a result. |
- __ Mov(result, res_false); |
- if (!HasCallSiteInlineCheck() && ReturnTrueFalseObject()) { |
- // Value to store in the cache cannot be an object. |
- __ Mov(smi_value, Smi::FromInt(1)); |
- } |
- |
- __ Bind(&loop); |
- |
- // If the chain prototype is the object prototype, return true. |
- __ Cmp(chain_prototype, prototype); |
- __ B(eq, &return_true); |
- |
- // If the chain prototype is null, we've reached the end of the chain, so |
- // return false. |
- __ Cmp(chain_prototype, null_value); |
- __ B(eq, &return_result); |
- |
- // Otherwise, load the next prototype in the chain, and loop. |
- __ Ldr(chain_map, FieldMemOperand(chain_prototype, HeapObject::kMapOffset)); |
- __ Ldr(chain_prototype, FieldMemOperand(chain_map, Map::kPrototypeOffset)); |
- __ B(&loop); |
- } |
- |
- // Return sequence when no arguments are on the stack. |
- // We cannot fall through to here. |
- __ Bind(&return_true); |
- __ Mov(result, res_true); |
- if (!HasCallSiteInlineCheck() && ReturnTrueFalseObject()) { |
- // Value to store in the cache cannot be an object. |
- __ Mov(smi_value, Smi::FromInt(0)); |
- } |
- __ Bind(&return_result); |
- if (HasCallSiteInlineCheck()) { |
- DCHECK(ReturnTrueFalseObject()); |
- __ Add(map_check_site, map_check_site, kDeltaToLoadBoolResult); |
- __ GetRelocatedValueLocation(map_check_site, scratch2); |
- __ Str(result, MemOperand(scratch2)); |
- } else { |
- Register cached_value = ReturnTrueFalseObject() ? smi_value : result; |
- __ StoreRoot(cached_value, Heap::kInstanceofCacheAnswerRootIndex); |
- } |
+void InstanceOfStub::Generate(MacroAssembler* masm) { |
+ Register const object = x1; // Object (lhs). |
+ Register const function = x0; // Function (rhs). |
+ Register const object_map = x2; // Map of {object}. |
+ Register const function_map = x3; // Map of {function}. |
+ Register const function_prototype = x4; // Prototype of {function}. |
+ Register const scratch = x5; |
+ |
+ DCHECK(object.is(InstanceOfDescriptor::LeftRegister())); |
+ DCHECK(function.is(InstanceOfDescriptor::RightRegister())); |
+ |
+ // Check if {object} is a smi. |
+ Label object_is_smi; |
+ __ JumpIfSmi(object, &object_is_smi); |
+ |
+ // Lookup the {function} and the {object} map in the global instanceof cache. |
+ // Note: This is safe because we clear the global instanceof cache whenever |
+ // we change the prototype of any object. |
+ Label fast_case, slow_case; |
+ __ Ldr(object_map, FieldMemOperand(object, HeapObject::kMapOffset)); |
+ __ JumpIfNotRoot(function, Heap::kInstanceofCacheFunctionRootIndex, |
+ &fast_case); |
+ __ JumpIfNotRoot(object_map, Heap::kInstanceofCacheMapRootIndex, &fast_case); |
+ __ LoadRoot(x0, Heap::kInstanceofCacheAnswerRootIndex); |
__ Ret(); |
- Label object_not_null, object_not_null_or_smi; |
- |
- __ Bind(¬_js_object); |
- Register object_type = x14; |
- // x0 result result return register (uninit) |
- // x10 function pointer to function |
- // x11 object pointer to object |
- // x14 object_type type of object (uninit) |
- |
- // Before null, smi and string checks, check that the rhs is a function. |
- // For a non-function rhs, an exception must be thrown. |
- __ JumpIfSmi(function, &slow); |
- __ JumpIfNotObjectType( |
- function, scratch1, object_type, JS_FUNCTION_TYPE, &slow); |
- |
- __ Mov(result, res_false); |
- |
- // Null is not instance of anything. |
- __ Cmp(object, Operand(isolate()->factory()->null_value())); |
- __ B(ne, &object_not_null); |
+ // If {object} is a smi we can safely return false if {function} is a JS |
+ // function, otherwise we have to miss to the runtime and throw an exception. |
+ __ Bind(&object_is_smi); |
+ __ JumpIfSmi(function, &slow_case); |
+ __ JumpIfNotObjectType(function, function_map, scratch, JS_FUNCTION_TYPE, |
+ &slow_case); |
+ __ LoadRoot(x0, Heap::kFalseValueRootIndex); |
__ Ret(); |
- __ Bind(&object_not_null); |
- // Smi values are not instances of anything. |
- __ JumpIfNotSmi(object, &object_not_null_or_smi); |
- __ Ret(); |
+ // Fast-case: The {function} must be a valid JSFunction. |
+ __ Bind(&fast_case); |
+ __ JumpIfSmi(function, &slow_case); |
+ __ JumpIfNotObjectType(function, function_map, scratch, JS_FUNCTION_TYPE, |
+ &slow_case); |
- __ Bind(&object_not_null_or_smi); |
- // String values are not instances of anything. |
- __ IsObjectJSStringType(object, scratch2, &slow); |
- __ Ret(); |
+ // Ensure that {function} has an instance prototype. |
+ __ Ldrb(scratch, FieldMemOperand(function_map, Map::kBitFieldOffset)); |
+ __ Tbnz(scratch, Map::kHasNonInstancePrototype, &slow_case); |
- // Slow-case. Tail call builtin. |
- __ Bind(&slow); |
- { |
- FrameScope scope(masm, StackFrame::INTERNAL); |
- // Arguments have either been passed into registers or have been previously |
- // popped. We need to push them before calling builtin. |
- __ Push(object, function); |
- __ InvokeBuiltin(Builtins::INSTANCE_OF, CALL_FUNCTION); |
- } |
- if (ReturnTrueFalseObject()) { |
- // Reload true/false because they were clobbered in the builtin call. |
- __ LoadTrueFalseRoots(res_true, res_false); |
- __ Cmp(result, 0); |
- __ Csel(result, res_true, res_false, eq); |
- } |
+ // Ensure that {function} is not bound. |
+ Register const shared_info = scratch; |
+ Register const scratch_w = scratch.W(); |
+ __ Ldr(shared_info, |
+ FieldMemOperand(function, JSFunction::kSharedFunctionInfoOffset)); |
+ // On 64-bit platforms, compiler hints field is not a smi. See definition of |
+ // kCompilerHintsOffset in src/objects.h. |
+ __ Ldr(scratch_w, FieldMemOperand(shared_info, |
+ SharedFunctionInfo::kCompilerHintsOffset)); |
+ __ Tbnz(scratch_w, SharedFunctionInfo::kBoundFunction, &slow_case); |
+ |
+ // Get the "prototype" (or initial map) of the {function}. |
+ __ Ldr(function_prototype, |
+ FieldMemOperand(function, JSFunction::kPrototypeOrInitialMapOffset)); |
+ __ AssertNotSmi(function_prototype); |
+ |
+ // Resolve the prototype if the {function} has an initial map. Afterwards the |
+ // {function_prototype} will be either the JSReceiver prototype object or the |
+ // hole value, which means that no instances of the {function} were created so |
+ // far and hence we should return false. |
+ Label function_prototype_valid; |
+ __ JumpIfNotObjectType(function_prototype, scratch, scratch, MAP_TYPE, |
+ &function_prototype_valid); |
+ __ Ldr(function_prototype, |
+ FieldMemOperand(function_prototype, Map::kPrototypeOffset)); |
+ __ Bind(&function_prototype_valid); |
+ __ AssertNotSmi(function_prototype); |
+ |
+ // Update the global instanceof cache with the current {object} map and |
+ // {function}. The cached answer will be set when it is known below. |
+ __ StoreRoot(function, Heap::kInstanceofCacheFunctionRootIndex); |
+ __ StoreRoot(object_map, Heap::kInstanceofCacheMapRootIndex); |
+ |
+ // Loop through the prototype chain looking for the {function} prototype. |
+ // Assume true, and change to false if not found. |
+ Register const object_prototype = object_map; |
+ Register const null = scratch; |
+ Label done, loop; |
+ __ LoadRoot(x0, Heap::kTrueValueRootIndex); |
+ __ LoadRoot(null, Heap::kNullValueRootIndex); |
+ __ Bind(&loop); |
+ __ Ldr(object_prototype, FieldMemOperand(object_map, Map::kPrototypeOffset)); |
+ __ Cmp(object_prototype, function_prototype); |
+ __ B(eq, &done); |
+ __ Cmp(object_prototype, null); |
+ __ Ldr(object_map, FieldMemOperand(object_prototype, HeapObject::kMapOffset)); |
+ __ B(ne, &loop); |
+ __ LoadRoot(x0, Heap::kFalseValueRootIndex); |
+ __ Bind(&done); |
+ __ StoreRoot(x0, Heap::kInstanceofCacheAnswerRootIndex); |
__ Ret(); |
+ |
+ // Slow-case: Call the runtime function. |
+ __ bind(&slow_case); |
+ __ Push(object, function); |
+ __ TailCallRuntime(Runtime::kInstanceOf, 2, 1); |
} |