}
-// Uses registers r0 to r4.
-// Expected input (depending on whether args are in registers or on the stack):
-// * object: r0 or at sp + 1 * kPointerSize.
-// * function: r1 or at sp.
-//
-// An inlined call site may have been generated before calling this stub.
-// In this case the offset to the inline sites to patch are passed in r5 and r6.
-// (See LCodeGen::DoInstanceOfKnownGlobal)
-void InstanceofStub::Generate(MacroAssembler* masm) {
- // Call site inlining and patching implies arguments in registers.
- DCHECK(HasArgsInRegisters() || !HasCallSiteInlineCheck());
-
- // Fixed register usage throughout the stub:
- const Register object = r0; // Object (lhs).
- Register map = r3; // Map of the object.
- const Register function = r1; // 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 (!HasArgsInRegisters()) {
- __ ldr(object, MemOperand(sp, 1 * kPointerSize));
- __ ldr(function, MemOperand(sp, 0));
- }
-
- // Check that the left hand is a JS object and load map.
- __ JumpIfSmi(object, ¬_js_object);
- __ IsObjectJSObjectType(object, map, scratch, ¬_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;
- __ CompareRoot(function, Heap::kInstanceofCacheFunctionRootIndex);
- __ b(ne, &miss);
- __ CompareRoot(map, Heap::kInstanceofCacheMapRootIndex);
- __ b(ne, &miss);
- __ LoadRoot(r0, Heap::kInstanceofCacheAnswerRootIndex);
- __ Ret(HasArgsInRegisters() ? 0 : 2);
-
- __ bind(&miss);
- }
-
- // Get the prototype of the function.
- __ TryGetFunctionPrototype(function, prototype, scratch, &slow, true);
+void InstanceOfStub::Generate(MacroAssembler* masm) {
+ Register const object = r1; // Object (lhs).
+ Register const function = r0; // Function (rhs).
+ Register const object_map = r2; // Map of {object}.
+ Register const function_map = r3; // Map of {function}.
+ Register const function_prototype = r4; // Prototype of {function}.
+ Register const scratch = r5;
+
+ 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));
+ __ CompareRoot(function, Heap::kInstanceofCacheFunctionRootIndex);
+ __ b(ne, &fast_case);
+ __ CompareRoot(object_map, Heap::kInstanceofCacheMapRootIndex);
+ __ b(ne, &fast_case);
+ __ LoadRoot(r0, Heap::kInstanceofCacheAnswerRootIndex);
+ __ Ret();
- // Check that the function prototype is a JS object.
- __ JumpIfSmi(prototype, &slow);
- __ IsObjectJSObjectType(prototype, scratch, scratch, &slow);
+ // 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);
+ __ CompareObjectType(function, function_map, scratch, JS_FUNCTION_TYPE);
+ __ b(ne, &slow_case);
+ __ LoadRoot(r0, Heap::kFalseValueRootIndex);
+ __ Ret();
- // 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()) {
- __ StoreRoot(function, Heap::kInstanceofCacheFunctionRootIndex);
- __ StoreRoot(map, Heap::kInstanceofCacheMapRootIndex);
- } else {
- DCHECK(HasArgsInRegisters());
- // Patch the (relocated) inlined map check.
-
- // The map_load_offset was stored in r5
- // (See LCodeGen::DoDeferredLInstanceOfKnownGlobal).
- const Register map_load_offset = r5;
- __ sub(r9, lr, map_load_offset);
- // Get the map location in r5 and patch it.
- __ GetRelocatedValueLocation(r9, map_load_offset, scratch);
- __ ldr(map_load_offset, MemOperand(map_load_offset));
- __ str(map, FieldMemOperand(map_load_offset, Cell::kValueOffset));
-
- __ mov(scratch, map);
- // |map_load_offset| points at the beginning of the cell. Calculate the
- // field containing the map.
- __ add(function, map_load_offset, Operand(Cell::kValueOffset - 1));
- __ RecordWriteField(map_load_offset, Cell::kValueOffset, scratch, function,
- kLRHasNotBeenSaved, kDontSaveFPRegs,
- OMIT_REMEMBERED_SET, OMIT_SMI_CHECK);
- }
+ // Fast-case: The {function} must be a valid JSFunction.
+ __ bind(&fast_case);
+ __ JumpIfSmi(function, &slow_case);
+ __ CompareObjectType(function, function_map, scratch, JS_FUNCTION_TYPE);
+ __ b(ne, &slow_case);
- // Register mapping: r3 is object map and r4 is function prototype.
- // Get prototype of object into r2.
- __ ldr(scratch, FieldMemOperand(map, Map::kPrototypeOffset));
+ // Ensure that {function} has an instance prototype.
+ __ ldrb(scratch, FieldMemOperand(function_map, Map::kBitFieldOffset));
+ __ tst(scratch, Operand(1 << Map::kHasNonInstancePrototype));
+ __ b(ne, &slow_case);
- // We don't need map any more. Use it as a scratch register.
- Register scratch2 = map;
- map = no_reg;
+ // Ensure that {function} is not bound.
+ Register const shared_info = scratch;
+ __ ldr(shared_info,
+ FieldMemOperand(function, JSFunction::kSharedFunctionInfoOffset));
+ __ ldr(scratch, FieldMemOperand(shared_info,
+ SharedFunctionInfo::kCompilerHintsOffset));
+ __ tst(scratch,
+ Operand(Smi::FromInt(1 << SharedFunctionInfo::kBoundFunction)));
+ __ b(ne, &slow_case);
- // Loop through the prototype chain looking for the function prototype.
- __ LoadRoot(scratch2, Heap::kNullValueRootIndex);
+ // 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;
+ __ CompareObjectType(function_prototype, scratch, scratch, MAP_TYPE);
+ __ b(ne, &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(r0, Heap::kTrueValueRootIndex);
+ __ LoadRoot(null, Heap::kNullValueRootIndex);
__ bind(&loop);
- __ cmp(scratch, Operand(prototype));
- __ b(eq, &is_instance);
- __ cmp(scratch, scratch2);
- __ b(eq, &is_not_instance);
- __ ldr(scratch, FieldMemOperand(scratch, HeapObject::kMapOffset));
- __ ldr(scratch, FieldMemOperand(scratch, Map::kPrototypeOffset));
- __ jmp(&loop);
- Factory* factory = isolate()->factory();
-
- __ bind(&is_instance);
- if (!HasCallSiteInlineCheck()) {
- __ mov(r0, Operand(Smi::FromInt(0)));
- __ StoreRoot(r0, Heap::kInstanceofCacheAnswerRootIndex);
- if (ReturnTrueFalseObject()) {
- __ Move(r0, factory->true_value());
- }
- } else {
- // Patch the call site to return true.
- __ LoadRoot(r0, Heap::kTrueValueRootIndex);
- // The bool_load_offset was stored in r6
- // (See LCodeGen::DoDeferredLInstanceOfKnownGlobal).
- const Register bool_load_offset = r6;
- __ sub(r9, lr, bool_load_offset);
- // Get the boolean result location in scratch and patch it.
- __ GetRelocatedValueLocation(r9, scratch, scratch2);
- __ str(r0, MemOperand(scratch));
-
- if (!ReturnTrueFalseObject()) {
- __ mov(r0, Operand(Smi::FromInt(0)));
- }
- }
- __ Ret(HasArgsInRegisters() ? 0 : 2);
-
- __ bind(&is_not_instance);
- if (!HasCallSiteInlineCheck()) {
- __ mov(r0, Operand(Smi::FromInt(1)));
- __ StoreRoot(r0, Heap::kInstanceofCacheAnswerRootIndex);
- if (ReturnTrueFalseObject()) {
- __ Move(r0, factory->false_value());
- }
- } else {
- // Patch the call site to return false.
- __ LoadRoot(r0, Heap::kFalseValueRootIndex);
- // The bool_load_offset was stored in r6
- // (See LCodeGen::DoDeferredLInstanceOfKnownGlobal).
- const Register bool_load_offset = r6;
- __ sub(r9, lr, bool_load_offset);
- ;
- // Get the boolean result location in scratch and patch it.
- __ GetRelocatedValueLocation(r9, scratch, scratch2);
- __ str(r0, MemOperand(scratch));
-
- if (!ReturnTrueFalseObject()) {
- __ mov(r0, Operand(Smi::FromInt(1)));
- }
- }
- __ Ret(HasArgsInRegisters() ? 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.
- __ JumpIfSmi(function, &slow);
- __ CompareObjectType(function, scratch2, scratch, JS_FUNCTION_TYPE);
- __ b(ne, &slow);
-
- // Null is not instance of anything.
- __ cmp(object, Operand(isolate()->factory()->null_value()));
- __ b(ne, &object_not_null);
- if (ReturnTrueFalseObject()) {
- __ Move(r0, factory->false_value());
- } else {
- __ mov(r0, Operand(Smi::FromInt(1)));
- }
- __ Ret(HasArgsInRegisters() ? 0 : 2);
-
- __ bind(&object_not_null);
- // Smi values are not instances of anything.
- __ JumpIfNotSmi(object, &object_not_null_or_smi);
- if (ReturnTrueFalseObject()) {
- __ Move(r0, factory->false_value());
- } else {
- __ mov(r0, Operand(Smi::FromInt(1)));
- }
- __ Ret(HasArgsInRegisters() ? 0 : 2);
-
- __ bind(&object_not_null_or_smi);
- // String values are not instances of anything.
- __ IsObjectJSStringType(object, scratch, &slow);
- if (ReturnTrueFalseObject()) {
- __ Move(r0, factory->false_value());
- } else {
- __ mov(r0, Operand(Smi::FromInt(1)));
- }
- __ Ret(HasArgsInRegisters() ? 0 : 2);
+ __ 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(r0, Heap::kFalseValueRootIndex);
+ __ bind(&done);
+ __ StoreRoot(r0, Heap::kInstanceofCacheAnswerRootIndex);
+ __ Ret();
- // Slow-case. Tail call builtin.
- __ bind(&slow);
- if (!ReturnTrueFalseObject()) {
- if (HasArgsInRegisters()) {
- __ Push(r0, r1);
- }
- __ InvokeBuiltin(Builtins::INSTANCE_OF, JUMP_FUNCTION);
- } else {
- {
- FrameAndConstantPoolScope scope(masm, StackFrame::INTERNAL);
- __ Push(r0, r1);
- __ InvokeBuiltin(Builtins::INSTANCE_OF, CALL_FUNCTION);
- }
- __ cmp(r0, Operand::Zero());
- __ LoadRoot(r0, Heap::kTrueValueRootIndex, eq);
- __ LoadRoot(r0, Heap::kFalseValueRootIndex, ne);
- __ Ret(HasArgsInRegisters() ? 0 : 2);
- }
+ // Slow-case: Call the runtime function.
+ __ bind(&slow_case);
+ __ Push(object, function);
+ __ TailCallRuntime(Runtime::kInstanceOf, 2, 1);
}
const Register StoreGlobalViaContextDescriptor::ValueRegister() { return r0; }
-const Register InstanceofDescriptor::left() { return r0; }
-const Register InstanceofDescriptor::right() { return r1; }
+const Register InstanceOfDescriptor::LeftRegister() { return r1; }
+const Register InstanceOfDescriptor::RightRegister() { return r0; }
const Register ArgumentsAccessReadDescriptor::index() { return r1; }
if (instr->IsCall()) {
HValue* hydrogen_value_for_lazy_bailout = hydrogen_val;
- LInstruction* instruction_needing_environment = NULL;
if (hydrogen_val->HasObservableSideEffects()) {
HSimulate* sim = HSimulate::cast(hydrogen_val->next());
- instruction_needing_environment = instr;
sim->ReplayEnvironment(current_block_->last_environment());
hydrogen_value_for_lazy_bailout = sim;
}
LInstruction* bailout = AssignEnvironment(new(zone()) LLazyBailout());
bailout->set_hydrogen_value(hydrogen_value_for_lazy_bailout);
chunk_->AddInstruction(bailout, current_block_);
- if (instruction_needing_environment != NULL) {
- // Store the lazy deopt environment with the instruction if needed.
- // Right now it is only used for LInstanceOfKnownGlobal.
- instruction_needing_environment->
- SetDeferredLazyDeoptimizationEnvironment(bailout->environment());
- }
}
}
LInstruction* LChunkBuilder::DoInstanceOf(HInstanceOf* instr) {
+ LOperand* left =
+ UseFixed(instr->left(), InstanceOfDescriptor::LeftRegister());
+ LOperand* right =
+ UseFixed(instr->right(), InstanceOfDescriptor::RightRegister());
LOperand* context = UseFixed(instr->context(), cp);
- LInstanceOf* result =
- new(zone()) LInstanceOf(context, UseFixed(instr->left(), r0),
- UseFixed(instr->right(), r1));
+ LInstanceOf* result = new (zone()) LInstanceOf(context, left, right);
return MarkAsCall(DefineFixed(result, r0), instr);
}
-LInstruction* LChunkBuilder::DoInstanceOfKnownGlobal(
- HInstanceOfKnownGlobal* instr) {
- LInstanceOfKnownGlobal* result =
- new(zone()) LInstanceOfKnownGlobal(
- UseFixed(instr->context(), cp),
- UseFixed(instr->left(), r0),
- FixedTemp(r4));
- return MarkAsCall(DefineFixed(result, r0), instr);
+LInstruction* LChunkBuilder::DoHasInPrototypeChainAndBranch(
+ HHasInPrototypeChainAndBranch* instr) {
+ LOperand* object = UseRegister(instr->object());
+ LOperand* prototype = UseRegister(instr->prototype());
+ return new (zone()) LHasInPrototypeChainAndBranch(object, prototype);
}
V(GetCachedArrayIndex) \
V(Goto) \
V(HasCachedArrayIndexAndBranch) \
+ V(HasInPrototypeChainAndBranch) \
V(HasInstanceTypeAndBranch) \
V(InnerAllocatedObject) \
V(InstanceOf) \
- V(InstanceOfKnownGlobal) \
V(InstructionGap) \
V(Integer32ToDouble) \
V(InvokeFunction) \
void set_hydrogen_value(HValue* value) { hydrogen_value_ = value; }
HValue* hydrogen_value() const { return hydrogen_value_; }
- virtual void SetDeferredLazyDeoptimizationEnvironment(LEnvironment* env) { }
-
void MarkAsCall() { bit_field_ = IsCallBits::update(bit_field_, true); }
bool IsCall() const { return IsCallBits::decode(bit_field_); }
inputs_[2] = right;
}
- LOperand* context() { return inputs_[0]; }
- LOperand* left() { return inputs_[1]; }
- LOperand* right() { return inputs_[2]; }
+ LOperand* context() const { return inputs_[0]; }
+ LOperand* left() const { return inputs_[1]; }
+ LOperand* right() const { return inputs_[2]; }
DECLARE_CONCRETE_INSTRUCTION(InstanceOf, "instance-of")
};
-class LInstanceOfKnownGlobal final : public LTemplateInstruction<1, 2, 1> {
+class LHasInPrototypeChainAndBranch final : public LControlInstruction<2, 0> {
public:
- LInstanceOfKnownGlobal(LOperand* context, LOperand* value, LOperand* temp) {
- inputs_[0] = context;
- inputs_[1] = value;
- temps_[0] = temp;
+ LHasInPrototypeChainAndBranch(LOperand* object, LOperand* prototype) {
+ inputs_[0] = object;
+ inputs_[1] = prototype;
}
- LOperand* context() { return inputs_[0]; }
- LOperand* value() { return inputs_[1]; }
- LOperand* temp() { return temps_[0]; }
-
- DECLARE_CONCRETE_INSTRUCTION(InstanceOfKnownGlobal,
- "instance-of-known-global")
- DECLARE_HYDROGEN_ACCESSOR(InstanceOfKnownGlobal)
+ LOperand* object() const { return inputs_[0]; }
+ LOperand* prototype() const { return inputs_[1]; }
- Handle<JSFunction> function() const { return hydrogen()->function(); }
- LEnvironment* GetDeferredLazyDeoptimizationEnvironment() {
- return lazy_deopt_env_;
- }
- virtual void SetDeferredLazyDeoptimizationEnvironment(
- LEnvironment* env) override {
- lazy_deopt_env_ = env;
- }
-
- private:
- LEnvironment* lazy_deopt_env_;
+ DECLARE_CONCRETE_INSTRUCTION(HasInPrototypeChainAndBranch,
+ "has-in-prototype-chain-and-branch")
+ DECLARE_HYDROGEN_ACCESSOR(HasInPrototypeChainAndBranch)
};
}
-template<class InstrType>
+template <class InstrType>
+void LCodeGen::EmitTrueBranch(InstrType instr, Condition condition) {
+ int true_block = instr->TrueDestination(chunk_);
+ __ b(condition, chunk_->GetAssemblyLabel(true_block));
+}
+
+
+template <class InstrType>
void LCodeGen::EmitFalseBranch(InstrType instr, Condition condition) {
int false_block = instr->FalseDestination(chunk_);
__ b(condition, chunk_->GetAssemblyLabel(false_block));
void LCodeGen::DoInstanceOf(LInstanceOf* instr) {
DCHECK(ToRegister(instr->context()).is(cp));
- DCHECK(ToRegister(instr->left()).is(r0)); // Object is in r0.
- DCHECK(ToRegister(instr->right()).is(r1)); // Function is in r1.
-
- InstanceofStub stub(isolate(), InstanceofStub::kArgsInRegisters);
+ DCHECK(ToRegister(instr->left()).is(InstanceOfDescriptor::LeftRegister()));
+ DCHECK(ToRegister(instr->right()).is(InstanceOfDescriptor::RightRegister()));
+ DCHECK(ToRegister(instr->result()).is(r0));
+ InstanceOfStub stub(isolate());
CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
-
- __ cmp(r0, Operand::Zero());
- __ mov(r0, Operand(factory()->false_value()), LeaveCC, ne);
- __ mov(r0, Operand(factory()->true_value()), LeaveCC, eq);
}
-void LCodeGen::DoInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr) {
- class DeferredInstanceOfKnownGlobal final : public LDeferredCode {
- public:
- DeferredInstanceOfKnownGlobal(LCodeGen* codegen,
- LInstanceOfKnownGlobal* instr)
- : LDeferredCode(codegen), instr_(instr) { }
- void Generate() override {
- codegen()->DoDeferredInstanceOfKnownGlobal(instr_, &map_check_,
- &load_bool_);
- }
- LInstruction* instr() override { return instr_; }
- Label* map_check() { return &map_check_; }
- Label* load_bool() { return &load_bool_; }
-
- private:
- LInstanceOfKnownGlobal* instr_;
- Label map_check_;
- Label load_bool_;
- };
-
- DeferredInstanceOfKnownGlobal* deferred;
- deferred = new(zone()) DeferredInstanceOfKnownGlobal(this, instr);
-
- Label done, false_result;
- Register object = ToRegister(instr->value());
- Register temp = ToRegister(instr->temp());
- Register result = ToRegister(instr->result());
+void LCodeGen::DoHasInPrototypeChainAndBranch(
+ LHasInPrototypeChainAndBranch* instr) {
+ Register const object = ToRegister(instr->object());
+ Register const object_map = scratch0();
+ Register const object_prototype = object_map;
+ Register const prototype = ToRegister(instr->prototype());
- // A Smi is not instance of anything.
- __ JumpIfSmi(object, &false_result);
-
- // This is the inlined call site instanceof cache. The two occurences of the
- // hole value will be patched to the last map/result pair generated by the
- // instanceof stub.
- Label cache_miss;
- Register map = temp;
- __ ldr(map, FieldMemOperand(object, HeapObject::kMapOffset));
- {
- // Block constant pool emission to ensure the positions of instructions are
- // as expected by the patcher. See InstanceofStub::Generate().
- Assembler::BlockConstPoolScope block_const_pool(masm());
- __ bind(deferred->map_check()); // Label for calculating code patching.
- // We use Factory::the_hole_value() on purpose instead of loading from the
- // root array to force relocation to be able to later patch with
- // the cached map.
- Handle<Cell> cell = factory()->NewCell(factory()->the_hole_value());
- __ mov(ip, Operand(cell));
- __ ldr(ip, FieldMemOperand(ip, Cell::kValueOffset));
- __ cmp(map, Operand(ip));
- __ b(ne, &cache_miss);
- __ bind(deferred->load_bool()); // Label for calculating code patching.
- // We use Factory::the_hole_value() on purpose instead of loading from the
- // root array to force relocation to be able to later patch
- // with true or false.
- __ mov(result, Operand(factory()->the_hole_value()));
+ // The {object} must be a spec object. It's sufficient to know that {object}
+ // is not a smi, since all other non-spec objects have {null} prototypes and
+ // will be ruled out below.
+ if (instr->hydrogen()->ObjectNeedsSmiCheck()) {
+ __ SmiTst(object);
+ EmitFalseBranch(instr, eq);
}
- __ b(&done);
-
- // The inlined call site cache did not match. Check null and string before
- // calling the deferred code.
- __ bind(&cache_miss);
- // Null is not instance of anything.
- __ LoadRoot(ip, Heap::kNullValueRootIndex);
- __ cmp(object, Operand(ip));
- __ b(eq, &false_result);
-
- // String values is not instance of anything.
- Condition is_string = masm_->IsObjectStringType(object, temp);
- __ b(is_string, &false_result);
-
- // Go to the deferred code.
- __ b(deferred->entry());
-
- __ bind(&false_result);
- __ LoadRoot(result, Heap::kFalseValueRootIndex);
-
- // Here result has either true or false. Deferred code also produces true or
- // false object.
- __ bind(deferred->exit());
- __ bind(&done);
-}
-
-
-void LCodeGen::DoDeferredInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr,
- Label* map_check,
- Label* bool_load) {
- InstanceofStub::Flags flags = InstanceofStub::kNoFlags;
- flags = static_cast<InstanceofStub::Flags>(
- flags | InstanceofStub::kArgsInRegisters);
- flags = static_cast<InstanceofStub::Flags>(
- flags | InstanceofStub::kCallSiteInlineCheck);
- flags = static_cast<InstanceofStub::Flags>(
- flags | InstanceofStub::kReturnTrueFalseObject);
- InstanceofStub stub(isolate(), flags);
- PushSafepointRegistersScope scope(this);
- LoadContextFromDeferred(instr->context());
-
- __ Move(InstanceofStub::right(), instr->function());
-
- int call_size = CallCodeSize(stub.GetCode(), RelocInfo::CODE_TARGET);
- int additional_delta = (call_size / Assembler::kInstrSize) + 4;
- {
- // Make sure that code size is predicable, since we use specific constants
- // offsets in the code to find embedded values..
- PredictableCodeSizeScope predictable(
- masm_, additional_delta * Assembler::kInstrSize);
- // The labels must be already bound since the code has predictabel size up
- // to the call instruction.
- DCHECK(map_check->is_bound());
- DCHECK(bool_load->is_bound());
- // Make sure we don't emit any additional entries in the constant pool
- // before the call to ensure that the CallCodeSize() calculated the
- // correct number of instructions for the constant pool load.
- {
- ConstantPoolUnavailableScope constant_pool_unavailable(masm_);
- int map_check_delta =
- masm_->InstructionsGeneratedSince(map_check) + additional_delta;
- int bool_load_delta =
- masm_->InstructionsGeneratedSince(bool_load) + additional_delta;
- Label before_push_delta;
- __ bind(&before_push_delta);
- __ BlockConstPoolFor(additional_delta);
- // r5 is used to communicate the offset to the location of the map check.
- __ mov(r5, Operand(map_check_delta * kPointerSize));
- // r6 is used to communicate the offset to the location of the bool load.
- __ mov(r6, Operand(bool_load_delta * kPointerSize));
- // The mov above can generate one or two instructions. The delta was
- // computed for two instructions, so we need to pad here in case of one
- // instruction.
- while (masm_->InstructionsGeneratedSince(&before_push_delta) != 4) {
- __ nop();
- }
- }
- CallCodeGeneric(stub.GetCode(), RelocInfo::CODE_TARGET, instr,
- RECORD_SAFEPOINT_WITH_REGISTERS_AND_NO_ARGUMENTS);
- }
- LEnvironment* env = instr->GetDeferredLazyDeoptimizationEnvironment();
- safepoints_.RecordLazyDeoptimizationIndex(env->deoptimization_index());
- // Put the result value (r0) into the result register slot and
- // restore all registers.
- __ StoreToSafepointRegisterSlot(r0, ToRegister(instr->result()));
+ // Loop through the {object}s prototype chain looking for the {prototype}.
+ __ ldr(object_map, FieldMemOperand(object, HeapObject::kMapOffset));
+ Label loop;
+ __ bind(&loop);
+ __ ldr(object_prototype, FieldMemOperand(object_map, Map::kPrototypeOffset));
+ __ cmp(object_prototype, prototype);
+ EmitTrueBranch(instr, eq);
+ __ CompareRoot(object_prototype, Heap::kNullValueRootIndex);
+ EmitFalseBranch(instr, eq);
+ __ ldr(object_map, FieldMemOperand(object_prototype, HeapObject::kMapOffset));
+ __ b(&loop);
}
void DoDeferredStringCharCodeAt(LStringCharCodeAt* instr);
void DoDeferredStringCharFromCode(LStringCharFromCode* instr);
void DoDeferredAllocate(LAllocate* instr);
- void DoDeferredInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr,
- Label* map_check, Label* bool_load);
void DoDeferredInstanceMigration(LCheckMaps* instr, Register object);
void DoDeferredLoadMutableDouble(LLoadFieldByIndex* instr,
Register result,
// EmitBranch expects to be the last instruction of a block.
template<class InstrType>
void EmitBranch(InstrType instr, Condition condition);
- template<class InstrType>
+ template <class InstrType>
+ void EmitTrueBranch(InstrType instr, Condition condition);
+ template <class InstrType>
void EmitFalseBranch(InstrType instr, Condition condition);
void EmitNumberUntagD(LNumberUntagD* instr, Register input,
DwVfpRegister result, NumberUntagDMode mode);
}
-void MacroAssembler::TryGetFunctionPrototype(Register function,
- Register result,
- Register scratch,
- Label* miss,
- bool miss_on_bound_function) {
- Label non_instance;
- if (miss_on_bound_function) {
- // Check that the receiver isn't a smi.
- JumpIfSmi(function, miss);
-
- // Check that the function really is a function. Load map into result reg.
- CompareObjectType(function, result, scratch, JS_FUNCTION_TYPE);
- b(ne, miss);
-
- ldr(scratch,
- FieldMemOperand(function, JSFunction::kSharedFunctionInfoOffset));
- ldr(scratch,
- FieldMemOperand(scratch, SharedFunctionInfo::kCompilerHintsOffset));
- tst(scratch,
- Operand(Smi::FromInt(1 << SharedFunctionInfo::kBoundFunction)));
- b(ne, miss);
-
- // Make sure that the function has an instance prototype.
- ldrb(scratch, FieldMemOperand(result, Map::kBitFieldOffset));
- tst(scratch, Operand(1 << Map::kHasNonInstancePrototype));
- b(ne, &non_instance);
- }
-
+void MacroAssembler::TryGetFunctionPrototype(Register function, Register result,
+ Register scratch, Label* miss) {
// Get the prototype or initial map from the function.
ldr(result,
FieldMemOperand(function, JSFunction::kPrototypeOrInitialMapOffset));
// Get the prototype from the initial map.
ldr(result, FieldMemOperand(result, Map::kPrototypeOffset));
- if (miss_on_bound_function) {
- jmp(&done);
-
- // Non-instance prototype: Fetch prototype from constructor field
- // in initial map.
- bind(&non_instance);
- GetMapConstructor(result, result, scratch, ip);
- }
-
// All done.
bind(&done);
}
}
-void MacroAssembler::GetRelocatedValueLocation(Register ldr_location,
- Register result,
- Register scratch) {
- Label small_constant_pool_load, load_result;
- ldr(result, MemOperand(ldr_location));
-
- if (FLAG_enable_embedded_constant_pool) {
- // Check if this is an extended constant pool load.
- and_(scratch, result, Operand(GetConsantPoolLoadMask()));
- teq(scratch, Operand(GetConsantPoolLoadPattern()));
- b(eq, &small_constant_pool_load);
- if (emit_debug_code()) {
- // Check that the instruction sequence is:
- // movw reg, #offset_low
- // movt reg, #offset_high
- // ldr reg, [pp, reg]
- Instr patterns[] = {GetMovWPattern(), GetMovTPattern(),
- GetLdrPpRegOffsetPattern()};
- for (int i = 0; i < 3; i++) {
- ldr(result, MemOperand(ldr_location, i * kInstrSize));
- and_(result, result, Operand(patterns[i]));
- cmp(result, Operand(patterns[i]));
- Check(eq, kTheInstructionToPatchShouldBeALoadFromConstantPool);
- }
- // Result was clobbered. Restore it.
- ldr(result, MemOperand(ldr_location));
- }
-
- // Get the offset into the constant pool. First extract movw immediate into
- // result.
- and_(scratch, result, Operand(0xfff));
- mov(ip, Operand(result, LSR, 4));
- and_(ip, ip, Operand(0xf000));
- orr(result, scratch, Operand(ip));
- // Then extract movt immediate and or into result.
- ldr(scratch, MemOperand(ldr_location, kInstrSize));
- and_(ip, scratch, Operand(0xf0000));
- orr(result, result, Operand(ip, LSL, 12));
- and_(scratch, scratch, Operand(0xfff));
- orr(result, result, Operand(scratch, LSL, 16));
-
- b(&load_result);
- }
-
- bind(&small_constant_pool_load);
- if (emit_debug_code()) {
- // Check that the instruction is a ldr reg, [<pc or pp> + offset] .
- and_(result, result, Operand(GetConsantPoolLoadPattern()));
- cmp(result, Operand(GetConsantPoolLoadPattern()));
- Check(eq, kTheInstructionToPatchShouldBeALoadFromConstantPool);
- // Result was clobbered. Restore it.
- ldr(result, MemOperand(ldr_location));
- }
-
- // Get the offset into the constant pool.
- const uint32_t kLdrOffsetMask = (1 << 12) - 1;
- and_(result, result, Operand(kLdrOffsetMask));
-
- bind(&load_result);
- // Get the address of the constant.
- if (FLAG_enable_embedded_constant_pool) {
- add(result, pp, Operand(result));
- } else {
- add(result, ldr_location, Operand(result));
- add(result, result, Operand(Instruction::kPCReadOffset));
- }
-}
-
-
void MacroAssembler::CheckPageFlag(
Register object,
Register scratch,
// function and jumps to the miss label if the fast checks fail. The
// function register will be untouched; the other registers may be
// clobbered.
- void TryGetFunctionPrototype(Register function,
- Register result,
- Register scratch,
- Label* miss,
- bool miss_on_bound_function = false);
+ void TryGetFunctionPrototype(Register function, Register result,
+ Register scratch, Label* miss);
// Compare object type for heap object. heap_object contains a non-Smi
// whose object type should be compared with the given type. This both
Register value,
uint32_t encoding_mask);
- // ---------------------------------------------------------------------------
- // Patching helpers.
-
- // Get the location of a relocated constant (its address in the constant pool)
- // from its load site.
- void GetRelocatedValueLocation(Register ldr_location, Register result,
- Register scratch);
-
void ClampUint8(Register output_reg, Register input_reg);
}
-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);
}
const Register StoreGlobalViaContextDescriptor::ValueRegister() { return x0; }
-const Register InstanceofDescriptor::left() {
- // Object to check (instanceof lhs).
- return x11;
-}
-
-
-const Register InstanceofDescriptor::right() {
- // Constructor function (instanceof rhs).
- return x10;
-}
+const Register InstanceOfDescriptor::LeftRegister() { return x1; }
+const Register InstanceOfDescriptor::RightRegister() { return x0; }
const Register ArgumentsAccessReadDescriptor::index() { return x1; }
if (instr->IsCall()) {
HValue* hydrogen_value_for_lazy_bailout = hydrogen_val;
- LInstruction* instruction_needing_environment = NULL;
if (hydrogen_val->HasObservableSideEffects()) {
HSimulate* sim = HSimulate::cast(hydrogen_val->next());
- instruction_needing_environment = instr;
sim->ReplayEnvironment(current_block_->last_environment());
hydrogen_value_for_lazy_bailout = sim;
}
LInstruction* bailout = AssignEnvironment(new(zone()) LLazyBailout());
bailout->set_hydrogen_value(hydrogen_value_for_lazy_bailout);
chunk_->AddInstruction(bailout, current_block_);
- if (instruction_needing_environment != NULL) {
- // Store the lazy deopt environment with the instruction if needed.
- // Right now it is only used for LInstanceOfKnownGlobal.
- instruction_needing_environment->
- SetDeferredLazyDeoptimizationEnvironment(bailout->environment());
- }
}
}
LInstruction* LChunkBuilder::DoInstanceOf(HInstanceOf* instr) {
+ LOperand* left =
+ UseFixed(instr->left(), InstanceOfDescriptor::LeftRegister());
+ LOperand* right =
+ UseFixed(instr->right(), InstanceOfDescriptor::RightRegister());
LOperand* context = UseFixed(instr->context(), cp);
- LInstanceOf* result = new(zone()) LInstanceOf(
- context,
- UseFixed(instr->left(), InstanceofStub::left()),
- UseFixed(instr->right(), InstanceofStub::right()));
+ LInstanceOf* result = new (zone()) LInstanceOf(context, left, right);
return MarkAsCall(DefineFixed(result, x0), instr);
}
-LInstruction* LChunkBuilder::DoInstanceOfKnownGlobal(
- HInstanceOfKnownGlobal* instr) {
- LInstanceOfKnownGlobal* result = new(zone()) LInstanceOfKnownGlobal(
- UseFixed(instr->context(), cp),
- UseFixed(instr->left(), InstanceofStub::left()));
- return MarkAsCall(DefineFixed(result, x0), instr);
+LInstruction* LChunkBuilder::DoHasInPrototypeChainAndBranch(
+ HHasInPrototypeChainAndBranch* instr) {
+ LOperand* object = UseRegister(instr->object());
+ LOperand* prototype = UseRegister(instr->prototype());
+ LOperand* scratch = TempRegister();
+ return new (zone()) LHasInPrototypeChainAndBranch(object, prototype, scratch);
}
V(GetCachedArrayIndex) \
V(Goto) \
V(HasCachedArrayIndexAndBranch) \
+ V(HasInPrototypeChainAndBranch) \
V(HasInstanceTypeAndBranch) \
V(InnerAllocatedObject) \
V(InstanceOf) \
- V(InstanceOfKnownGlobal) \
V(InstructionGap) \
V(Integer32ToDouble) \
V(InvokeFunction) \
void set_hydrogen_value(HValue* value) { hydrogen_value_ = value; }
HValue* hydrogen_value() const { return hydrogen_value_; }
- virtual void SetDeferredLazyDeoptimizationEnvironment(LEnvironment* env) { }
-
void MarkAsCall() { bit_field_ = IsCallBits::update(bit_field_, true); }
bool IsCall() const { return IsCallBits::decode(bit_field_); }
inputs_[2] = right;
}
- LOperand* context() { return inputs_[0]; }
- LOperand* left() { return inputs_[1]; }
- LOperand* right() { return inputs_[2]; }
+ LOperand* context() const { return inputs_[0]; }
+ LOperand* left() const { return inputs_[1]; }
+ LOperand* right() const { return inputs_[2]; }
DECLARE_CONCRETE_INSTRUCTION(InstanceOf, "instance-of")
};
-class LInstanceOfKnownGlobal final : public LTemplateInstruction<1, 2, 0> {
+class LHasInPrototypeChainAndBranch final : public LControlInstruction<2, 1> {
public:
- LInstanceOfKnownGlobal(LOperand* context, LOperand* value) {
- inputs_[0] = context;
- inputs_[1] = value;
+ LHasInPrototypeChainAndBranch(LOperand* object, LOperand* prototype,
+ LOperand* scratch) {
+ inputs_[0] = object;
+ inputs_[1] = prototype;
+ temps_[0] = scratch;
}
- LOperand* context() { return inputs_[0]; }
- LOperand* value() { return inputs_[1]; }
+ LOperand* object() const { return inputs_[0]; }
+ LOperand* prototype() const { return inputs_[1]; }
+ LOperand* scratch() const { return temps_[0]; }
- DECLARE_CONCRETE_INSTRUCTION(InstanceOfKnownGlobal,
- "instance-of-known-global")
- DECLARE_HYDROGEN_ACCESSOR(InstanceOfKnownGlobal)
-
- Handle<JSFunction> function() const { return hydrogen()->function(); }
- LEnvironment* GetDeferredLazyDeoptimizationEnvironment() {
- return lazy_deopt_env_;
- }
- virtual void SetDeferredLazyDeoptimizationEnvironment(
- LEnvironment* env) override {
- lazy_deopt_env_ = env;
- }
-
- private:
- LEnvironment* lazy_deopt_env_;
+ DECLARE_CONCRETE_INSTRUCTION(HasInPrototypeChainAndBranch,
+ "has-in-prototype-chain-and-branch")
+ DECLARE_HYDROGEN_ACCESSOR(HasInPrototypeChainAndBranch)
};
void LCodeGen::DoInstanceOf(LInstanceOf* instr) {
DCHECK(ToRegister(instr->context()).is(cp));
- // Assert that the arguments are in the registers expected by InstanceofStub.
- DCHECK(ToRegister(instr->left()).Is(InstanceofStub::left()));
- DCHECK(ToRegister(instr->right()).Is(InstanceofStub::right()));
-
- InstanceofStub stub(isolate(), InstanceofStub::kArgsInRegisters);
+ DCHECK(ToRegister(instr->left()).is(InstanceOfDescriptor::LeftRegister()));
+ DCHECK(ToRegister(instr->right()).is(InstanceOfDescriptor::RightRegister()));
+ DCHECK(ToRegister(instr->result()).is(x0));
+ InstanceOfStub stub(isolate());
CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
-
- // InstanceofStub returns a result in x0:
- // 0 => not an instance
- // smi 1 => instance.
- __ Cmp(x0, 0);
- __ LoadTrueFalseRoots(x0, x1);
- __ Csel(x0, x0, x1, eq);
}
-void LCodeGen::DoInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr) {
- class DeferredInstanceOfKnownGlobal: public LDeferredCode {
- public:
- DeferredInstanceOfKnownGlobal(LCodeGen* codegen,
- LInstanceOfKnownGlobal* instr)
- : LDeferredCode(codegen), instr_(instr) { }
- virtual void Generate() {
- codegen()->DoDeferredInstanceOfKnownGlobal(instr_);
- }
- virtual LInstruction* instr() { return instr_; }
- private:
- LInstanceOfKnownGlobal* instr_;
- };
-
- DeferredInstanceOfKnownGlobal* deferred =
- new(zone()) DeferredInstanceOfKnownGlobal(this, instr);
-
- Label map_check, return_false, cache_miss, done;
- Register object = ToRegister(instr->value());
- Register result = ToRegister(instr->result());
- // x4 is expected in the associated deferred code and stub.
- Register map_check_site = x4;
- Register map = x5;
-
- // This instruction is marked as call. We can clobber any register.
- DCHECK(instr->IsMarkedAsCall());
-
- // We must take into account that object is in x11.
- DCHECK(object.Is(x11));
- Register scratch = x10;
-
- // A Smi is not instance of anything.
- __ JumpIfSmi(object, &return_false);
+void LCodeGen::DoHasInPrototypeChainAndBranch(
+ LHasInPrototypeChainAndBranch* instr) {
+ Register const object = ToRegister(instr->object());
+ Register const object_map = ToRegister(instr->scratch());
+ Register const object_prototype = object_map;
+ Register const prototype = ToRegister(instr->prototype());
- // This is the inlined call site instanceof cache. The two occurences of the
- // hole value will be patched to the last map/result pair generated by the
- // instanceof stub.
- __ Ldr(map, FieldMemOperand(object, HeapObject::kMapOffset));
- {
- // Below we use Factory::the_hole_value() on purpose instead of loading from
- // the root array to force relocation and later be able to patch with a
- // custom value.
- InstructionAccurateScope scope(masm(), 5);
- __ bind(&map_check);
- // Will be patched with the cached map.
- Handle<Cell> cell = factory()->NewCell(factory()->the_hole_value());
- __ ldr(scratch, Immediate(cell));
- __ ldr(scratch, FieldMemOperand(scratch, Cell::kValueOffset));
- __ cmp(map, scratch);
- __ b(&cache_miss, ne);
- // The address of this instruction is computed relative to the map check
- // above, so check the size of the code generated.
- DCHECK(masm()->InstructionsGeneratedSince(&map_check) == 4);
- // Will be patched with the cached result.
- __ ldr(result, Immediate(factory()->the_hole_value()));
+ // The {object} must be a spec object. It's sufficient to know that {object}
+ // is not a smi, since all other non-spec objects have {null} prototypes and
+ // will be ruled out below.
+ if (instr->hydrogen()->ObjectNeedsSmiCheck()) {
+ __ JumpIfSmi(object, instr->FalseLabel(chunk_));
}
- __ B(&done);
-
- // The inlined call site cache did not match.
- // Check null and string before calling the deferred code.
- __ Bind(&cache_miss);
- // Compute the address of the map check. It must not be clobbered until the
- // InstanceOfStub has used it.
- __ Adr(map_check_site, &map_check);
- // Null is not instance of anything.
- __ JumpIfRoot(object, Heap::kNullValueRootIndex, &return_false);
-
- // String values are not instances of anything.
- // Return false if the object is a string. Otherwise, jump to the deferred
- // code.
- // Note that we can't jump directly to deferred code from
- // IsObjectJSStringType, because it uses tbz for the jump and the deferred
- // code can be out of range.
- __ IsObjectJSStringType(object, scratch, NULL, &return_false);
- __ B(deferred->entry());
-
- __ Bind(&return_false);
- __ LoadRoot(result, Heap::kFalseValueRootIndex);
-
- // Here result is either true or false.
- __ Bind(deferred->exit());
- __ Bind(&done);
-}
-
-void LCodeGen::DoDeferredInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr) {
- Register result = ToRegister(instr->result());
- DCHECK(result.Is(x0)); // InstanceofStub returns its result in x0.
- InstanceofStub::Flags flags = InstanceofStub::kNoFlags;
- flags = static_cast<InstanceofStub::Flags>(
- flags | InstanceofStub::kArgsInRegisters);
- flags = static_cast<InstanceofStub::Flags>(
- flags | InstanceofStub::kReturnTrueFalseObject);
- flags = static_cast<InstanceofStub::Flags>(
- flags | InstanceofStub::kCallSiteInlineCheck);
-
- PushSafepointRegistersScope scope(this);
- LoadContextFromDeferred(instr->context());
-
- // Prepare InstanceofStub arguments.
- DCHECK(ToRegister(instr->value()).Is(InstanceofStub::left()));
- __ LoadObject(InstanceofStub::right(), instr->function());
-
- InstanceofStub stub(isolate(), flags);
- CallCodeGeneric(stub.GetCode(),
- RelocInfo::CODE_TARGET,
- instr,
- RECORD_SAFEPOINT_WITH_REGISTERS_AND_NO_ARGUMENTS);
- LEnvironment* env = instr->GetDeferredLazyDeoptimizationEnvironment();
- safepoints_.RecordLazyDeoptimizationIndex(env->deoptimization_index());
-
- // Put the result value into the result register slot.
- __ StoreToSafepointRegisterSlot(result, result);
+ // Loop through the {object}s prototype chain looking for the {prototype}.
+ __ Ldr(object_map, FieldMemOperand(object, HeapObject::kMapOffset));
+ Label loop;
+ __ Bind(&loop);
+ __ Ldr(object_prototype, FieldMemOperand(object_map, Map::kPrototypeOffset));
+ __ Cmp(object_prototype, prototype);
+ __ B(eq, instr->TrueLabel(chunk_));
+ __ CompareRoot(object_prototype, Heap::kNullValueRootIndex);
+ __ B(eq, instr->FalseLabel(chunk_));
+ __ Ldr(object_map, FieldMemOperand(object_prototype, HeapObject::kMapOffset));
+ __ B(&loop);
}
LOperand* temp1,
LOperand* temp2);
void DoDeferredAllocate(LAllocate* instr);
- void DoDeferredInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr);
void DoDeferredInstanceMigration(LCheckMaps* instr, Register object);
void DoDeferredLoadMutableDouble(LLoadFieldByIndex* instr,
Register result,
}
-void MacroAssembler::TryGetFunctionPrototype(Register function,
- Register result,
- Register scratch,
- Label* miss,
- BoundFunctionAction action) {
+void MacroAssembler::TryGetFunctionPrototype(Register function, Register result,
+ Register scratch, Label* miss) {
DCHECK(!AreAliased(function, result, scratch));
- Label non_instance;
- if (action == kMissOnBoundFunction) {
- // Check that the receiver isn't a smi.
- JumpIfSmi(function, miss);
-
- // Check that the function really is a function. Load map into result reg.
- JumpIfNotObjectType(function, result, scratch, JS_FUNCTION_TYPE, miss);
-
- Register scratch_w = scratch.W();
- Ldr(scratch,
- 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(scratch, SharedFunctionInfo::kCompilerHintsOffset));
- Tbnz(scratch, SharedFunctionInfo::kBoundFunction, miss);
-
- // Make sure that the function has an instance prototype.
- Ldrb(scratch, FieldMemOperand(result, Map::kBitFieldOffset));
- Tbnz(scratch, Map::kHasNonInstancePrototype, &non_instance);
- }
-
// Get the prototype or initial map from the function.
Ldr(result,
FieldMemOperand(function, JSFunction::kPrototypeOrInitialMapOffset));
// Get the prototype from the initial map.
Ldr(result, FieldMemOperand(result, Map::kPrototypeOffset));
- if (action == kMissOnBoundFunction) {
- B(&done);
-
- // Non-instance prototype: fetch prototype from constructor field in initial
- // map.
- Bind(&non_instance);
- GetMapConstructor(result, result, scratch, scratch);
- }
-
// All done.
Bind(&done);
}
}
-void MacroAssembler::GetRelocatedValueLocation(Register ldr_location,
- Register result) {
- DCHECK(!result.Is(ldr_location));
- const uint32_t kLdrLitOffset_lsb = 5;
- const uint32_t kLdrLitOffset_width = 19;
- Ldr(result, MemOperand(ldr_location));
- if (emit_debug_code()) {
- And(result, result, LoadLiteralFMask);
- Cmp(result, LoadLiteralFixed);
- Check(eq, kTheInstructionToPatchShouldBeAnLdrLiteral);
- // The instruction was clobbered. Reload it.
- Ldr(result, MemOperand(ldr_location));
- }
- Sbfx(result, result, kLdrLitOffset_lsb, kLdrLitOffset_width);
- Add(result, ldr_location, Operand(result, LSL, kWordSizeInBytesLog2));
-}
-
-
void MacroAssembler::EnsureNotWhite(
Register value,
Register bitmap_scratch,
// ---------------------------------------------------------------------------
// Support functions.
- // Try to get function prototype of a function and puts the value in the
- // result register. Checks that the function really is a function and jumps
- // to the miss label if the fast checks fail. The function register will be
- // untouched; the other registers may be clobbered.
- enum BoundFunctionAction {
- kMissOnBoundFunction,
- kDontMissOnBoundFunction
- };
-
// Machine code version of Map::GetConstructor().
// |temp| holds |result|'s map when done, and |temp2| its instance type.
void GetMapConstructor(Register result, Register map, Register temp,
Register temp2);
- void TryGetFunctionPrototype(Register function,
- Register result,
- Register scratch,
- Label* miss,
- BoundFunctionAction action =
- kDontMissOnBoundFunction);
+ void TryGetFunctionPrototype(Register function, Register result,
+ Register scratch, Label* miss);
// Compare object type for heap object. heap_object contains a non-Smi
// whose object type should be compared with the given type. This both
Label* on_black);
- // Get the location of a relocated constant (its address in the constant pool)
- // from its load site.
- void GetRelocatedValueLocation(Register ldr_location,
- Register result);
-
-
// ---------------------------------------------------------------------------
// Debugging.
V(kTheInstructionShouldBeAnOris, "The instruction should be an oris") \
V(kTheInstructionShouldBeALi, "The instruction should be a li") \
V(kTheInstructionShouldBeASldi, "The instruction should be a sldi") \
- V(kTheInstructionToPatchShouldBeALoadFromConstantPool, \
- "The instruction to patch should be a load from the constant pool") \
V(kTheInstructionToPatchShouldBeAnLdrLiteral, \
"The instruction to patch should be a ldr literal") \
V(kTheInstructionToPatchShouldBeALis, \
V(SHR, 1) \
V(SHR_STRONG, 1) \
V(IN, 1) \
- V(INSTANCE_OF, 1) \
V(CALL_NON_FUNCTION, 0) \
V(CALL_NON_FUNCTION_AS_CONSTRUCTOR, 0) \
V(CALL_FUNCTION_PROXY, 1) \
// static
-Callable CodeFactory::Instanceof(Isolate* isolate,
- InstanceofStub::Flags flags) {
- InstanceofStub stub(isolate, flags);
+Callable CodeFactory::InstanceOf(Isolate* isolate) {
+ InstanceOfStub stub(isolate);
return Callable(stub.GetCode(), stub.GetCallInterfaceDescriptor());
}
static Callable StoreGlobalViaContext(Isolate* isolate, int depth,
LanguageMode language_mode);
- static Callable Instanceof(Isolate* isolate, InstanceofStub::Flags flags);
+ static Callable InstanceOf(Isolate* isolate);
static Callable ToBoolean(
Isolate* isolate, ToBooleanStub::ResultMode mode,
}
-void InstanceofStub::PrintName(std::ostream& os) const { // NOLINT
- os << "InstanceofStub";
- if (HasArgsInRegisters()) os << "_REGS";
- if (HasCallSiteInlineCheck()) os << "_INLINE";
- if (ReturnTrueFalseObject()) os << "_TRUEFALSE";
-}
-
-
void JSEntryStub::FinishCode(Handle<Code> code) {
Handle<FixedArray> handler_table =
code->GetIsolate()->factory()->NewFixedArray(1, TENURED);
V(CompareIC) \
V(DoubleToI) \
V(FunctionPrototype) \
- V(Instanceof) \
+ V(InstanceOf) \
V(InternalArrayConstructor) \
V(JSEntry) \
V(KeyedLoadICTrampoline) \
DEFINE_HYDROGEN_CODE_STUB(GrowArrayElements, HydrogenCodeStub);
};
-class InstanceofStub: public PlatformCodeStub {
- public:
- enum Flags {
- kNoFlags = 0,
- kArgsInRegisters = 1 << 0,
- kCallSiteInlineCheck = 1 << 1,
- kReturnTrueFalseObject = 1 << 2
- };
-
- InstanceofStub(Isolate* isolate, Flags flags) : PlatformCodeStub(isolate) {
- minor_key_ = FlagBits::encode(flags);
- }
- static Register left() { return InstanceofDescriptor::left(); }
- static Register right() { return InstanceofDescriptor::right(); }
-
- CallInterfaceDescriptor GetCallInterfaceDescriptor() const override {
- if (HasArgsInRegisters()) {
- return InstanceofDescriptor(isolate());
- }
- return ContextOnlyDescriptor(isolate());
- }
+class InstanceOfStub final : public PlatformCodeStub {
+ public:
+ explicit InstanceOfStub(Isolate* isolate) : PlatformCodeStub(isolate) {}
private:
- Flags flags() const { return FlagBits::decode(minor_key_); }
-
- bool HasArgsInRegisters() const { return (flags() & kArgsInRegisters) != 0; }
-
- bool HasCallSiteInlineCheck() const {
- return (flags() & kCallSiteInlineCheck) != 0;
- }
-
- bool ReturnTrueFalseObject() const {
- return (flags() & kReturnTrueFalseObject) != 0;
- }
-
- void PrintName(std::ostream& os) const override; // NOLINT
-
- class FlagBits : public BitField<Flags, 0, 3> {};
-
- DEFINE_PLATFORM_CODE_STUB(Instanceof, PlatformCodeStub);
+ DEFINE_CALL_INTERFACE_DESCRIPTOR(InstanceOf);
+ DEFINE_PLATFORM_CODE_STUB(InstanceOf, PlatformCodeStub);
};
void JSGenericLowering::LowerJSInstanceOf(Node* node) {
CallDescriptor::Flags flags = AdjustFrameStatesForCall(node);
- InstanceofStub::Flags stub_flags = static_cast<InstanceofStub::Flags>(
- InstanceofStub::kReturnTrueFalseObject |
- InstanceofStub::kArgsInRegisters);
- Callable callable = CodeFactory::Instanceof(isolate(), stub_flags);
+ Callable callable = CodeFactory::InstanceOf(isolate());
ReplaceWithStubCall(node, callable, flags);
}
VisitForStackValue(expr->right());
__ InvokeBuiltin(Builtins::IN, CALL_FUNCTION);
PrepareForBailoutBeforeSplit(expr, false, NULL, NULL);
- __ LoadRoot(ip, Heap::kTrueValueRootIndex);
- __ cmp(r0, ip);
+ __ CompareRoot(r0, Heap::kTrueValueRootIndex);
Split(eq, if_true, if_false, fall_through);
break;
case Token::INSTANCEOF: {
- VisitForStackValue(expr->right());
- InstanceofStub stub(isolate(), InstanceofStub::kNoFlags);
+ VisitForAccumulatorValue(expr->right());
+ __ pop(r1);
+ InstanceOfStub stub(isolate());
__ CallStub(&stub);
- PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
- // The stub returns 0 for true.
- __ tst(r0, r0);
+ PrepareForBailoutBeforeSplit(expr, false, NULL, NULL);
+ __ CompareRoot(r0, Heap::kTrueValueRootIndex);
Split(eq, if_true, if_false, fall_through);
break;
}
break;
case Token::INSTANCEOF: {
- VisitForStackValue(expr->right());
- InstanceofStub stub(isolate(), InstanceofStub::kNoFlags);
+ VisitForAccumulatorValue(expr->right());
+ __ Pop(x1);
+ InstanceOfStub stub(isolate());
__ CallStub(&stub);
- PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
- // The stub returns 0 for true.
- __ CompareAndSplit(x0, 0, eq, if_true, if_false, fall_through);
+ PrepareForBailoutBeforeSplit(expr, false, NULL, NULL);
+ __ CompareRoot(x0, Heap::kTrueValueRootIndex);
+ Split(eq, if_true, if_false, fall_through);
break;
}
break;
case Token::INSTANCEOF: {
- VisitForStackValue(expr->right());
- InstanceofStub stub(isolate(), InstanceofStub::kNoFlags);
+ VisitForAccumulatorValue(expr->right());
+ __ Pop(edx);
+ InstanceOfStub stub(isolate());
__ CallStub(&stub);
- PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
- __ test(eax, eax);
- // The stub returns 0 for true.
- Split(zero, if_true, if_false, fall_through);
+ PrepareForBailoutBeforeSplit(expr, false, NULL, NULL);
+ __ cmp(eax, isolate()->factory()->true_value());
+ Split(equal, if_true, if_false, fall_through);
break;
}
break;
case Token::INSTANCEOF: {
- VisitForStackValue(expr->right());
- InstanceofStub stub(isolate(), InstanceofStub::kNoFlags);
+ VisitForAccumulatorValue(expr->right());
+ __ mov(a0, result_register());
+ __ pop(a1);
+ InstanceOfStub stub(isolate());
__ CallStub(&stub);
- PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
- // The stub returns 0 for true.
- Split(eq, v0, Operand(zero_reg), if_true, if_false, fall_through);
+ PrepareForBailoutBeforeSplit(expr, false, NULL, NULL);
+ __ LoadRoot(at, Heap::kTrueValueRootIndex);
+ Split(eq, v0, Operand(at), if_true, if_false, fall_through);
break;
}
break;
case Token::INSTANCEOF: {
- VisitForStackValue(expr->right());
- InstanceofStub stub(isolate(), InstanceofStub::kNoFlags);
+ VisitForAccumulatorValue(expr->right());
+ __ mov(a0, result_register());
+ __ pop(a1);
+ InstanceOfStub stub(isolate());
__ CallStub(&stub);
- PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
- // The stub returns 0 for true.
- Split(eq, v0, Operand(zero_reg), if_true, if_false, fall_through);
+ PrepareForBailoutBeforeSplit(expr, false, NULL, NULL);
+ __ LoadRoot(a4, Heap::kTrueValueRootIndex);
+ Split(eq, v0, Operand(a4), if_true, if_false, fall_through);
break;
}
break;
case Token::INSTANCEOF: {
- VisitForStackValue(expr->right());
- InstanceofStub stub(isolate(), InstanceofStub::kNoFlags);
+ VisitForAccumulatorValue(expr->right());
+ __ Pop(rdx);
+ InstanceOfStub stub(isolate());
__ CallStub(&stub);
- PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
- __ testp(rax, rax);
- // The stub returns 0 for true.
- Split(zero, if_true, if_false, fall_through);
+ PrepareForBailoutBeforeSplit(expr, false, NULL, NULL);
+ __ CompareRoot(rax, Heap::kTrueValueRootIndex);
+ Split(equal, if_true, if_false, fall_through);
break;
}
case HValue::kHasInstanceTypeAndBranch:
case HValue::kInnerAllocatedObject:
case HValue::kInstanceOf:
- case HValue::kInstanceOfKnownGlobal:
case HValue::kIsConstructCallAndBranch:
+ case HValue::kHasInPrototypeChainAndBranch:
case HValue::kIsObjectAndBranch:
case HValue::kIsSmiAndBranch:
case HValue::kIsStringAndBranch:
V(HasInstanceTypeAndBranch) \
V(InnerAllocatedObject) \
V(InstanceOf) \
- V(InstanceOfKnownGlobal) \
V(InvokeFunction) \
V(IsConstructCallAndBranch) \
+ V(HasInPrototypeChainAndBranch) \
V(IsObjectAndBranch) \
V(IsStringAndBranch) \
V(IsSmiAndBranch) \
};
-class HInstanceOfKnownGlobal final : public HTemplateInstruction<2> {
+class HHasInPrototypeChainAndBranch final
+ : public HTemplateControlInstruction<2, 2> {
public:
- DECLARE_INSTRUCTION_WITH_CONTEXT_FACTORY_P2(HInstanceOfKnownGlobal,
- HValue*,
- Handle<JSFunction>);
+ DECLARE_INSTRUCTION_FACTORY_P2(HHasInPrototypeChainAndBranch, HValue*,
+ HValue*);
- HValue* context() { return OperandAt(0); }
- HValue* left() { return OperandAt(1); }
- Handle<JSFunction> function() { return function_; }
+ HValue* object() const { return OperandAt(0); }
+ HValue* prototype() const { return OperandAt(1); }
Representation RequiredInputRepresentation(int index) override {
return Representation::Tagged();
}
- DECLARE_CONCRETE_INSTRUCTION(InstanceOfKnownGlobal)
+ bool ObjectNeedsSmiCheck() const {
+ return !object()->type().IsHeapObject() &&
+ !object()->representation().IsHeapObject();
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(HasInPrototypeChainAndBranch)
private:
- HInstanceOfKnownGlobal(HValue* context,
- HValue* left,
- Handle<JSFunction> right)
- : HTemplateInstruction<2>(HType::Boolean()), function_(right) {
- SetOperandAt(0, context);
- SetOperandAt(1, left);
- set_representation(Representation::Tagged());
- SetAllSideEffects();
+ HHasInPrototypeChainAndBranch(HValue* object, HValue* prototype) {
+ SetOperandAt(0, object);
+ SetOperandAt(1, prototype);
+ SetDependsOnFlag(kCalls);
}
-
- Handle<JSFunction> function_;
};
// Check to see if the rhs of the instanceof is a known function.
if (right->IsConstant() &&
HConstant::cast(right)->handle(isolate())->IsJSFunction()) {
- Handle<Object> function = HConstant::cast(right)->handle(isolate());
- Handle<JSFunction> target = Handle<JSFunction>::cast(function);
- HInstanceOfKnownGlobal* result =
- New<HInstanceOfKnownGlobal>(left, target);
- return ast_context()->ReturnInstruction(result, expr->id());
+ Handle<JSFunction> constructor =
+ Handle<JSFunction>::cast(HConstant::cast(right)->handle(isolate()));
+ if (!constructor->map()->has_non_instance_prototype()) {
+ JSFunction::EnsureHasInitialMap(constructor);
+ DCHECK(constructor->has_initial_map());
+ Handle<Map> initial_map(constructor->initial_map(), isolate());
+ top_info()->dependencies()->AssumeInitialMapCantChange(initial_map);
+ HInstruction* prototype =
+ Add<HConstant>(handle(initial_map->prototype(), isolate()));
+ HHasInPrototypeChainAndBranch* result =
+ New<HHasInPrototypeChainAndBranch>(left, prototype);
+ return ast_context()->ReturnControl(result, expr->id());
+ }
}
HInstanceOf* result = New<HInstanceOf>(left, right);
}
+void HOptimizedGraphBuilder::GenerateHasInPrototypeChain(CallRuntime* call) {
+ DCHECK_EQ(2, call->arguments()->length());
+ CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
+ CHECK_ALIVE(VisitForValue(call->arguments()->at(1)));
+ HValue* prototype = Pop();
+ HValue* object = Pop();
+ HHasInPrototypeChainAndBranch* result =
+ New<HHasInPrototypeChainAndBranch>(object, prototype);
+ return ast_context()->ReturnControl(result, call->id());
+}
+
+
void HOptimizedGraphBuilder::GenerateFixedArrayGet(CallRuntime* call) {
DCHECK(call->arguments()->length() == 2);
CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
F(DebugIsActive) \
F(Likely) \
F(Unlikely) \
+ F(HasInPrototypeChain) \
/* Typed Arrays */ \
F(TypedArrayInitialize) \
F(DataViewInitialize) \
}
-// Generate stub code for instanceof.
-// This code can patch a call site inlined cache of the instance of check,
-// which looks like this.
-//
-// 81 ff XX XX XX XX cmp edi, <the hole, patched to a map>
-// 75 0a jne <some near label>
-// b8 XX XX XX XX mov eax, <the hole, patched to either true or false>
-//
-// If call site patching is requested the stack will have the delta from the
-// return address to the cmp instruction just below the return address. This
-// also means that call site patching can only take place with arguments in
-// registers. TOS looks like this when call site patching is requested
-//
-// esp[0] : return address
-// esp[4] : delta from return address to cmp instruction
-//
-void InstanceofStub::Generate(MacroAssembler* masm) {
- // Call site inlining and patching implies arguments in registers.
- DCHECK(HasArgsInRegisters() || !HasCallSiteInlineCheck());
-
- // Fixed register usage throughout the stub.
- Register object = eax; // Object (lhs).
- Register map = ebx; // Map of the object.
- Register function = edx; // Function (rhs).
- Register prototype = edi; // Prototype of the function.
- Register scratch = ecx;
-
- // Constants describing the call site code to patch.
- static const int kDeltaToCmpImmediate = 2;
- static const int kDeltaToMov = 8;
- static const int kDeltaToMovImmediate = 9;
- static const int8_t kCmpEdiOperandByte1 = bit_cast<int8_t, uint8_t>(0x3b);
- static const int8_t kCmpEdiOperandByte2 = bit_cast<int8_t, uint8_t>(0x3d);
- static const int8_t kMovEaxImmediateByte = bit_cast<int8_t, uint8_t>(0xb8);
-
- DCHECK_EQ(object.code(), InstanceofStub::left().code());
- DCHECK_EQ(function.code(), InstanceofStub::right().code());
-
- // Get the object and function - they are always both needed.
- Label slow, not_js_object;
- if (!HasArgsInRegisters()) {
- __ mov(object, Operand(esp, 2 * kPointerSize));
- __ mov(function, Operand(esp, 1 * kPointerSize));
- }
+void InstanceOfStub::Generate(MacroAssembler* masm) {
+ Register const object = edx; // Object (lhs).
+ Register const function = eax; // Function (rhs).
+ Register const object_map = ecx; // Map of {object}.
+ Register const function_map = ebx; // Map of {function}.
+ Register const function_prototype = function_map; // Prototype of {function}.
+ Register const scratch = edi;
+
+ 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, Label::kNear);
+
+ // 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;
+ __ mov(object_map, FieldOperand(object, HeapObject::kMapOffset));
+ __ CompareRoot(function, scratch, Heap::kInstanceofCacheFunctionRootIndex);
+ __ j(not_equal, &fast_case, Label::kNear);
+ __ CompareRoot(object_map, scratch, Heap::kInstanceofCacheMapRootIndex);
+ __ j(not_equal, &fast_case, Label::kNear);
+ __ LoadRoot(eax, Heap::kInstanceofCacheAnswerRootIndex);
+ __ ret(0);
- // Check that the left hand is a JS object.
- __ JumpIfSmi(object, ¬_js_object);
- __ IsObjectJSObjectType(object, map, scratch, ¬_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()) {
- // Look up the function and the map in the instanceof cache.
- Label miss;
- __ CompareRoot(function, scratch, Heap::kInstanceofCacheFunctionRootIndex);
- __ j(not_equal, &miss, Label::kNear);
- __ CompareRoot(map, scratch, Heap::kInstanceofCacheMapRootIndex);
- __ j(not_equal, &miss, Label::kNear);
- __ LoadRoot(eax, Heap::kInstanceofCacheAnswerRootIndex);
- __ ret((HasArgsInRegisters() ? 0 : 2) * kPointerSize);
- __ bind(&miss);
- }
+ // 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);
+ __ CmpObjectType(function, JS_FUNCTION_TYPE, function_map);
+ __ j(not_equal, &slow_case);
+ __ LoadRoot(eax, Heap::kFalseValueRootIndex);
+ __ ret(0);
- // Get the prototype of the function.
- __ TryGetFunctionPrototype(function, prototype, scratch, &slow, true);
+ // Fast-case: The {function} must be a valid JSFunction.
+ __ bind(&fast_case);
+ __ JumpIfSmi(function, &slow_case);
+ __ CmpObjectType(function, JS_FUNCTION_TYPE, function_map);
+ __ j(not_equal, &slow_case);
- // Check that the function prototype is a JS object.
- __ JumpIfSmi(prototype, &slow);
- __ IsObjectJSObjectType(prototype, scratch, scratch, &slow);
+ // Ensure that {function} has an instance prototype.
+ __ test_b(FieldOperand(function_map, Map::kBitFieldOffset),
+ static_cast<uint8_t>(1 << Map::kHasNonInstancePrototype));
+ __ j(not_zero, &slow_case);
- // 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()) {
- __ StoreRoot(map, scratch, Heap::kInstanceofCacheMapRootIndex);
- __ StoreRoot(function, scratch, Heap::kInstanceofCacheFunctionRootIndex);
- } else {
- // The constants for the code patching are based on no push instructions
- // at the call site.
- DCHECK(HasArgsInRegisters());
- // Get return address and delta to inlined map check.
- __ mov(scratch, Operand(esp, 0 * kPointerSize));
- __ sub(scratch, Operand(esp, 1 * kPointerSize));
- if (FLAG_debug_code) {
- __ cmpb(Operand(scratch, 0), kCmpEdiOperandByte1);
- __ Assert(equal, kInstanceofStubUnexpectedCallSiteCacheCmp1);
- __ cmpb(Operand(scratch, 1), kCmpEdiOperandByte2);
- __ Assert(equal, kInstanceofStubUnexpectedCallSiteCacheCmp2);
- }
- __ mov(scratch, Operand(scratch, kDeltaToCmpImmediate));
- __ mov(Operand(scratch, 0), map);
- __ push(map);
- // Scratch points at the cell payload. Calculate the start of the object.
- __ sub(scratch, Immediate(Cell::kValueOffset - 1));
- __ RecordWriteField(scratch, Cell::kValueOffset, map, function,
- kDontSaveFPRegs, OMIT_REMEMBERED_SET, OMIT_SMI_CHECK);
- __ pop(map);
- }
+ // Ensure that {function} is not bound.
+ Register const shared_info = scratch;
+ __ mov(shared_info,
+ FieldOperand(function, JSFunction::kSharedFunctionInfoOffset));
+ __ BooleanBitTest(shared_info, SharedFunctionInfo::kCompilerHintsOffset,
+ SharedFunctionInfo::kBoundFunction);
+ __ j(not_zero, &slow_case);
- // Loop through the prototype chain of the object looking for the function
- // prototype.
- __ mov(scratch, FieldOperand(map, Map::kPrototypeOffset));
- Label loop, is_instance, is_not_instance;
+ // Get the "prototype" (or initial map) of the {function}.
+ __ mov(function_prototype,
+ FieldOperand(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;
+ Register const function_prototype_map = scratch;
+ __ CmpObjectType(function_prototype, MAP_TYPE, function_prototype_map);
+ __ j(not_equal, &function_prototype_valid, Label::kNear);
+ __ mov(function_prototype,
+ FieldOperand(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, scratch, Heap::kInstanceofCacheFunctionRootIndex);
+ __ StoreRoot(object_map, scratch, 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;
+ Label done, loop;
+ __ mov(eax, isolate()->factory()->true_value());
__ bind(&loop);
- __ cmp(scratch, prototype);
- __ j(equal, &is_instance, Label::kNear);
- Factory* factory = isolate()->factory();
- __ cmp(scratch, Immediate(factory->null_value()));
- __ j(equal, &is_not_instance, Label::kNear);
- __ mov(scratch, FieldOperand(scratch, HeapObject::kMapOffset));
- __ mov(scratch, FieldOperand(scratch, Map::kPrototypeOffset));
- __ jmp(&loop);
-
- __ bind(&is_instance);
- if (!HasCallSiteInlineCheck()) {
- __ mov(eax, Immediate(0));
- __ StoreRoot(eax, scratch, Heap::kInstanceofCacheAnswerRootIndex);
- if (ReturnTrueFalseObject()) {
- __ mov(eax, factory->true_value());
- }
- } else {
- // Get return address and delta to inlined map check.
- __ mov(eax, factory->true_value());
- __ mov(scratch, Operand(esp, 0 * kPointerSize));
- __ sub(scratch, Operand(esp, 1 * kPointerSize));
- if (FLAG_debug_code) {
- __ cmpb(Operand(scratch, kDeltaToMov), kMovEaxImmediateByte);
- __ Assert(equal, kInstanceofStubUnexpectedCallSiteCacheMov);
- }
- __ mov(Operand(scratch, kDeltaToMovImmediate), eax);
- if (!ReturnTrueFalseObject()) {
- __ Move(eax, Immediate(0));
- }
- }
- __ ret((HasArgsInRegisters() ? 0 : 2) * kPointerSize);
-
- __ bind(&is_not_instance);
- if (!HasCallSiteInlineCheck()) {
- __ mov(eax, Immediate(Smi::FromInt(1)));
- __ StoreRoot(eax, scratch, Heap::kInstanceofCacheAnswerRootIndex);
- if (ReturnTrueFalseObject()) {
- __ mov(eax, factory->false_value());
- }
- } else {
- // Get return address and delta to inlined map check.
- __ mov(eax, factory->false_value());
- __ mov(scratch, Operand(esp, 0 * kPointerSize));
- __ sub(scratch, Operand(esp, 1 * kPointerSize));
- if (FLAG_debug_code) {
- __ cmpb(Operand(scratch, kDeltaToMov), kMovEaxImmediateByte);
- __ Assert(equal, kInstanceofStubUnexpectedCallSiteCacheMov);
- }
- __ mov(Operand(scratch, kDeltaToMovImmediate), eax);
- if (!ReturnTrueFalseObject()) {
- __ Move(eax, Immediate(Smi::FromInt(1)));
- }
- }
- __ ret((HasArgsInRegisters() ? 0 : 2) * kPointerSize);
-
- 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.
- __ JumpIfSmi(function, &slow, Label::kNear);
- __ CmpObjectType(function, JS_FUNCTION_TYPE, scratch);
- __ j(not_equal, &slow, Label::kNear);
-
- // Null is not instance of anything.
- __ cmp(object, factory->null_value());
- __ j(not_equal, &object_not_null, Label::kNear);
- if (ReturnTrueFalseObject()) {
- __ mov(eax, factory->false_value());
- } else {
- __ Move(eax, Immediate(Smi::FromInt(1)));
- }
- __ ret((HasArgsInRegisters() ? 0 : 2) * kPointerSize);
-
- __ bind(&object_not_null);
- // Smi values is not instance of anything.
- __ JumpIfNotSmi(object, &object_not_null_or_smi, Label::kNear);
- if (ReturnTrueFalseObject()) {
- __ mov(eax, factory->false_value());
- } else {
- __ Move(eax, Immediate(Smi::FromInt(1)));
- }
- __ ret((HasArgsInRegisters() ? 0 : 2) * kPointerSize);
-
- __ bind(&object_not_null_or_smi);
- // String values is not instance of anything.
- Condition is_string = masm->IsObjectStringType(object, scratch, scratch);
- __ j(NegateCondition(is_string), &slow, Label::kNear);
- if (ReturnTrueFalseObject()) {
- __ mov(eax, factory->false_value());
- } else {
- __ Move(eax, Immediate(Smi::FromInt(1)));
- }
- __ ret((HasArgsInRegisters() ? 0 : 2) * kPointerSize);
+ __ mov(object_prototype, FieldOperand(object_map, Map::kPrototypeOffset));
+ __ cmp(object_prototype, function_prototype);
+ __ j(equal, &done, Label::kNear);
+ __ cmp(object_prototype, isolate()->factory()->null_value());
+ __ mov(object_map, FieldOperand(object_prototype, HeapObject::kMapOffset));
+ __ j(not_equal, &loop);
+ __ mov(eax, isolate()->factory()->false_value());
+ __ bind(&done);
+ __ StoreRoot(eax, scratch, Heap::kInstanceofCacheAnswerRootIndex);
+ __ ret(0);
- // Slow-case: Go through the JavaScript implementation.
- __ bind(&slow);
- if (!ReturnTrueFalseObject()) {
- // Tail call the builtin which returns 0 or 1.
- if (HasArgsInRegisters()) {
- // Push arguments below return address.
- __ pop(scratch);
- __ push(object);
- __ push(function);
- __ push(scratch);
- }
- __ InvokeBuiltin(Builtins::INSTANCE_OF, JUMP_FUNCTION);
- } else {
- // Call the builtin and convert 0/1 to true/false.
- {
- FrameScope scope(masm, StackFrame::INTERNAL);
- __ push(object);
- __ push(function);
- __ InvokeBuiltin(Builtins::INSTANCE_OF, CALL_FUNCTION);
- }
- Label true_value, done;
- __ test(eax, eax);
- __ j(zero, &true_value, Label::kNear);
- __ mov(eax, factory->false_value());
- __ jmp(&done, Label::kNear);
- __ bind(&true_value);
- __ mov(eax, factory->true_value());
- __ bind(&done);
- __ ret((HasArgsInRegisters() ? 0 : 2) * kPointerSize);
- }
+ // Slow-case: Call the runtime function.
+ __ bind(&slow_case);
+ __ pop(scratch); // Pop return address.
+ __ push(object); // Push {object}.
+ __ push(function); // Push {function}.
+ __ push(scratch); // Push return address.
+ __ TailCallRuntime(Runtime::kInstanceOf, 2, 1);
}
const Register StoreGlobalViaContextDescriptor::ValueRegister() { return eax; }
-const Register InstanceofDescriptor::left() { return eax; }
-const Register InstanceofDescriptor::right() { return edx; }
+const Register InstanceOfDescriptor::LeftRegister() { return edx; }
+const Register InstanceOfDescriptor::RightRegister() { return eax; }
const Register ArgumentsAccessReadDescriptor::index() { return edx; }
}
+template <class InstrType>
+void LCodeGen::EmitTrueBranch(InstrType instr, Condition cc) {
+ int true_block = instr->TrueDestination(chunk_);
+ if (cc == no_condition) {
+ __ jmp(chunk_->GetAssemblyLabel(true_block));
+ } else {
+ __ j(cc, chunk_->GetAssemblyLabel(true_block));
+ }
+}
+
+
template<class InstrType>
void LCodeGen::EmitFalseBranch(InstrType instr, Condition cc) {
int false_block = instr->FalseDestination(chunk_);
void LCodeGen::DoInstanceOf(LInstanceOf* instr) {
- // Object and function are in fixed registers defined by the stub.
DCHECK(ToRegister(instr->context()).is(esi));
- InstanceofStub stub(isolate(), InstanceofStub::kArgsInRegisters);
+ DCHECK(ToRegister(instr->left()).is(InstanceOfDescriptor::LeftRegister()));
+ DCHECK(ToRegister(instr->right()).is(InstanceOfDescriptor::RightRegister()));
+ DCHECK(ToRegister(instr->result()).is(eax));
+ InstanceOfStub stub(isolate());
CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
-
- Label true_value, done;
- __ test(eax, Operand(eax));
- __ j(zero, &true_value, Label::kNear);
- __ mov(ToRegister(instr->result()), factory()->false_value());
- __ jmp(&done, Label::kNear);
- __ bind(&true_value);
- __ mov(ToRegister(instr->result()), factory()->true_value());
- __ bind(&done);
-}
-
-
-void LCodeGen::DoInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr) {
- class DeferredInstanceOfKnownGlobal final : public LDeferredCode {
- public:
- DeferredInstanceOfKnownGlobal(LCodeGen* codegen,
- LInstanceOfKnownGlobal* instr)
- : LDeferredCode(codegen), instr_(instr) { }
- void Generate() override {
- codegen()->DoDeferredInstanceOfKnownGlobal(instr_, &map_check_);
- }
- LInstruction* instr() override { return instr_; }
- Label* map_check() { return &map_check_; }
- private:
- LInstanceOfKnownGlobal* instr_;
- Label map_check_;
- };
-
- DeferredInstanceOfKnownGlobal* deferred;
- deferred = new(zone()) DeferredInstanceOfKnownGlobal(this, instr);
-
- Label done, false_result;
- Register object = ToRegister(instr->value());
- Register temp = ToRegister(instr->temp());
-
- // A Smi is not an instance of anything.
- __ JumpIfSmi(object, &false_result, Label::kNear);
-
- // This is the inlined call site instanceof cache. The two occurences of the
- // hole value will be patched to the last map/result pair generated by the
- // instanceof stub.
- Label cache_miss;
- Register map = ToRegister(instr->temp());
- __ mov(map, FieldOperand(object, HeapObject::kMapOffset));
- __ bind(deferred->map_check()); // Label for calculating code patching.
- Handle<Cell> cache_cell = factory()->NewCell(factory()->the_hole_value());
- __ cmp(map, Operand::ForCell(cache_cell)); // Patched to cached map.
- __ j(not_equal, &cache_miss, Label::kNear);
- __ mov(eax, factory()->the_hole_value()); // Patched to either true or false.
- __ jmp(&done, Label::kNear);
-
- // The inlined call site cache did not match. Check for null and string
- // before calling the deferred code.
- __ bind(&cache_miss);
- // Null is not an instance of anything.
- __ cmp(object, factory()->null_value());
- __ j(equal, &false_result, Label::kNear);
-
- // String values are not instances of anything.
- Condition is_string = masm_->IsObjectStringType(object, temp, temp);
- __ j(is_string, &false_result, Label::kNear);
-
- // Go to the deferred code.
- __ jmp(deferred->entry());
-
- __ bind(&false_result);
- __ mov(ToRegister(instr->result()), factory()->false_value());
-
- // Here result has either true or false. Deferred code also produces true or
- // false object.
- __ bind(deferred->exit());
- __ bind(&done);
}
-void LCodeGen::DoDeferredInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr,
- Label* map_check) {
- PushSafepointRegistersScope scope(this);
+void LCodeGen::DoHasInPrototypeChainAndBranch(
+ LHasInPrototypeChainAndBranch* instr) {
+ Register const object = ToRegister(instr->object());
+ Register const object_map = ToRegister(instr->scratch());
+ Register const object_prototype = object_map;
+ Register const prototype = ToRegister(instr->prototype());
- InstanceofStub::Flags flags = InstanceofStub::kNoFlags;
- flags = static_cast<InstanceofStub::Flags>(
- flags | InstanceofStub::kArgsInRegisters);
- flags = static_cast<InstanceofStub::Flags>(
- flags | InstanceofStub::kCallSiteInlineCheck);
- flags = static_cast<InstanceofStub::Flags>(
- flags | InstanceofStub::kReturnTrueFalseObject);
- InstanceofStub stub(isolate(), flags);
-
- // Get the temp register reserved by the instruction. This needs to be a
- // register which is pushed last by PushSafepointRegisters as top of the
- // stack is used to pass the offset to the location of the map check to
- // the stub.
- Register temp = ToRegister(instr->temp());
- DCHECK(MacroAssembler::SafepointRegisterStackIndex(temp) == 0);
- __ LoadHeapObject(InstanceofStub::right(), instr->function());
- static const int kAdditionalDelta = 13;
- int delta = masm_->SizeOfCodeGeneratedSince(map_check) + kAdditionalDelta;
- __ mov(temp, Immediate(delta));
- __ StoreToSafepointRegisterSlot(temp, temp);
- CallCodeGeneric(stub.GetCode(),
- RelocInfo::CODE_TARGET,
- instr,
- RECORD_SAFEPOINT_WITH_REGISTERS_AND_NO_ARGUMENTS);
- // Get the deoptimization index of the LLazyBailout-environment that
- // corresponds to this instruction.
- LEnvironment* env = instr->GetDeferredLazyDeoptimizationEnvironment();
- safepoints_.RecordLazyDeoptimizationIndex(env->deoptimization_index());
+ // The {object} must be a spec object. It's sufficient to know that {object}
+ // is not a smi, since all other non-spec objects have {null} prototypes and
+ // will be ruled out below.
+ if (instr->hydrogen()->ObjectNeedsSmiCheck()) {
+ __ test(object, Immediate(kSmiTagMask));
+ EmitFalseBranch(instr, zero);
+ }
- // Put the result value into the eax slot and restore all registers.
- __ StoreToSafepointRegisterSlot(eax, eax);
+ // Loop through the {object}s prototype chain looking for the {prototype}.
+ __ mov(object_map, FieldOperand(object, HeapObject::kMapOffset));
+ Label loop;
+ __ bind(&loop);
+ __ mov(object_prototype, FieldOperand(object_map, Map::kPrototypeOffset));
+ __ cmp(object_prototype, prototype);
+ EmitTrueBranch(instr, equal);
+ __ cmp(object_prototype, factory()->null_value());
+ EmitFalseBranch(instr, equal);
+ __ mov(object_map, FieldOperand(object_prototype, HeapObject::kMapOffset));
+ __ jmp(&loop);
}
void DoDeferredStringCharCodeAt(LStringCharCodeAt* instr);
void DoDeferredStringCharFromCode(LStringCharFromCode* instr);
void DoDeferredAllocate(LAllocate* instr);
- void DoDeferredInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr,
- Label* map_check);
void DoDeferredInstanceMigration(LCheckMaps* instr, Register object);
void DoDeferredLoadMutableDouble(LLoadFieldByIndex* instr,
Register object,
// EmitBranch expects to be the last instruction of a block.
template<class InstrType>
void EmitBranch(InstrType instr, Condition cc);
- template<class InstrType>
+ template <class InstrType>
+ void EmitTrueBranch(InstrType instr, Condition cc);
+ template <class InstrType>
void EmitFalseBranch(InstrType instr, Condition cc);
void EmitNumberUntagD(LNumberUntagD* instr, Register input, Register temp,
XMMRegister result, NumberUntagDMode mode);
if (instr->IsCall()) {
HValue* hydrogen_value_for_lazy_bailout = hydrogen_val;
- LInstruction* instruction_needing_environment = NULL;
if (hydrogen_val->HasObservableSideEffects()) {
HSimulate* sim = HSimulate::cast(hydrogen_val->next());
- instruction_needing_environment = instr;
sim->ReplayEnvironment(current_block_->last_environment());
hydrogen_value_for_lazy_bailout = sim;
}
LInstruction* bailout = AssignEnvironment(new(zone()) LLazyBailout());
bailout->set_hydrogen_value(hydrogen_value_for_lazy_bailout);
chunk_->AddInstruction(bailout, current_block_);
- if (instruction_needing_environment != NULL) {
- // Store the lazy deopt environment with the instruction if needed.
- // Right now it is only used for LInstanceOfKnownGlobal.
- instruction_needing_environment->
- SetDeferredLazyDeoptimizationEnvironment(bailout->environment());
- }
}
}
LInstruction* LChunkBuilder::DoInstanceOf(HInstanceOf* instr) {
- LOperand* left = UseFixed(instr->left(), InstanceofStub::left());
- LOperand* right = UseFixed(instr->right(), InstanceofStub::right());
+ LOperand* left =
+ UseFixed(instr->left(), InstanceOfDescriptor::LeftRegister());
+ LOperand* right =
+ UseFixed(instr->right(), InstanceOfDescriptor::RightRegister());
LOperand* context = UseFixed(instr->context(), esi);
- LInstanceOf* result = new(zone()) LInstanceOf(context, left, right);
+ LInstanceOf* result = new (zone()) LInstanceOf(context, left, right);
return MarkAsCall(DefineFixed(result, eax), instr);
}
-LInstruction* LChunkBuilder::DoInstanceOfKnownGlobal(
- HInstanceOfKnownGlobal* instr) {
- LInstanceOfKnownGlobal* result =
- new(zone()) LInstanceOfKnownGlobal(
- UseFixed(instr->context(), esi),
- UseFixed(instr->left(), InstanceofStub::left()),
- FixedTemp(edi));
- return MarkAsCall(DefineFixed(result, eax), instr);
+LInstruction* LChunkBuilder::DoHasInPrototypeChainAndBranch(
+ HHasInPrototypeChainAndBranch* instr) {
+ LOperand* object = UseRegister(instr->object());
+ LOperand* prototype = UseRegister(instr->prototype());
+ LOperand* temp = TempRegister();
+ return new (zone()) LHasInPrototypeChainAndBranch(object, prototype, temp);
}
V(GetCachedArrayIndex) \
V(Goto) \
V(HasCachedArrayIndexAndBranch) \
+ V(HasInPrototypeChainAndBranch) \
V(HasInstanceTypeAndBranch) \
V(InnerAllocatedObject) \
V(InstanceOf) \
- V(InstanceOfKnownGlobal) \
V(InstructionGap) \
V(Integer32ToDouble) \
V(InvokeFunction) \
void set_hydrogen_value(HValue* value) { hydrogen_value_ = value; }
HValue* hydrogen_value() const { return hydrogen_value_; }
- virtual void SetDeferredLazyDeoptimizationEnvironment(LEnvironment* env) { }
-
void MarkAsCall() { bit_field_ = IsCallBits::update(bit_field_, true); }
bool IsCall() const { return IsCallBits::decode(bit_field_); }
inputs_[2] = right;
}
- LOperand* context() { return inputs_[0]; }
+ LOperand* context() const { return inputs_[0]; }
+ LOperand* left() const { return inputs_[1]; }
+ LOperand* right() const { return inputs_[2]; }
DECLARE_CONCRETE_INSTRUCTION(InstanceOf, "instance-of")
};
-class LInstanceOfKnownGlobal final : public LTemplateInstruction<1, 2, 1> {
+class LHasInPrototypeChainAndBranch final : public LControlInstruction<2, 1> {
public:
- LInstanceOfKnownGlobal(LOperand* context, LOperand* value, LOperand* temp) {
- inputs_[0] = context;
- inputs_[1] = value;
- temps_[0] = temp;
+ LHasInPrototypeChainAndBranch(LOperand* object, LOperand* prototype,
+ LOperand* scratch) {
+ inputs_[0] = object;
+ inputs_[1] = prototype;
+ temps_[0] = scratch;
}
- LOperand* context() { return inputs_[0]; }
- LOperand* value() { return inputs_[1]; }
- LOperand* temp() { return temps_[0]; }
+ LOperand* object() const { return inputs_[0]; }
+ LOperand* prototype() const { return inputs_[1]; }
+ LOperand* scratch() const { return temps_[0]; }
- DECLARE_CONCRETE_INSTRUCTION(InstanceOfKnownGlobal,
- "instance-of-known-global")
- DECLARE_HYDROGEN_ACCESSOR(InstanceOfKnownGlobal)
-
- Handle<JSFunction> function() const { return hydrogen()->function(); }
- LEnvironment* GetDeferredLazyDeoptimizationEnvironment() {
- return lazy_deopt_env_;
- }
- virtual void SetDeferredLazyDeoptimizationEnvironment(
- LEnvironment* env) override {
- lazy_deopt_env_ = env;
- }
-
- private:
- LEnvironment* lazy_deopt_env_;
+ DECLARE_CONCRETE_INSTRUCTION(HasInPrototypeChainAndBranch,
+ "has-in-prototype-chain-and-branch")
+ DECLARE_HYDROGEN_ACCESSOR(HasInPrototypeChainAndBranch)
};
}
-void MacroAssembler::TryGetFunctionPrototype(Register function,
- Register result,
- Register scratch,
- Label* miss,
- bool miss_on_bound_function) {
- Label non_instance;
- if (miss_on_bound_function) {
- // Check that the receiver isn't a smi.
- JumpIfSmi(function, miss);
-
- // Check that the function really is a function.
- CmpObjectType(function, JS_FUNCTION_TYPE, result);
- j(not_equal, miss);
-
- // If a bound function, go to miss label.
- mov(scratch,
- FieldOperand(function, JSFunction::kSharedFunctionInfoOffset));
- BooleanBitTest(scratch, SharedFunctionInfo::kCompilerHintsOffset,
- SharedFunctionInfo::kBoundFunction);
- j(not_zero, miss);
-
- // Make sure that the function has an instance prototype.
- movzx_b(scratch, FieldOperand(result, Map::kBitFieldOffset));
- test(scratch, Immediate(1 << Map::kHasNonInstancePrototype));
- j(not_zero, &non_instance);
- }
-
+void MacroAssembler::TryGetFunctionPrototype(Register function, Register result,
+ Register scratch, Label* miss) {
// Get the prototype or initial map from the function.
mov(result,
FieldOperand(function, JSFunction::kPrototypeOrInitialMapOffset));
// If the function does not have an initial map, we're done.
Label done;
CmpObjectType(result, MAP_TYPE, scratch);
- j(not_equal, &done);
+ j(not_equal, &done, Label::kNear);
// Get the prototype from the initial map.
mov(result, FieldOperand(result, Map::kPrototypeOffset));
- if (miss_on_bound_function) {
- jmp(&done);
-
- // Non-instance prototype: Fetch prototype from constructor field
- // in initial map.
- bind(&non_instance);
- GetMapConstructor(result, result, scratch);
- }
-
// All done.
bind(&done);
}
// function and jumps to the miss label if the fast checks fail. The
// function register will be untouched; the other registers may be
// clobbered.
- void TryGetFunctionPrototype(Register function,
- Register result,
- Register scratch,
- Label* miss,
- bool miss_on_bound_function = false);
+ void TryGetFunctionPrototype(Register function, Register result,
+ Register scratch, Label* miss);
// Picks out an array index from the hash field.
// Register use:
}
-void InstanceofDescriptor::InitializePlatformSpecific(
+void InstanceOfDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
- Register registers[] = {left(), right()};
+ Register registers[] = {LeftRegister(), RightRegister()};
data->InitializePlatformSpecific(arraysize(registers), registers);
}
V(VectorStoreTransition) \
V(VectorStoreICTrampoline) \
V(VectorStoreIC) \
- V(Instanceof) \
+ V(InstanceOf) \
V(LoadWithVector) \
V(FastNewClosure) \
V(FastNewContext) \
};
-class InstanceofDescriptor : public CallInterfaceDescriptor {
+class InstanceOfDescriptor final : public CallInterfaceDescriptor {
public:
- DECLARE_DESCRIPTOR(InstanceofDescriptor, CallInterfaceDescriptor)
+ DECLARE_DESCRIPTOR(InstanceOfDescriptor, CallInterfaceDescriptor)
enum ParameterIndices { kLeftIndex, kRightIndex, kParameterCount };
- static const Register left();
- static const Register right();
+ static const Register LeftRegister();
+ static const Register RightRegister();
};
}
-// Uses registers a0 to t0.
-// Expected input (depending on whether args are in registers or on the stack):
-// * object: a0 or at sp + 1 * kPointerSize.
-// * function: a1 or at sp.
-//
-// An inlined call site may have been generated before calling this stub.
-// In this case the offset to the inline site to patch is passed on the stack,
-// in the safepoint slot for register t0.
-void InstanceofStub::Generate(MacroAssembler* masm) {
- // Call site inlining and patching implies arguments in registers.
- DCHECK(HasArgsInRegisters() || !HasCallSiteInlineCheck());
-
- // Fixed register usage throughout the stub:
- const Register object = a0; // Object (lhs).
- Register map = a3; // Map of the object.
- const Register function = a1; // Function (rhs).
- const Register prototype = t0; // Prototype of the function.
- const Register inline_site = t5;
- const Register scratch = a2;
-
- const int32_t kDeltaToLoadBoolResult = 5 * kPointerSize;
-
- Label slow, loop, is_instance, is_not_instance, not_js_object;
-
- if (!HasArgsInRegisters()) {
- __ lw(object, MemOperand(sp, 1 * kPointerSize));
- __ lw(function, MemOperand(sp, 0));
- }
-
- // Check that the left hand is a JS object and load map.
- __ JumpIfSmi(object, ¬_js_object);
- __ IsObjectJSObjectType(object, map, scratch, ¬_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;
- __ LoadRoot(at, Heap::kInstanceofCacheFunctionRootIndex);
- __ Branch(&miss, ne, function, Operand(at));
- __ LoadRoot(at, Heap::kInstanceofCacheMapRootIndex);
- __ Branch(&miss, ne, map, Operand(at));
- __ LoadRoot(v0, Heap::kInstanceofCacheAnswerRootIndex);
- __ DropAndRet(HasArgsInRegisters() ? 0 : 2);
-
- __ bind(&miss);
- }
-
- // Get the prototype of the function.
- __ TryGetFunctionPrototype(function, prototype, scratch, &slow, true);
-
- // Check that the function prototype is a JS object.
- __ JumpIfSmi(prototype, &slow);
- __ IsObjectJSObjectType(prototype, scratch, scratch, &slow);
+void InstanceOfStub::Generate(MacroAssembler* masm) {
+ Register const object = a1; // Object (lhs).
+ Register const function = a0; // Function (rhs).
+ Register const object_map = a2; // Map of {object}.
+ Register const function_map = a3; // Map of {function}.
+ Register const function_prototype = t0; // Prototype of {function}.
+ Register const scratch = t1;
+
+ 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;
+ __ lw(object_map, FieldMemOperand(object, HeapObject::kMapOffset));
+ __ LoadRoot(at, Heap::kInstanceofCacheFunctionRootIndex);
+ __ Branch(&fast_case, ne, function, Operand(at));
+ __ LoadRoot(at, Heap::kInstanceofCacheMapRootIndex);
+ __ Branch(&fast_case, ne, object_map, Operand(at));
+ __ Ret(USE_DELAY_SLOT);
+ __ LoadRoot(v0, Heap::kInstanceofCacheAnswerRootIndex); // In delay slot.
+
+ // 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);
+ __ GetObjectType(function, function_map, scratch);
+ __ Branch(&slow_case, ne, scratch, Operand(JS_FUNCTION_TYPE));
+ __ Ret(USE_DELAY_SLOT);
+ __ LoadRoot(v0, Heap::kFalseValueRootIndex); // In delay slot.
- // 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()) {
- __ StoreRoot(function, Heap::kInstanceofCacheFunctionRootIndex);
- __ StoreRoot(map, Heap::kInstanceofCacheMapRootIndex);
- } else {
- DCHECK(HasArgsInRegisters());
- // Patch the (relocated) inlined map check.
-
- // The offset was stored in t0 safepoint slot.
- // (See LCodeGen::DoDeferredLInstanceOfKnownGlobal).
- __ LoadFromSafepointRegisterSlot(scratch, t0);
- __ Subu(inline_site, ra, scratch);
- // Get the map location in scratch and patch it.
- __ GetRelocatedValue(inline_site, scratch, v1); // v1 used as scratch.
- __ sw(map, FieldMemOperand(scratch, Cell::kValueOffset));
-
- __ mov(t4, map);
- // |scratch| points at the beginning of the cell. Calculate the field
- // containing the map.
- __ Addu(function, scratch, Operand(Cell::kValueOffset - 1));
- __ RecordWriteField(scratch, Cell::kValueOffset, t4, function,
- kRAHasNotBeenSaved, kDontSaveFPRegs,
- OMIT_REMEMBERED_SET, OMIT_SMI_CHECK);
- }
+ // Fast-case: The {function} must be a valid JSFunction.
+ __ bind(&fast_case);
+ __ JumpIfSmi(function, &slow_case);
+ __ GetObjectType(function, function_map, scratch);
+ __ Branch(&slow_case, ne, scratch, Operand(JS_FUNCTION_TYPE));
- // Register mapping: a3 is object map and t0 is function prototype.
- // Get prototype of object into a2.
- __ lw(scratch, FieldMemOperand(map, Map::kPrototypeOffset));
+ // Ensure that {function} has an instance prototype.
+ __ lbu(scratch, FieldMemOperand(function_map, Map::kBitFieldOffset));
+ __ And(at, scratch, Operand(1 << Map::kHasNonInstancePrototype));
+ __ Branch(&slow_case, ne, at, Operand(zero_reg));
- // We don't need map any more. Use it as a scratch register.
- Register scratch2 = map;
- map = no_reg;
+ // Ensure that {function} is not bound.
+ Register const shared_info = scratch;
+ __ lw(shared_info,
+ FieldMemOperand(function, JSFunction::kSharedFunctionInfoOffset));
+ __ lbu(scratch,
+ FieldMemOperand(shared_info, SharedFunctionInfo::kBoundByteOffset));
+ __ And(at, scratch, Operand(1 << SharedFunctionInfo::kBoundBitWithinByte));
+ __ Branch(&slow_case, ne, at, Operand(zero_reg));
- // Loop through the prototype chain looking for the function prototype.
- __ LoadRoot(scratch2, Heap::kNullValueRootIndex);
+ // Get the "prototype" (or initial map) of the {function}.
+ __ lw(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;
+ __ GetObjectType(function_prototype, scratch, scratch);
+ __ Branch(&function_prototype_valid, ne, scratch, Operand(MAP_TYPE));
+ __ lw(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(v0, Heap::kTrueValueRootIndex);
+ __ LoadRoot(null, Heap::kNullValueRootIndex);
__ bind(&loop);
- __ Branch(&is_instance, eq, scratch, Operand(prototype));
- __ Branch(&is_not_instance, eq, scratch, Operand(scratch2));
- __ lw(scratch, FieldMemOperand(scratch, HeapObject::kMapOffset));
- __ lw(scratch, FieldMemOperand(scratch, Map::kPrototypeOffset));
- __ Branch(&loop);
-
- __ bind(&is_instance);
- DCHECK_EQ(static_cast<Smi*>(0), Smi::FromInt(0));
- if (!HasCallSiteInlineCheck()) {
- __ mov(v0, zero_reg);
- __ StoreRoot(v0, Heap::kInstanceofCacheAnswerRootIndex);
- if (ReturnTrueFalseObject()) {
- __ LoadRoot(v0, Heap::kTrueValueRootIndex);
- }
- } else {
- // Patch the call site to return true.
- __ LoadRoot(v0, Heap::kTrueValueRootIndex);
- __ Addu(inline_site, inline_site, Operand(kDeltaToLoadBoolResult));
- // Get the boolean result location in scratch and patch it.
- __ PatchRelocatedValue(inline_site, scratch, v0);
-
- if (!ReturnTrueFalseObject()) {
- __ mov(v0, zero_reg);
- }
- }
- __ DropAndRet(HasArgsInRegisters() ? 0 : 2);
-
- __ bind(&is_not_instance);
- if (!HasCallSiteInlineCheck()) {
- __ li(v0, Operand(Smi::FromInt(1)));
- __ StoreRoot(v0, Heap::kInstanceofCacheAnswerRootIndex);
- if (ReturnTrueFalseObject()) {
- __ LoadRoot(v0, Heap::kFalseValueRootIndex);
- }
- } else {
- // Patch the call site to return false.
- __ LoadRoot(v0, Heap::kFalseValueRootIndex);
- __ Addu(inline_site, inline_site, Operand(kDeltaToLoadBoolResult));
- // Get the boolean result location in scratch and patch it.
- __ PatchRelocatedValue(inline_site, scratch, v0);
-
- if (!ReturnTrueFalseObject()) {
- __ li(v0, Operand(Smi::FromInt(1)));
- }
- }
-
- __ DropAndRet(HasArgsInRegisters() ? 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.
- __ JumpIfSmi(function, &slow);
- __ GetObjectType(function, scratch2, scratch);
- __ Branch(&slow, ne, scratch, Operand(JS_FUNCTION_TYPE));
-
- // Null is not instance of anything.
- __ Branch(&object_not_null, ne, object,
- Operand(isolate()->factory()->null_value()));
- if (ReturnTrueFalseObject()) {
- __ LoadRoot(v0, Heap::kFalseValueRootIndex);
- } else {
- __ li(v0, Operand(Smi::FromInt(1)));
- }
- __ DropAndRet(HasArgsInRegisters() ? 0 : 2);
-
- __ bind(&object_not_null);
- // Smi values are not instances of anything.
- __ JumpIfNotSmi(object, &object_not_null_or_smi);
- if (ReturnTrueFalseObject()) {
- __ LoadRoot(v0, Heap::kFalseValueRootIndex);
- } else {
- __ li(v0, Operand(Smi::FromInt(1)));
- }
- __ DropAndRet(HasArgsInRegisters() ? 0 : 2);
-
- __ bind(&object_not_null_or_smi);
- // String values are not instances of anything.
- __ IsObjectJSStringType(object, scratch, &slow);
- if (ReturnTrueFalseObject()) {
- __ LoadRoot(v0, Heap::kFalseValueRootIndex);
- } else {
- __ li(v0, Operand(Smi::FromInt(1)));
- }
- __ DropAndRet(HasArgsInRegisters() ? 0 : 2);
+ __ lw(object_prototype, FieldMemOperand(object_map, Map::kPrototypeOffset));
+ __ Branch(&done, eq, object_prototype, Operand(function_prototype));
+ __ Branch(USE_DELAY_SLOT, &loop, ne, object_prototype, Operand(null));
+ __ lw(object_map, FieldMemOperand(object_prototype, HeapObject::kMapOffset));
+ __ LoadRoot(v0, Heap::kFalseValueRootIndex);
+ __ bind(&done);
+ __ Ret(USE_DELAY_SLOT);
+ __ StoreRoot(v0, Heap::kInstanceofCacheAnswerRootIndex); // In delay slot.
- // Slow-case. Tail call builtin.
- __ bind(&slow);
- if (!ReturnTrueFalseObject()) {
- if (HasArgsInRegisters()) {
- __ Push(a0, a1);
- }
- __ InvokeBuiltin(Builtins::INSTANCE_OF, JUMP_FUNCTION);
- } else {
- {
- FrameScope scope(masm, StackFrame::INTERNAL);
- __ Push(a0, a1);
- __ InvokeBuiltin(Builtins::INSTANCE_OF, CALL_FUNCTION);
- }
- __ mov(a0, v0);
- __ LoadRoot(v0, Heap::kTrueValueRootIndex);
- __ DropAndRet(HasArgsInRegisters() ? 0 : 2, eq, a0, Operand(zero_reg));
- __ LoadRoot(v0, Heap::kFalseValueRootIndex);
- __ DropAndRet(HasArgsInRegisters() ? 0 : 2);
- }
+ // Slow-case: Call the runtime function.
+ __ bind(&slow_case);
+ __ Push(object, function);
+ __ TailCallRuntime(Runtime::kInstanceOf, 2, 1);
}
const Register StoreGlobalViaContextDescriptor::ValueRegister() { return a0; }
-const Register InstanceofDescriptor::left() { return a0; }
-const Register InstanceofDescriptor::right() { return a1; }
+const Register InstanceOfDescriptor::LeftRegister() { return a1; }
+const Register InstanceOfDescriptor::RightRegister() { return a0; }
const Register ArgumentsAccessReadDescriptor::index() { return a1; }
}
-template<class InstrType>
-void LCodeGen::EmitFalseBranch(InstrType instr,
- Condition condition,
- Register src1,
- const Operand& src2) {
+template <class InstrType>
+void LCodeGen::EmitTrueBranch(InstrType instr, Condition condition,
+ Register src1, const Operand& src2) {
+ int true_block = instr->TrueDestination(chunk_);
+ __ Branch(chunk_->GetAssemblyLabel(true_block), condition, src1, src2);
+}
+
+
+template <class InstrType>
+void LCodeGen::EmitFalseBranch(InstrType instr, Condition condition,
+ Register src1, const Operand& src2) {
int false_block = instr->FalseDestination(chunk_);
__ Branch(chunk_->GetAssemblyLabel(false_block), condition, src1, src2);
}
void LCodeGen::DoInstanceOf(LInstanceOf* instr) {
DCHECK(ToRegister(instr->context()).is(cp));
- Label true_label, done;
- DCHECK(ToRegister(instr->left()).is(a0)); // Object is in a0.
- DCHECK(ToRegister(instr->right()).is(a1)); // Function is in a1.
- Register result = ToRegister(instr->result());
- DCHECK(result.is(v0));
-
- InstanceofStub stub(isolate(), InstanceofStub::kArgsInRegisters);
+ DCHECK(ToRegister(instr->left()).is(InstanceOfDescriptor::LeftRegister()));
+ DCHECK(ToRegister(instr->right()).is(InstanceOfDescriptor::RightRegister()));
+ DCHECK(ToRegister(instr->result()).is(v0));
+ InstanceOfStub stub(isolate());
CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
-
- __ Branch(&true_label, eq, result, Operand(zero_reg));
- __ li(result, Operand(factory()->false_value()));
- __ Branch(&done);
- __ bind(&true_label);
- __ li(result, Operand(factory()->true_value()));
- __ bind(&done);
}
-void LCodeGen::DoInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr) {
- class DeferredInstanceOfKnownGlobal final : public LDeferredCode {
- public:
- DeferredInstanceOfKnownGlobal(LCodeGen* codegen,
- LInstanceOfKnownGlobal* instr)
- : LDeferredCode(codegen), instr_(instr) { }
- void Generate() override {
- codegen()->DoDeferredInstanceOfKnownGlobal(instr_, &map_check_);
- }
- LInstruction* instr() override { return instr_; }
- Label* map_check() { return &map_check_; }
-
- private:
- LInstanceOfKnownGlobal* instr_;
- Label map_check_;
- };
-
- DeferredInstanceOfKnownGlobal* deferred;
- deferred = new(zone()) DeferredInstanceOfKnownGlobal(this, instr);
-
- Label done, false_result;
- Register object = ToRegister(instr->value());
- Register temp = ToRegister(instr->temp());
- Register result = ToRegister(instr->result());
-
- DCHECK(object.is(a0));
- DCHECK(result.is(v0));
+void LCodeGen::DoHasInPrototypeChainAndBranch(
+ LHasInPrototypeChainAndBranch* instr) {
+ Register const object = ToRegister(instr->object());
+ Register const object_map = scratch0();
+ Register const object_prototype = object_map;
+ Register const prototype = ToRegister(instr->prototype());
- // A Smi is not instance of anything.
- __ JumpIfSmi(object, &false_result);
-
- // This is the inlined call site instanceof cache. The two occurences of the
- // hole value will be patched to the last map/result pair generated by the
- // instanceof stub.
- Label cache_miss;
- Register map = temp;
- __ lw(map, FieldMemOperand(object, HeapObject::kMapOffset));
-
- Assembler::BlockTrampolinePoolScope block_trampoline_pool(masm_);
- __ bind(deferred->map_check()); // Label for calculating code patching.
- // We use Factory::the_hole_value() on purpose instead of loading from the
- // root array to force relocation to be able to later patch with
- // the cached map.
- Handle<Cell> cell = factory()->NewCell(factory()->the_hole_value());
- __ li(at, Operand(cell));
- __ lw(at, FieldMemOperand(at, Cell::kValueOffset));
- __ BranchShort(&cache_miss, ne, map, Operand(at));
- // We use Factory::the_hole_value() on purpose instead of loading from the
- // root array to force relocation to be able to later patch
- // with true or false. The distance from map check has to be constant.
- __ li(result, Operand(factory()->the_hole_value()), CONSTANT_SIZE);
- __ Branch(&done);
-
- // The inlined call site cache did not match. Check null and string before
- // calling the deferred code.
- __ bind(&cache_miss);
- // Null is not instance of anything.
- __ LoadRoot(temp, Heap::kNullValueRootIndex);
- __ Branch(&false_result, eq, object, Operand(temp));
-
- // String values is not instance of anything.
- Condition cc = __ IsObjectStringType(object, temp, temp);
- __ Branch(&false_result, cc, temp, Operand(zero_reg));
-
- // Go to the deferred code.
- __ Branch(deferred->entry());
-
- __ bind(&false_result);
- __ LoadRoot(result, Heap::kFalseValueRootIndex);
-
- // Here result has either true or false. Deferred code also produces true or
- // false object.
- __ bind(deferred->exit());
- __ bind(&done);
-}
-
-
-void LCodeGen::DoDeferredInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr,
- Label* map_check) {
- Register result = ToRegister(instr->result());
- DCHECK(result.is(v0));
-
- InstanceofStub::Flags flags = InstanceofStub::kNoFlags;
- flags = static_cast<InstanceofStub::Flags>(
- flags | InstanceofStub::kArgsInRegisters);
- flags = static_cast<InstanceofStub::Flags>(
- flags | InstanceofStub::kCallSiteInlineCheck);
- flags = static_cast<InstanceofStub::Flags>(
- flags | InstanceofStub::kReturnTrueFalseObject);
- InstanceofStub stub(isolate(), flags);
-
- PushSafepointRegistersScope scope(this);
- LoadContextFromDeferred(instr->context());
-
- // Get the temp register reserved by the instruction. This needs to be t0 as
- // its slot of the pushing of safepoint registers is used to communicate the
- // offset to the location of the map check.
- Register temp = ToRegister(instr->temp());
- DCHECK(temp.is(t0));
- __ li(InstanceofStub::right(), instr->function());
- static const int kAdditionalDelta = 7;
- int delta = masm_->InstructionsGeneratedSince(map_check) + kAdditionalDelta;
- Label before_push_delta;
- __ bind(&before_push_delta);
- {
- Assembler::BlockTrampolinePoolScope block_trampoline_pool(masm_);
- __ li(temp, Operand(delta * kPointerSize), CONSTANT_SIZE);
- __ StoreToSafepointRegisterSlot(temp, temp);
- }
- CallCodeGeneric(stub.GetCode(),
- RelocInfo::CODE_TARGET,
- instr,
- RECORD_SAFEPOINT_WITH_REGISTERS_AND_NO_ARGUMENTS);
- LEnvironment* env = instr->GetDeferredLazyDeoptimizationEnvironment();
- safepoints_.RecordLazyDeoptimizationIndex(env->deoptimization_index());
- // Put the result value into the result register slot and
- // restore all registers.
- __ StoreToSafepointRegisterSlot(result, result);
+ // The {object} must be a spec object. It's sufficient to know that {object}
+ // is not a smi, since all other non-spec objects have {null} prototypes and
+ // will be ruled out below.
+ if (instr->hydrogen()->ObjectNeedsSmiCheck()) {
+ __ SmiTst(object, at);
+ EmitFalseBranch(instr, eq, at, Operand(zero_reg));
+ }
+ // Loop through the {object}s prototype chain looking for the {prototype}.
+ __ lw(object_map, FieldMemOperand(object, HeapObject::kMapOffset));
+ Label loop;
+ __ bind(&loop);
+ __ lw(object_prototype, FieldMemOperand(object_map, Map::kPrototypeOffset));
+ EmitTrueBranch(instr, eq, object_prototype, Operand(prototype));
+ __ LoadRoot(at, Heap::kNullValueRootIndex);
+ EmitFalseBranch(instr, eq, object_prototype, Operand(at));
+ __ Branch(USE_DELAY_SLOT, &loop);
+ __ lw(object_map, FieldMemOperand(object_prototype, HeapObject::kMapOffset));
}
void DoDeferredStringCharCodeAt(LStringCharCodeAt* instr);
void DoDeferredStringCharFromCode(LStringCharFromCode* instr);
void DoDeferredAllocate(LAllocate* instr);
- void DoDeferredInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr,
- Label* map_check);
-
void DoDeferredInstanceMigration(LCheckMaps* instr, Register object);
void DoDeferredLoadMutableDouble(LLoadFieldByIndex* instr,
Register result,
Condition condition,
FPURegister src1,
FPURegister src2);
- template<class InstrType>
- void EmitFalseBranch(InstrType instr,
- Condition condition,
- Register src1,
+ template <class InstrType>
+ void EmitTrueBranch(InstrType instr, Condition condition, Register src1,
+ const Operand& src2);
+ template <class InstrType>
+ void EmitFalseBranch(InstrType instr, Condition condition, Register src1,
const Operand& src2);
template<class InstrType>
void EmitFalseBranchF(InstrType instr,
if (instr->IsCall()) {
HValue* hydrogen_value_for_lazy_bailout = hydrogen_val;
- LInstruction* instruction_needing_environment = NULL;
if (hydrogen_val->HasObservableSideEffects()) {
HSimulate* sim = HSimulate::cast(hydrogen_val->next());
- instruction_needing_environment = instr;
sim->ReplayEnvironment(current_block_->last_environment());
hydrogen_value_for_lazy_bailout = sim;
}
LInstruction* bailout = AssignEnvironment(new(zone()) LLazyBailout());
bailout->set_hydrogen_value(hydrogen_value_for_lazy_bailout);
chunk_->AddInstruction(bailout, current_block_);
- if (instruction_needing_environment != NULL) {
- // Store the lazy deopt environment with the instruction if needed.
- // Right now it is only used for LInstanceOfKnownGlobal.
- instruction_needing_environment->
- SetDeferredLazyDeoptimizationEnvironment(bailout->environment());
- }
}
}
LInstruction* LChunkBuilder::DoInstanceOf(HInstanceOf* instr) {
+ LOperand* left =
+ UseFixed(instr->left(), InstanceOfDescriptor::LeftRegister());
+ LOperand* right =
+ UseFixed(instr->right(), InstanceOfDescriptor::RightRegister());
LOperand* context = UseFixed(instr->context(), cp);
- LInstanceOf* result =
- new(zone()) LInstanceOf(context, UseFixed(instr->left(), a0),
- UseFixed(instr->right(), a1));
+ LInstanceOf* result = new (zone()) LInstanceOf(context, left, right);
return MarkAsCall(DefineFixed(result, v0), instr);
}
-LInstruction* LChunkBuilder::DoInstanceOfKnownGlobal(
- HInstanceOfKnownGlobal* instr) {
- LInstanceOfKnownGlobal* result =
- new(zone()) LInstanceOfKnownGlobal(
- UseFixed(instr->context(), cp),
- UseFixed(instr->left(), a0),
- FixedTemp(t0));
- return MarkAsCall(DefineFixed(result, v0), instr);
+LInstruction* LChunkBuilder::DoHasInPrototypeChainAndBranch(
+ HHasInPrototypeChainAndBranch* instr) {
+ LOperand* object = UseRegister(instr->object());
+ LOperand* prototype = UseRegister(instr->prototype());
+ return new (zone()) LHasInPrototypeChainAndBranch(object, prototype);
}
V(GetCachedArrayIndex) \
V(Goto) \
V(HasCachedArrayIndexAndBranch) \
+ V(HasInPrototypeChainAndBranch) \
V(HasInstanceTypeAndBranch) \
V(InnerAllocatedObject) \
V(InstanceOf) \
- V(InstanceOfKnownGlobal) \
V(InstructionGap) \
V(Integer32ToDouble) \
V(InvokeFunction) \
void set_hydrogen_value(HValue* value) { hydrogen_value_ = value; }
HValue* hydrogen_value() const { return hydrogen_value_; }
- virtual void SetDeferredLazyDeoptimizationEnvironment(LEnvironment* env) { }
-
void MarkAsCall() { bit_field_ = IsCallBits::update(bit_field_, true); }
bool IsCall() const { return IsCallBits::decode(bit_field_); }
inputs_[2] = right;
}
- LOperand* context() { return inputs_[0]; }
- LOperand* left() { return inputs_[1]; }
- LOperand* right() { return inputs_[2]; }
+ LOperand* context() const { return inputs_[0]; }
+ LOperand* left() const { return inputs_[1]; }
+ LOperand* right() const { return inputs_[2]; }
DECLARE_CONCRETE_INSTRUCTION(InstanceOf, "instance-of")
};
-class LInstanceOfKnownGlobal final : public LTemplateInstruction<1, 2, 1> {
+class LHasInPrototypeChainAndBranch final : public LControlInstruction<2, 0> {
public:
- LInstanceOfKnownGlobal(LOperand* context, LOperand* value, LOperand* temp) {
- inputs_[0] = context;
- inputs_[1] = value;
- temps_[0] = temp;
+ LHasInPrototypeChainAndBranch(LOperand* object, LOperand* prototype) {
+ inputs_[0] = object;
+ inputs_[1] = prototype;
}
- LOperand* context() { return inputs_[0]; }
- LOperand* value() { return inputs_[1]; }
- LOperand* temp() { return temps_[0]; }
-
- DECLARE_CONCRETE_INSTRUCTION(InstanceOfKnownGlobal,
- "instance-of-known-global")
- DECLARE_HYDROGEN_ACCESSOR(InstanceOfKnownGlobal)
+ LOperand* object() const { return inputs_[0]; }
+ LOperand* prototype() const { return inputs_[1]; }
- Handle<JSFunction> function() const { return hydrogen()->function(); }
- LEnvironment* GetDeferredLazyDeoptimizationEnvironment() {
- return lazy_deopt_env_;
- }
- virtual void SetDeferredLazyDeoptimizationEnvironment(
- LEnvironment* env) override {
- lazy_deopt_env_ = env;
- }
-
- private:
- LEnvironment* lazy_deopt_env_;
+ DECLARE_CONCRETE_INSTRUCTION(HasInPrototypeChainAndBranch,
+ "has-in-prototype-chain-and-branch")
+ DECLARE_HYDROGEN_ACCESSOR(HasInPrototypeChainAndBranch)
};
}
-void MacroAssembler::TryGetFunctionPrototype(Register function,
- Register result,
- Register scratch,
- Label* miss,
- bool miss_on_bound_function) {
- Label non_instance;
- if (miss_on_bound_function) {
- // Check that the receiver isn't a smi.
- JumpIfSmi(function, miss);
-
- // Check that the function really is a function. Load map into result reg.
- GetObjectType(function, result, scratch);
- Branch(miss, ne, scratch, Operand(JS_FUNCTION_TYPE));
-
- lw(scratch,
- FieldMemOperand(function, JSFunction::kSharedFunctionInfoOffset));
- lw(scratch,
- FieldMemOperand(scratch, SharedFunctionInfo::kCompilerHintsOffset));
- And(scratch, scratch,
- Operand(Smi::FromInt(1 << SharedFunctionInfo::kBoundFunction)));
- Branch(miss, ne, scratch, Operand(zero_reg));
-
- // Make sure that the function has an instance prototype.
- lbu(scratch, FieldMemOperand(result, Map::kBitFieldOffset));
- And(scratch, scratch, Operand(1 << Map::kHasNonInstancePrototype));
- Branch(&non_instance, ne, scratch, Operand(zero_reg));
- }
-
+void MacroAssembler::TryGetFunctionPrototype(Register function, Register result,
+ Register scratch, Label* miss) {
// Get the prototype or initial map from the function.
lw(result,
FieldMemOperand(function, JSFunction::kPrototypeOrInitialMapOffset));
// Get the prototype from the initial map.
lw(result, FieldMemOperand(result, Map::kPrototypeOffset));
- if (miss_on_bound_function) {
- jmp(&done);
-
- // Non-instance prototype: Fetch prototype from constructor field
- // in initial map.
- bind(&non_instance);
- GetMapConstructor(result, result, scratch, scratch);
- }
-
// All done.
bind(&done);
}
#undef BRANCH_ARGS_CHECK
-void MacroAssembler::PatchRelocatedValue(Register li_location,
- Register scratch,
- Register new_value) {
- lw(scratch, MemOperand(li_location));
- // At this point scratch is a lui(at, ...) instruction.
- if (emit_debug_code()) {
- And(scratch, scratch, kOpcodeMask);
- Check(eq, kTheInstructionToPatchShouldBeALui,
- scratch, Operand(LUI));
- lw(scratch, MemOperand(li_location));
- }
- srl(t9, new_value, kImm16Bits);
- Ins(scratch, t9, 0, kImm16Bits);
- sw(scratch, MemOperand(li_location));
-
- lw(scratch, MemOperand(li_location, kInstrSize));
- // scratch is now ori(at, ...).
- if (emit_debug_code()) {
- And(scratch, scratch, kOpcodeMask);
- Check(eq, kTheInstructionToPatchShouldBeAnOri,
- scratch, Operand(ORI));
- lw(scratch, MemOperand(li_location, kInstrSize));
- }
- Ins(scratch, new_value, 0, kImm16Bits);
- sw(scratch, MemOperand(li_location, kInstrSize));
-
- // Update the I-cache so the new lui and ori can be executed.
- FlushICache(li_location, 2);
-}
-
-void MacroAssembler::GetRelocatedValue(Register li_location,
- Register value,
- Register scratch) {
- lw(value, MemOperand(li_location));
- if (emit_debug_code()) {
- And(value, value, kOpcodeMask);
- Check(eq, kTheInstructionShouldBeALui,
- value, Operand(LUI));
- lw(value, MemOperand(li_location));
- }
-
- // value now holds a lui instruction. Extract the immediate.
- sll(value, value, kImm16Bits);
-
- lw(scratch, MemOperand(li_location, kInstrSize));
- if (emit_debug_code()) {
- And(scratch, scratch, kOpcodeMask);
- Check(eq, kTheInstructionShouldBeAnOri,
- scratch, Operand(ORI));
- lw(scratch, MemOperand(li_location, kInstrSize));
- }
- // "scratch" now holds an ori instruction. Extract the immediate.
- andi(scratch, scratch, kImm16Mask);
-
- // Merge the results.
- or_(value, value, scratch);
-}
-
-
void MacroAssembler::CheckPageFlag(
Register object,
Register scratch,
// function and jumps to the miss label if the fast checks fail. The
// function register will be untouched; the other registers may be
// clobbered.
- void TryGetFunctionPrototype(Register function,
- Register result,
- Register scratch,
- Label* miss,
- bool miss_on_bound_function = false);
+ void TryGetFunctionPrototype(Register function, Register result,
+ Register scratch, Label* miss);
void GetObjectType(Register function,
Register map,
void EnterFrame(StackFrame::Type type, bool load_constant_pool_pointer_reg);
void LeaveFrame(StackFrame::Type type);
- // Patch the relocated value (lui/ori pair).
- void PatchRelocatedValue(Register li_location,
- Register scratch,
- Register new_value);
- // Get the relocatad value (loaded data) from the lui/ori pair.
- void GetRelocatedValue(Register li_location,
- Register value,
- Register scratch);
-
// Expects object in a0 and returns map with validated enum cache
// in a0. Assumes that any other register can be used as a scratch.
void CheckEnumCache(Register null_value, Label* call_runtime);
}
-// Uses registers a0 to a4.
-// Expected input (depending on whether args are in registers or on the stack):
-// * object: a0 or at sp + 1 * kPointerSize.
-// * function: a1 or at sp.
-//
-// An inlined call site may have been generated before calling this stub.
-// In this case the offset to the inline site to patch is passed on the stack,
-// in the safepoint slot for register a4.
-void InstanceofStub::Generate(MacroAssembler* masm) {
- // Call site inlining and patching implies arguments in registers.
- DCHECK(HasArgsInRegisters() || !HasCallSiteInlineCheck());
-
- // Fixed register usage throughout the stub:
- const Register object = a0; // Object (lhs).
- Register map = a3; // Map of the object.
- const Register function = a1; // Function (rhs).
- const Register prototype = a4; // Prototype of the function.
- const Register inline_site = t1;
- const Register scratch = a2;
-
- const int32_t kDeltaToLoadBoolResult = 7 * Assembler::kInstrSize;
-
- Label slow, loop, is_instance, is_not_instance, not_js_object;
-
- if (!HasArgsInRegisters()) {
- __ ld(object, MemOperand(sp, 1 * kPointerSize));
- __ ld(function, MemOperand(sp, 0));
- }
-
- // Check that the left hand is a JS object and load map.
- __ JumpIfSmi(object, ¬_js_object);
- __ IsObjectJSObjectType(object, map, scratch, ¬_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;
- __ LoadRoot(at, Heap::kInstanceofCacheFunctionRootIndex);
- __ Branch(&miss, ne, function, Operand(at));
- __ LoadRoot(at, Heap::kInstanceofCacheMapRootIndex);
- __ Branch(&miss, ne, map, Operand(at));
- __ LoadRoot(v0, Heap::kInstanceofCacheAnswerRootIndex);
- __ DropAndRet(HasArgsInRegisters() ? 0 : 2);
-
- __ bind(&miss);
- }
-
- // Get the prototype of the function.
- __ TryGetFunctionPrototype(function, prototype, scratch, &slow, true);
-
- // Check that the function prototype is a JS object.
- __ JumpIfSmi(prototype, &slow);
- __ IsObjectJSObjectType(prototype, scratch, scratch, &slow);
+void InstanceOfStub::Generate(MacroAssembler* masm) {
+ Register const object = a1; // Object (lhs).
+ Register const function = a0; // Function (rhs).
+ Register const object_map = a2; // Map of {object}.
+ Register const function_map = a3; // Map of {function}.
+ Register const function_prototype = a4; // Prototype of {function}.
+ Register const scratch = a5;
+
+ 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;
+ __ ld(object_map, FieldMemOperand(object, HeapObject::kMapOffset));
+ __ LoadRoot(at, Heap::kInstanceofCacheFunctionRootIndex);
+ __ Branch(&fast_case, ne, function, Operand(at));
+ __ LoadRoot(at, Heap::kInstanceofCacheMapRootIndex);
+ __ Branch(&fast_case, ne, object_map, Operand(at));
+ __ Ret(USE_DELAY_SLOT);
+ __ LoadRoot(v0, Heap::kInstanceofCacheAnswerRootIndex); // In delay slot.
+
+ // 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);
+ __ GetObjectType(function, function_map, scratch);
+ __ Branch(&slow_case, ne, scratch, Operand(JS_FUNCTION_TYPE));
+ __ Ret(USE_DELAY_SLOT);
+ __ LoadRoot(v0, Heap::kFalseValueRootIndex); // In delay slot.
- // 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()) {
- __ StoreRoot(function, Heap::kInstanceofCacheFunctionRootIndex);
- __ StoreRoot(map, Heap::kInstanceofCacheMapRootIndex);
- } else {
- DCHECK(HasArgsInRegisters());
- // Patch the (relocated) inlined map check.
-
- // The offset was stored in a4 safepoint slot.
- // (See LCodeGen::DoDeferredLInstanceOfKnownGlobal).
- __ LoadFromSafepointRegisterSlot(scratch, a4);
- __ Dsubu(inline_site, ra, scratch);
- // Get the map location in scratch and patch it.
- __ GetRelocatedValue(inline_site, scratch, v1); // v1 used as scratch.
- __ sd(map, FieldMemOperand(scratch, Cell::kValueOffset));
-
- __ mov(t0, map);
- // |scratch| points at the beginning of the cell. Calculate the
- // field containing the map.
- __ Daddu(function, scratch, Operand(Cell::kValueOffset - 1));
- __ RecordWriteField(scratch, Cell::kValueOffset, t0, function,
- kRAHasNotBeenSaved, kDontSaveFPRegs,
- OMIT_REMEMBERED_SET, OMIT_SMI_CHECK);
- }
+ // Fast-case: The {function} must be a valid JSFunction.
+ __ bind(&fast_case);
+ __ JumpIfSmi(function, &slow_case);
+ __ GetObjectType(function, function_map, scratch);
+ __ Branch(&slow_case, ne, scratch, Operand(JS_FUNCTION_TYPE));
- // Register mapping: a3 is object map and a4 is function prototype.
- // Get prototype of object into a2.
- __ ld(scratch, FieldMemOperand(map, Map::kPrototypeOffset));
+ // Ensure that {function} has an instance prototype.
+ __ lbu(scratch, FieldMemOperand(function_map, Map::kBitFieldOffset));
+ __ And(at, scratch, Operand(1 << Map::kHasNonInstancePrototype));
+ __ Branch(&slow_case, ne, at, Operand(zero_reg));
- // We don't need map any more. Use it as a scratch register.
- Register scratch2 = map;
- map = no_reg;
+ // Ensure that {function} is not bound.
+ Register const shared_info = scratch;
+ __ ld(shared_info,
+ FieldMemOperand(function, JSFunction::kSharedFunctionInfoOffset));
+ __ lbu(scratch,
+ FieldMemOperand(shared_info, SharedFunctionInfo::kBoundByteOffset));
+ __ And(at, scratch, Operand(1 << SharedFunctionInfo::kBoundBitWithinByte));
+ __ Branch(&slow_case, ne, at, Operand(zero_reg));
- // Loop through the prototype chain looking for the function prototype.
- __ LoadRoot(scratch2, Heap::kNullValueRootIndex);
+ // Get the "prototype" (or initial map) of the {function}.
+ __ ld(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;
+ __ GetObjectType(function_prototype, scratch, scratch);
+ __ Branch(&function_prototype_valid, ne, scratch, Operand(MAP_TYPE));
+ __ ld(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(v0, Heap::kTrueValueRootIndex);
+ __ LoadRoot(null, Heap::kNullValueRootIndex);
__ bind(&loop);
- __ Branch(&is_instance, eq, scratch, Operand(prototype));
- __ Branch(&is_not_instance, eq, scratch, Operand(scratch2));
- __ ld(scratch, FieldMemOperand(scratch, HeapObject::kMapOffset));
- __ ld(scratch, FieldMemOperand(scratch, Map::kPrototypeOffset));
- __ Branch(&loop);
-
- __ bind(&is_instance);
- DCHECK_EQ(static_cast<Smi*>(0), Smi::FromInt(0));
- if (!HasCallSiteInlineCheck()) {
- __ mov(v0, zero_reg);
- __ StoreRoot(v0, Heap::kInstanceofCacheAnswerRootIndex);
- if (ReturnTrueFalseObject()) {
- __ LoadRoot(v0, Heap::kTrueValueRootIndex);
- }
- } else {
- // Patch the call site to return true.
- __ LoadRoot(v0, Heap::kTrueValueRootIndex);
- __ Daddu(inline_site, inline_site, Operand(kDeltaToLoadBoolResult));
- // Get the boolean result location in scratch and patch it.
- __ PatchRelocatedValue(inline_site, scratch, v0);
-
- if (!ReturnTrueFalseObject()) {
- __ mov(v0, zero_reg);
- }
- }
- __ DropAndRet(HasArgsInRegisters() ? 0 : 2);
-
- __ bind(&is_not_instance);
- if (!HasCallSiteInlineCheck()) {
- __ li(v0, Operand(Smi::FromInt(1)));
- __ StoreRoot(v0, Heap::kInstanceofCacheAnswerRootIndex);
- if (ReturnTrueFalseObject()) {
- __ LoadRoot(v0, Heap::kFalseValueRootIndex);
- }
- } else {
- // Patch the call site to return false.
- __ LoadRoot(v0, Heap::kFalseValueRootIndex);
- __ Daddu(inline_site, inline_site, Operand(kDeltaToLoadBoolResult));
- // Get the boolean result location in scratch and patch it.
- __ PatchRelocatedValue(inline_site, scratch, v0);
-
- if (!ReturnTrueFalseObject()) {
- __ li(v0, Operand(Smi::FromInt(1)));
- }
- }
-
- __ DropAndRet(HasArgsInRegisters() ? 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.
- __ JumpIfSmi(function, &slow);
- __ GetObjectType(function, scratch2, scratch);
- __ Branch(&slow, ne, scratch, Operand(JS_FUNCTION_TYPE));
-
- // Null is not instance of anything.
- __ Branch(&object_not_null, ne, object,
- Operand(isolate()->factory()->null_value()));
- if (ReturnTrueFalseObject()) {
- __ LoadRoot(v0, Heap::kFalseValueRootIndex);
- } else {
- __ li(v0, Operand(Smi::FromInt(1)));
- }
- __ DropAndRet(HasArgsInRegisters() ? 0 : 2);
-
- __ bind(&object_not_null);
- // Smi values are not instances of anything.
- __ JumpIfNotSmi(object, &object_not_null_or_smi);
- if (ReturnTrueFalseObject()) {
- __ LoadRoot(v0, Heap::kFalseValueRootIndex);
- } else {
- __ li(v0, Operand(Smi::FromInt(1)));
- }
- __ DropAndRet(HasArgsInRegisters() ? 0 : 2);
-
- __ bind(&object_not_null_or_smi);
- // String values are not instances of anything.
- __ IsObjectJSStringType(object, scratch, &slow);
- if (ReturnTrueFalseObject()) {
- __ LoadRoot(v0, Heap::kFalseValueRootIndex);
- } else {
- __ li(v0, Operand(Smi::FromInt(1)));
- }
- __ DropAndRet(HasArgsInRegisters() ? 0 : 2);
+ __ ld(object_prototype, FieldMemOperand(object_map, Map::kPrototypeOffset));
+ __ Branch(&done, eq, object_prototype, Operand(function_prototype));
+ __ Branch(USE_DELAY_SLOT, &loop, ne, object_prototype, Operand(null));
+ __ ld(object_map, FieldMemOperand(object_prototype, HeapObject::kMapOffset));
+ __ LoadRoot(v0, Heap::kFalseValueRootIndex);
+ __ bind(&done);
+ __ Ret(USE_DELAY_SLOT);
+ __ StoreRoot(v0, Heap::kInstanceofCacheAnswerRootIndex); // In delay slot.
- // Slow-case. Tail call builtin.
- __ bind(&slow);
- if (!ReturnTrueFalseObject()) {
- if (HasArgsInRegisters()) {
- __ Push(a0, a1);
- }
- __ InvokeBuiltin(Builtins::INSTANCE_OF, JUMP_FUNCTION);
- } else {
- {
- FrameScope scope(masm, StackFrame::INTERNAL);
- __ Push(a0, a1);
- __ InvokeBuiltin(Builtins::INSTANCE_OF, CALL_FUNCTION);
- }
- __ mov(a0, v0);
- __ LoadRoot(v0, Heap::kTrueValueRootIndex);
- __ DropAndRet(HasArgsInRegisters() ? 0 : 2, eq, a0, Operand(zero_reg));
- __ LoadRoot(v0, Heap::kFalseValueRootIndex);
- __ DropAndRet(HasArgsInRegisters() ? 0 : 2);
- }
+ // Slow-case: Call the runtime function.
+ __ bind(&slow_case);
+ __ Push(object, function);
+ __ TailCallRuntime(Runtime::kInstanceOf, 2, 1);
}
const Register StoreGlobalViaContextDescriptor::ValueRegister() { return a0; }
-const Register InstanceofDescriptor::left() { return a0; }
-const Register InstanceofDescriptor::right() { return a1; }
+const Register InstanceOfDescriptor::LeftRegister() { return a1; }
+const Register InstanceOfDescriptor::RightRegister() { return a0; }
const Register ArgumentsAccessReadDescriptor::index() { return a1; }
}
-template<class InstrType>
-void LCodeGen::EmitFalseBranch(InstrType instr,
- Condition condition,
- Register src1,
- const Operand& src2) {
+template <class InstrType>
+void LCodeGen::EmitTrueBranch(InstrType instr, Condition condition,
+ Register src1, const Operand& src2) {
+ int true_block = instr->TrueDestination(chunk_);
+ __ Branch(chunk_->GetAssemblyLabel(true_block), condition, src1, src2);
+}
+
+
+template <class InstrType>
+void LCodeGen::EmitFalseBranch(InstrType instr, Condition condition,
+ Register src1, const Operand& src2) {
int false_block = instr->FalseDestination(chunk_);
__ Branch(chunk_->GetAssemblyLabel(false_block), condition, src1, src2);
}
void LCodeGen::DoInstanceOf(LInstanceOf* instr) {
DCHECK(ToRegister(instr->context()).is(cp));
Label true_label, done;
- DCHECK(ToRegister(instr->left()).is(a0)); // Object is in a0.
- DCHECK(ToRegister(instr->right()).is(a1)); // Function is in a1.
- Register result = ToRegister(instr->result());
- DCHECK(result.is(v0));
+ DCHECK(ToRegister(instr->left()).is(InstanceOfDescriptor::LeftRegister()));
+ DCHECK(ToRegister(instr->right()).is(InstanceOfDescriptor::RightRegister()));
+ DCHECK(ToRegister(instr->result()).is(v0));
- InstanceofStub stub(isolate(), InstanceofStub::kArgsInRegisters);
+ InstanceOfStub stub(isolate());
CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
-
- __ Branch(&true_label, eq, result, Operand(zero_reg));
- __ li(result, Operand(factory()->false_value()));
- __ Branch(&done);
- __ bind(&true_label);
- __ li(result, Operand(factory()->true_value()));
- __ bind(&done);
-}
-
-
-void LCodeGen::DoInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr) {
- class DeferredInstanceOfKnownGlobal final : public LDeferredCode {
- public:
- DeferredInstanceOfKnownGlobal(LCodeGen* codegen,
- LInstanceOfKnownGlobal* instr)
- : LDeferredCode(codegen), instr_(instr) { }
- void Generate() override {
- codegen()->DoDeferredInstanceOfKnownGlobal(instr_, &map_check_);
- }
- LInstruction* instr() override { return instr_; }
- Label* map_check() { return &map_check_; }
-
- private:
- LInstanceOfKnownGlobal* instr_;
- Label map_check_;
- };
-
- DeferredInstanceOfKnownGlobal* deferred;
- deferred = new(zone()) DeferredInstanceOfKnownGlobal(this, instr);
-
- Label done, false_result;
- Register object = ToRegister(instr->value());
- Register temp = ToRegister(instr->temp());
- Register result = ToRegister(instr->result());
-
- DCHECK(object.is(a0));
- DCHECK(result.is(v0));
-
- // A Smi is not instance of anything.
- __ JumpIfSmi(object, &false_result);
-
- // This is the inlined call site instanceof cache. The two occurences of the
- // hole value will be patched to the last map/result pair generated by the
- // instanceof stub.
- Label cache_miss;
- Register map = temp;
- __ ld(map, FieldMemOperand(object, HeapObject::kMapOffset));
-
- Assembler::BlockTrampolinePoolScope block_trampoline_pool(masm_);
- __ bind(deferred->map_check()); // Label for calculating code patching.
- // We use Factory::the_hole_value() on purpose instead of loading from the
- // root array to force relocation to be able to later patch with
- // the cached map.
- Handle<Cell> cell = factory()->NewCell(factory()->the_hole_value());
- __ li(at, Operand(cell));
- __ ld(at, FieldMemOperand(at, Cell::kValueOffset));
- __ BranchShort(&cache_miss, ne, map, Operand(at));
- // We use Factory::the_hole_value() on purpose instead of loading from the
- // root array to force relocation to be able to later patch
- // with true or false. The distance from map check has to be constant.
- __ li(result, Operand(factory()->the_hole_value()));
- __ Branch(&done);
-
- // The inlined call site cache did not match. Check null and string before
- // calling the deferred code.
- __ bind(&cache_miss);
- // Null is not instance of anything.
- __ LoadRoot(temp, Heap::kNullValueRootIndex);
- __ Branch(&false_result, eq, object, Operand(temp));
-
- // String values is not instance of anything.
- Condition cc = __ IsObjectStringType(object, temp, temp);
- __ Branch(&false_result, cc, temp, Operand(zero_reg));
-
- // Go to the deferred code.
- __ Branch(deferred->entry());
-
- __ bind(&false_result);
- __ LoadRoot(result, Heap::kFalseValueRootIndex);
-
- // Here result has either true or false. Deferred code also produces true or
- // false object.
- __ bind(deferred->exit());
- __ bind(&done);
}
-void LCodeGen::DoDeferredInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr,
- Label* map_check) {
- Register result = ToRegister(instr->result());
- DCHECK(result.is(v0));
+void LCodeGen::DoHasInPrototypeChainAndBranch(
+ LHasInPrototypeChainAndBranch* instr) {
+ Register const object = ToRegister(instr->object());
+ Register const object_map = scratch0();
+ Register const object_prototype = object_map;
+ Register const prototype = ToRegister(instr->prototype());
- InstanceofStub::Flags flags = InstanceofStub::kNoFlags;
- flags = static_cast<InstanceofStub::Flags>(
- flags | InstanceofStub::kArgsInRegisters);
- flags = static_cast<InstanceofStub::Flags>(
- flags | InstanceofStub::kCallSiteInlineCheck);
- flags = static_cast<InstanceofStub::Flags>(
- flags | InstanceofStub::kReturnTrueFalseObject);
- InstanceofStub stub(isolate(), flags);
-
- PushSafepointRegistersScope scope(this);
- LoadContextFromDeferred(instr->context());
+ // The {object} must be a spec object. It's sufficient to know that {object}
+ // is not a smi, since all other non-spec objects have {null} prototypes and
+ // will be ruled out below.
+ if (instr->hydrogen()->ObjectNeedsSmiCheck()) {
+ __ SmiTst(object, at);
+ EmitFalseBranch(instr, eq, at, Operand(zero_reg));
+ }
- // Get the temp register reserved by the instruction. This needs to be a4 as
- // its slot of the pushing of safepoint registers is used to communicate the
- // offset to the location of the map check.
- Register temp = ToRegister(instr->temp());
- DCHECK(temp.is(a4));
- __ li(InstanceofStub::right(), instr->function());
- static const int kAdditionalDelta = 13;
- int delta = masm_->InstructionsGeneratedSince(map_check) + kAdditionalDelta;
- Label before_push_delta;
- __ bind(&before_push_delta);
- {
- Assembler::BlockTrampolinePoolScope block_trampoline_pool(masm_);
- __ li(temp, Operand(delta * kIntSize), CONSTANT_SIZE);
- __ StoreToSafepointRegisterSlot(temp, temp);
- }
- CallCodeGeneric(stub.GetCode(),
- RelocInfo::CODE_TARGET,
- instr,
- RECORD_SAFEPOINT_WITH_REGISTERS_AND_NO_ARGUMENTS);
- LEnvironment* env = instr->GetDeferredLazyDeoptimizationEnvironment();
- safepoints_.RecordLazyDeoptimizationIndex(env->deoptimization_index());
- // Put the result value into the result register slot and
- // restore all registers.
- __ StoreToSafepointRegisterSlot(result, result);
+ // Loop through the {object}s prototype chain looking for the {prototype}.
+ __ ld(object_map, FieldMemOperand(object, HeapObject::kMapOffset));
+ Label loop;
+ __ bind(&loop);
+ __ ld(object_prototype, FieldMemOperand(object_map, Map::kPrototypeOffset));
+ EmitTrueBranch(instr, eq, object_prototype, Operand(prototype));
+ __ LoadRoot(at, Heap::kNullValueRootIndex);
+ EmitFalseBranch(instr, eq, object_prototype, Operand(at));
+ __ Branch(&loop, USE_DELAY_SLOT);
+ __ ld(object_map, FieldMemOperand(object_prototype,
+ HeapObject::kMapOffset)); // In delay slot.
}
void DoDeferredStringCharCodeAt(LStringCharCodeAt* instr);
void DoDeferredStringCharFromCode(LStringCharFromCode* instr);
void DoDeferredAllocate(LAllocate* instr);
- void DoDeferredInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr,
- Label* map_check);
void DoDeferredInstanceMigration(LCheckMaps* instr, Register object);
void DoDeferredLoadMutableDouble(LLoadFieldByIndex* instr,
Condition condition,
FPURegister src1,
FPURegister src2);
- template<class InstrType>
- void EmitFalseBranch(InstrType instr,
- Condition condition,
- Register src1,
+ template <class InstrType>
+ void EmitTrueBranch(InstrType instr, Condition condition, Register src1,
+ const Operand& src2);
+ template <class InstrType>
+ void EmitFalseBranch(InstrType instr, Condition condition, Register src1,
const Operand& src2);
template<class InstrType>
void EmitFalseBranchF(InstrType instr,
if (instr->IsCall()) {
HValue* hydrogen_value_for_lazy_bailout = hydrogen_val;
- LInstruction* instruction_needing_environment = NULL;
if (hydrogen_val->HasObservableSideEffects()) {
HSimulate* sim = HSimulate::cast(hydrogen_val->next());
- instruction_needing_environment = instr;
sim->ReplayEnvironment(current_block_->last_environment());
hydrogen_value_for_lazy_bailout = sim;
}
LInstruction* bailout = AssignEnvironment(new(zone()) LLazyBailout());
bailout->set_hydrogen_value(hydrogen_value_for_lazy_bailout);
chunk_->AddInstruction(bailout, current_block_);
- if (instruction_needing_environment != NULL) {
- // Store the lazy deopt environment with the instruction if needed.
- // Right now it is only used for LInstanceOfKnownGlobal.
- instruction_needing_environment->
- SetDeferredLazyDeoptimizationEnvironment(bailout->environment());
- }
}
}
LInstruction* LChunkBuilder::DoInstanceOf(HInstanceOf* instr) {
+ LOperand* left =
+ UseFixed(instr->left(), InstanceOfDescriptor::LeftRegister());
+ LOperand* right =
+ UseFixed(instr->right(), InstanceOfDescriptor::RightRegister());
LOperand* context = UseFixed(instr->context(), cp);
- LInstanceOf* result =
- new(zone()) LInstanceOf(context, UseFixed(instr->left(), a0),
- UseFixed(instr->right(), a1));
+ LInstanceOf* result = new (zone()) LInstanceOf(context, left, right);
return MarkAsCall(DefineFixed(result, v0), instr);
}
-LInstruction* LChunkBuilder::DoInstanceOfKnownGlobal(
- HInstanceOfKnownGlobal* instr) {
- LInstanceOfKnownGlobal* result =
- new(zone()) LInstanceOfKnownGlobal(
- UseFixed(instr->context(), cp),
- UseFixed(instr->left(), a0),
- FixedTemp(a4));
- return MarkAsCall(DefineFixed(result, v0), instr);
+LInstruction* LChunkBuilder::DoHasInPrototypeChainAndBranch(
+ HHasInPrototypeChainAndBranch* instr) {
+ LOperand* object = UseRegister(instr->object());
+ LOperand* prototype = UseRegister(instr->prototype());
+ return new (zone()) LHasInPrototypeChainAndBranch(object, prototype);
}
V(GetCachedArrayIndex) \
V(Goto) \
V(HasCachedArrayIndexAndBranch) \
+ V(HasInPrototypeChainAndBranch) \
V(HasInstanceTypeAndBranch) \
V(InnerAllocatedObject) \
V(InstanceOf) \
- V(InstanceOfKnownGlobal) \
V(InstructionGap) \
V(Integer32ToDouble) \
V(InvokeFunction) \
void set_hydrogen_value(HValue* value) { hydrogen_value_ = value; }
HValue* hydrogen_value() const { return hydrogen_value_; }
- virtual void SetDeferredLazyDeoptimizationEnvironment(LEnvironment* env) { }
-
void MarkAsCall() { bit_field_ = IsCallBits::update(bit_field_, true); }
bool IsCall() const { return IsCallBits::decode(bit_field_); }
inputs_[2] = right;
}
- LOperand* context() { return inputs_[0]; }
- LOperand* left() { return inputs_[1]; }
- LOperand* right() { return inputs_[2]; }
+ LOperand* context() const { return inputs_[0]; }
+ LOperand* left() const { return inputs_[1]; }
+ LOperand* right() const { return inputs_[2]; }
DECLARE_CONCRETE_INSTRUCTION(InstanceOf, "instance-of")
};
-class LInstanceOfKnownGlobal final : public LTemplateInstruction<1, 2, 1> {
+class LHasInPrototypeChainAndBranch final : public LControlInstruction<2, 0> {
public:
- LInstanceOfKnownGlobal(LOperand* context, LOperand* value, LOperand* temp) {
- inputs_[0] = context;
- inputs_[1] = value;
- temps_[0] = temp;
+ LHasInPrototypeChainAndBranch(LOperand* object, LOperand* prototype) {
+ inputs_[0] = object;
+ inputs_[1] = prototype;
}
- LOperand* context() { return inputs_[0]; }
- LOperand* value() { return inputs_[1]; }
- LOperand* temp() { return temps_[0]; }
-
- DECLARE_CONCRETE_INSTRUCTION(InstanceOfKnownGlobal,
- "instance-of-known-global")
- DECLARE_HYDROGEN_ACCESSOR(InstanceOfKnownGlobal)
+ LOperand* object() const { return inputs_[0]; }
+ LOperand* prototype() const { return inputs_[1]; }
- Handle<JSFunction> function() const { return hydrogen()->function(); }
- LEnvironment* GetDeferredLazyDeoptimizationEnvironment() {
- return lazy_deopt_env_;
- }
- virtual void SetDeferredLazyDeoptimizationEnvironment(
- LEnvironment* env) override {
- lazy_deopt_env_ = env;
- }
-
- private:
- LEnvironment* lazy_deopt_env_;
+ DECLARE_CONCRETE_INSTRUCTION(HasInPrototypeChainAndBranch,
+ "has-in-prototype-chain-and-branch")
+ DECLARE_HYDROGEN_ACCESSOR(HasInPrototypeChainAndBranch)
};
}
-void MacroAssembler::TryGetFunctionPrototype(Register function,
- Register result,
- Register scratch,
- Label* miss,
- bool miss_on_bound_function) {
- Label non_instance;
- if (miss_on_bound_function) {
- // Check that the receiver isn't a smi.
- JumpIfSmi(function, miss);
-
- // Check that the function really is a function. Load map into result reg.
- GetObjectType(function, result, scratch);
- Branch(miss, ne, scratch, Operand(JS_FUNCTION_TYPE));
-
- ld(scratch,
- FieldMemOperand(function, JSFunction::kSharedFunctionInfoOffset));
- lwu(scratch,
- FieldMemOperand(scratch, SharedFunctionInfo::kCompilerHintsOffset));
- And(scratch, scratch,
- Operand(1 << SharedFunctionInfo::kBoundFunction));
- Branch(miss, ne, scratch, Operand(zero_reg));
-
- // Make sure that the function has an instance prototype.
- lbu(scratch, FieldMemOperand(result, Map::kBitFieldOffset));
- And(scratch, scratch, Operand(1 << Map::kHasNonInstancePrototype));
- Branch(&non_instance, ne, scratch, Operand(zero_reg));
- }
-
+void MacroAssembler::TryGetFunctionPrototype(Register function, Register result,
+ Register scratch, Label* miss) {
// Get the prototype or initial map from the function.
ld(result,
FieldMemOperand(function, JSFunction::kPrototypeOrInitialMapOffset));
// Get the prototype from the initial map.
ld(result, FieldMemOperand(result, Map::kPrototypeOffset));
- if (miss_on_bound_function) {
- jmp(&done);
-
- // Non-instance prototype: Fetch prototype from constructor field
- // in initial map.
- bind(&non_instance);
- GetMapConstructor(result, result, scratch, scratch);
- }
-
// All done.
bind(&done);
}
#undef BRANCH_ARGS_CHECK
-void MacroAssembler::PatchRelocatedValue(Register li_location,
- Register scratch,
- Register new_value) {
- lwu(scratch, MemOperand(li_location));
- // At this point scratch is a lui(at, ...) instruction.
- if (emit_debug_code()) {
- And(scratch, scratch, kOpcodeMask);
- Check(eq, kTheInstructionToPatchShouldBeALui,
- scratch, Operand(LUI));
- lwu(scratch, MemOperand(li_location));
- }
- dsrl32(t9, new_value, 0);
- Ins(scratch, t9, 0, kImm16Bits);
- sw(scratch, MemOperand(li_location));
-
- lwu(scratch, MemOperand(li_location, kInstrSize));
- // scratch is now ori(at, ...).
- if (emit_debug_code()) {
- And(scratch, scratch, kOpcodeMask);
- Check(eq, kTheInstructionToPatchShouldBeAnOri,
- scratch, Operand(ORI));
- lwu(scratch, MemOperand(li_location, kInstrSize));
- }
- dsrl(t9, new_value, kImm16Bits);
- Ins(scratch, t9, 0, kImm16Bits);
- sw(scratch, MemOperand(li_location, kInstrSize));
-
- lwu(scratch, MemOperand(li_location, kInstrSize * 3));
- // scratch is now ori(at, ...).
- if (emit_debug_code()) {
- And(scratch, scratch, kOpcodeMask);
- Check(eq, kTheInstructionToPatchShouldBeAnOri,
- scratch, Operand(ORI));
- lwu(scratch, MemOperand(li_location, kInstrSize * 3));
- }
-
- Ins(scratch, new_value, 0, kImm16Bits);
- sw(scratch, MemOperand(li_location, kInstrSize * 3));
-
- // Update the I-cache so the new lui and ori can be executed.
- FlushICache(li_location, 4);
-}
-
-void MacroAssembler::GetRelocatedValue(Register li_location,
- Register value,
- Register scratch) {
- lwu(value, MemOperand(li_location));
- if (emit_debug_code()) {
- And(value, value, kOpcodeMask);
- Check(eq, kTheInstructionShouldBeALui,
- value, Operand(LUI));
- lwu(value, MemOperand(li_location));
- }
-
- // value now holds a lui instruction. Extract the immediate.
- andi(value, value, kImm16Mask);
- dsll32(value, value, kImm16Bits);
-
- lwu(scratch, MemOperand(li_location, kInstrSize));
- if (emit_debug_code()) {
- And(scratch, scratch, kOpcodeMask);
- Check(eq, kTheInstructionShouldBeAnOri,
- scratch, Operand(ORI));
- lwu(scratch, MemOperand(li_location, kInstrSize));
- }
- // "scratch" now holds an ori instruction. Extract the immediate.
- andi(scratch, scratch, kImm16Mask);
- dsll32(scratch, scratch, 0);
-
- or_(value, value, scratch);
-
- lwu(scratch, MemOperand(li_location, kInstrSize * 3));
- if (emit_debug_code()) {
- And(scratch, scratch, kOpcodeMask);
- Check(eq, kTheInstructionShouldBeAnOri,
- scratch, Operand(ORI));
- lwu(scratch, MemOperand(li_location, kInstrSize * 3));
- }
- // "scratch" now holds an ori instruction. Extract the immediate.
- andi(scratch, scratch, kImm16Mask);
- dsll(scratch, scratch, kImm16Bits);
-
- or_(value, value, scratch);
- // Sign extend extracted address.
- dsra(value, value, kImm16Bits);
-}
-
-
void MacroAssembler::CheckPageFlag(
Register object,
Register scratch,
// function and jumps to the miss label if the fast checks fail. The
// function register will be untouched; the other registers may be
// clobbered.
- void TryGetFunctionPrototype(Register function,
- Register result,
- Register scratch,
- Label* miss,
- bool miss_on_bound_function = false);
+ void TryGetFunctionPrototype(Register function, Register result,
+ Register scratch, Label* miss);
void GetObjectType(Register function,
Register map,
void EnterFrame(StackFrame::Type type, bool load_constant_pool_pointer_reg);
void LeaveFrame(StackFrame::Type type);
- // Patch the relocated value (lui/ori pair).
- void PatchRelocatedValue(Register li_location,
- Register scratch,
- Register new_value);
- // Get the relocatad value (loaded data) from the lui/ori pair.
- void GetRelocatedValue(Register li_location,
- Register value,
- Register scratch);
-
// Expects object in a0 and returns map with validated enum cache
// in a0. Assumes that any other register can be used as a scratch.
void CheckEnumCache(Register null_value, Label* call_runtime);
static const int kNativeBitWithinByte =
(kNative + kCompilerHintsSmiTagSize) % kBitsPerByte;
+ static const int kBoundBitWithinByte =
+ (kBoundFunction + kCompilerHintsSmiTagSize) % kBitsPerByte;
+
#if defined(V8_TARGET_LITTLE_ENDIAN)
static const int kStrictModeByteOffset = kCompilerHintsOffset +
(kStrictModeFunction + kCompilerHintsSmiTagSize) / kBitsPerByte;
(kStrongModeFunction + kCompilerHintsSmiTagSize) / kBitsPerByte;
static const int kNativeByteOffset = kCompilerHintsOffset +
(kNative + kCompilerHintsSmiTagSize) / kBitsPerByte;
+ static const int kBoundByteOffset =
+ kCompilerHintsOffset +
+ (kBoundFunction + kCompilerHintsSmiTagSize) / kBitsPerByte;
#elif defined(V8_TARGET_BIG_ENDIAN)
static const int kStrictModeByteOffset = kCompilerHintsOffset +
(kCompilerHintsSize - 1) -
static const int kNativeByteOffset = kCompilerHintsOffset +
(kCompilerHintsSize - 1) -
((kNative + kCompilerHintsSmiTagSize) / kBitsPerByte);
+ static const int kBoundByteOffset =
+ kCompilerHintsOffset + (kCompilerHintsSize - 1) -
+ ((kBoundFunction + kCompilerHintsSmiTagSize) / kBitsPerByte);
#else
#error Unknown byte ordering
#endif
}
-// ECMA-262, section 11.8.6, page 54. To make the implementation more
-// efficient, the return value should be zero if the 'this' is an
-// instance of F, and non-zero if not. This makes it possible to avoid
-// an expensive ToBoolean conversion in the generated code.
-function INSTANCE_OF(F) {
- var V = this;
- if (!IS_SPEC_FUNCTION(F)) {
- throw %MakeTypeError(kInstanceofFunctionExpected, F);
- }
-
- // If V is not an object, return false.
- if (!IS_SPEC_OBJECT(V)) {
- return 1;
- }
-
- // Check if function is bound, if so, get [[BoundFunction]] from it
- // and use that instead of F.
- var bindings = %BoundFunctionGetBindings(F);
- if (bindings) {
- F = bindings[kBoundFunctionIndex]; // Always a non-bound function.
- }
- // Get the prototype of F; if it is not an object, throw an error.
- var O = F.prototype;
- if (!IS_SPEC_OBJECT(O)) {
- throw %MakeTypeError(kInstanceofNonobjectProto, O);
- }
-
- // Return whether or not O is in the prototype chain of V.
- return %IsInPrototypeChain(O, V) ? 0 : 1;
-}
-
-
function CALL_NON_FUNCTION() {
var delegate = %GetFunctionDelegate(this);
if (!IS_FUNCTION(delegate)) {
SHR,
SHR_STRONG,
IN,
- INSTANCE_OF,
CALL_NON_FUNCTION,
CALL_NON_FUNCTION_AS_CONSTRUCTOR,
CALL_FUNCTION_PROXY,
}
-RUNTIME_FUNCTION(Runtime_IsInPrototypeChain) {
- SealHandleScope shs(isolate);
- DCHECK(args.length() == 2);
- // See ECMA-262, section 15.3.5.3, page 88 (steps 5 - 8).
- CONVERT_ARG_CHECKED(Object, O, 0);
- CONVERT_ARG_CHECKED(Object, V, 1);
- return isolate->heap()->ToBoolean(V->HasInPrototypeChain(isolate, O));
-}
-
-
// Enumerator used as indices into the array returned from GetOwnProperty
enum PropertyDescriptorIndices {
IS_ACCESSOR_INDEX,
return Smi::FromInt(x->StrictEquals(y) ? EQUAL : NOT_EQUAL);
}
+
+RUNTIME_FUNCTION(Runtime_InstanceOf) {
+ // ECMA-262, section 11.8.6, page 54.
+ HandleScope shs(isolate);
+ DCHECK_EQ(2, args.length());
+ DCHECK(args.length() == 2);
+ CONVERT_ARG_HANDLE_CHECKED(Object, object, 0);
+ CONVERT_ARG_HANDLE_CHECKED(Object, callable, 1);
+ // {callable} must have a [[Call]] internal method.
+ if (!callable->IsCallable()) {
+ THROW_NEW_ERROR_RETURN_FAILURE(
+ isolate,
+ NewTypeError(MessageTemplate::kInstanceofFunctionExpected, callable));
+ }
+ // If {object} is not a receiver, return false.
+ if (!object->IsJSReceiver()) {
+ return isolate->heap()->false_value();
+ }
+ // Check if {callable} is bound, if so, get [[BoundFunction]] from it and use
+ // that instead of {callable}.
+ if (callable->IsJSFunction()) {
+ Handle<JSFunction> function = Handle<JSFunction>::cast(callable);
+ if (function->shared()->bound()) {
+ Handle<FixedArray> bindings(function->function_bindings(), isolate);
+ callable =
+ handle(bindings->get(JSFunction::kBoundFunctionIndex), isolate);
+ }
+ }
+ DCHECK(callable->IsCallable());
+ // Get the "prototype" of {callable}; raise an error if it's not a receiver.
+ Handle<Object> prototype;
+ ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
+ isolate, prototype,
+ Object::GetProperty(callable, isolate->factory()->prototype_string()));
+ if (!prototype->IsJSReceiver()) {
+ THROW_NEW_ERROR_RETURN_FAILURE(
+ isolate,
+ NewTypeError(MessageTemplate::kInstanceofNonobjectProto, prototype));
+ }
+ // Return whether or not {prototype} is in the prototype chain of {object}.
+ return isolate->heap()->ToBoolean(
+ object->HasInPrototypeChain(isolate, *prototype));
+}
+
+
+RUNTIME_FUNCTION(Runtime_HasInPrototypeChain) {
+ SealHandleScope scope(isolate);
+ DCHECK_EQ(2, args.length());
+ CONVERT_ARG_CHECKED(Object, object, 0);
+ CONVERT_ARG_CHECKED(Object, prototype, 1);
+ return isolate->heap()->ToBoolean(
+ object->HasInPrototypeChain(isolate, prototype));
+}
+
} // namespace internal
} // namespace v8
F(GetPrototype, 1, 1) \
F(InternalSetPrototype, 2, 1) \
F(SetPrototype, 2, 1) \
- F(IsInPrototypeChain, 2, 1) \
F(GetOwnProperty, 2, 1) \
F(PreventExtensions, 1, 1) \
F(IsExtensible, 1, 1) \
F(DefineGetterPropertyUnchecked, 4, 1) \
F(DefineSetterPropertyUnchecked, 4, 1) \
F(ToObject, 1, 1) \
- F(StrictEquals, 2, 1)
+ F(StrictEquals, 2, 1) \
+ F(InstanceOf, 2, 1) \
+ F(HasInPrototypeChain, 2, 1)
#define FOR_EACH_INTRINSIC_OBSERVE(F) \
// ECMA-262 - 15.2.4.6
function ObjectIsPrototypeOf(V) {
if (!IS_SPEC_OBJECT(V)) return false;
- CHECK_OBJECT_COERCIBLE(this, "Object.prototype.isPrototypeOf");
- return %IsInPrototypeChain(this, V);
+ var O = TO_OBJECT(this);
+ return %_HasInPrototypeChain(V, O);
}
}
-void InstanceofStub::Generate(MacroAssembler* masm) {
- // Implements "value instanceof function" operator.
- // Expected input state with no inline cache:
- // rsp[0] : return address
- // rsp[8] : function pointer
- // rsp[16] : value
- // Expected input state with an inline one-element cache:
- // rsp[0] : return address
- // rsp[8] : offset from return address to location of inline cache
- // rsp[16] : function pointer
- // rsp[24] : value
- // Returns a bitwise zero to indicate that the value
- // is and instance of the function and anything else to
- // indicate that the value is not an instance.
-
- // Fixed register usage throughout the stub.
- Register object = rax; // Object (lhs).
- Register map = rbx; // Map of the object.
- Register function = rdx; // Function (rhs).
- Register prototype = rdi; // Prototype of the function.
- Register scratch = rcx;
-
- static const int kOffsetToMapCheckValue = 2;
- static const int kOffsetToResultValue = kPointerSize == kInt64Size ? 18 : 14;
- // The last 4 bytes of the instruction sequence
- // movp(rdi, FieldOperand(rax, HeapObject::kMapOffset))
- // Move(kScratchRegister, Factory::the_hole_value())
- // in front of the hole value address.
- static const unsigned int kWordBeforeMapCheckValue =
- kPointerSize == kInt64Size ? 0xBA49FF78 : 0xBA41FF78;
- // The last 4 bytes of the instruction sequence
- // __ j(not_equal, &cache_miss);
- // __ LoadRoot(ToRegister(instr->result()), Heap::kTheHoleValueRootIndex);
- // before the offset of the hole value in the root array.
- static const unsigned int kWordBeforeResultValue =
- kPointerSize == kInt64Size ? 0x458B4906 : 0x458B4106;
-
- int extra_argument_offset = HasCallSiteInlineCheck() ? 1 : 0;
-
- DCHECK_EQ(object.code(), InstanceofStub::left().code());
- DCHECK_EQ(function.code(), InstanceofStub::right().code());
-
- // Get the object and function - they are always both needed.
- // Go slow case if the object is a smi.
- Label slow;
- StackArgumentsAccessor args(rsp, 2 + extra_argument_offset,
- ARGUMENTS_DONT_CONTAIN_RECEIVER);
- if (!HasArgsInRegisters()) {
- __ movp(object, args.GetArgumentOperand(0));
- __ movp(function, args.GetArgumentOperand(1));
- }
- __ JumpIfSmi(object, &slow);
-
- // Check that the left hand is a JS object. Leave its map in rax.
- __ CmpObjectType(object, FIRST_SPEC_OBJECT_TYPE, map);
- __ j(below, &slow);
- __ CmpInstanceType(map, LAST_SPEC_OBJECT_TYPE);
- __ j(above, &slow);
-
- // 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()) {
- // Look up the function and the map in the instanceof cache.
- Label miss;
- __ CompareRoot(function, Heap::kInstanceofCacheFunctionRootIndex);
- __ j(not_equal, &miss, Label::kNear);
- __ CompareRoot(map, Heap::kInstanceofCacheMapRootIndex);
- __ j(not_equal, &miss, Label::kNear);
- __ LoadRoot(rax, Heap::kInstanceofCacheAnswerRootIndex);
- __ ret((HasArgsInRegisters() ? 0 : 2) * kPointerSize);
- __ bind(&miss);
- }
+void InstanceOfStub::Generate(MacroAssembler* masm) {
+ Register const object = rdx; // Object (lhs).
+ Register const function = rax; // Function (rhs).
+ Register const object_map = rcx; // Map of {object}.
+ Register const function_map = r8; // Map of {function}.
+ Register const function_prototype = rdi; // Prototype of {function}.
+
+ 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, Label::kNear);
+
+ // 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;
+ __ movp(object_map, FieldOperand(object, HeapObject::kMapOffset));
+ __ CompareRoot(function, Heap::kInstanceofCacheFunctionRootIndex);
+ __ j(not_equal, &fast_case, Label::kNear);
+ __ CompareRoot(object_map, Heap::kInstanceofCacheMapRootIndex);
+ __ j(not_equal, &fast_case, Label::kNear);
+ __ LoadRoot(rax, Heap::kInstanceofCacheAnswerRootIndex);
+ __ ret(0);
- // Get the prototype of the function.
- __ TryGetFunctionPrototype(function, prototype, &slow, true);
-
- // Check that the function prototype is a JS object.
- __ JumpIfSmi(prototype, &slow);
- __ CmpObjectType(prototype, FIRST_SPEC_OBJECT_TYPE, kScratchRegister);
- __ j(below, &slow);
- __ CmpInstanceType(kScratchRegister, LAST_SPEC_OBJECT_TYPE);
- __ j(above, &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()) {
- __ StoreRoot(function, Heap::kInstanceofCacheFunctionRootIndex);
- __ StoreRoot(map, Heap::kInstanceofCacheMapRootIndex);
- } else {
- // The constants for the code patching are based on push instructions
- // at the call site.
- DCHECK(!HasArgsInRegisters());
- // Get return address and delta to inlined map check.
- __ movq(kScratchRegister, StackOperandForReturnAddress(0));
- __ subp(kScratchRegister, args.GetArgumentOperand(2));
- if (FLAG_debug_code) {
- __ movl(scratch, Immediate(kWordBeforeMapCheckValue));
- __ cmpl(Operand(kScratchRegister, kOffsetToMapCheckValue - 4), scratch);
- __ Assert(equal, kInstanceofStubUnexpectedCallSiteCacheCheck);
- }
- __ movp(kScratchRegister,
- Operand(kScratchRegister, kOffsetToMapCheckValue));
- __ movp(Operand(kScratchRegister, 0), map);
-
- __ movp(r8, map);
- // Scratch points at the cell payload. Calculate the start of the object.
- __ subp(kScratchRegister, Immediate(Cell::kValueOffset - 1));
- __ RecordWriteField(kScratchRegister, Cell::kValueOffset, r8, function,
- kDontSaveFPRegs, OMIT_REMEMBERED_SET, OMIT_SMI_CHECK);
- }
+ // 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);
+ __ CmpObjectType(function, JS_FUNCTION_TYPE, function_map);
+ __ j(not_equal, &slow_case);
+ __ LoadRoot(rax, Heap::kFalseValueRootIndex);
+ __ ret(0);
- // Loop through the prototype chain looking for the function prototype.
- __ movp(scratch, FieldOperand(map, Map::kPrototypeOffset));
- Label loop, is_instance, is_not_instance;
- __ LoadRoot(kScratchRegister, Heap::kNullValueRootIndex);
+ // Fast-case: The {function} must be a valid JSFunction.
+ __ bind(&fast_case);
+ __ JumpIfSmi(function, &slow_case);
+ __ CmpObjectType(function, JS_FUNCTION_TYPE, function_map);
+ __ j(not_equal, &slow_case);
+
+ // Ensure that {function} has an instance prototype.
+ __ testb(FieldOperand(function_map, Map::kBitFieldOffset),
+ Immediate(1 << Map::kHasNonInstancePrototype));
+ __ j(not_zero, &slow_case);
+
+ // Ensure that {function} is not bound.
+ Register const shared_info = kScratchRegister;
+ __ movp(shared_info,
+ FieldOperand(function, JSFunction::kSharedFunctionInfoOffset));
+ __ TestBitSharedFunctionInfoSpecialField(
+ shared_info, SharedFunctionInfo::kCompilerHintsOffset,
+ SharedFunctionInfo::kBoundFunction);
+ __ j(not_zero, &slow_case);
+
+ // Get the "prototype" (or initial map) of the {function}.
+ __ movp(function_prototype,
+ FieldOperand(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;
+ Register const function_prototype_map = kScratchRegister;
+ __ CmpObjectType(function_prototype, MAP_TYPE, function_prototype_map);
+ __ j(not_equal, &function_prototype_valid, Label::kNear);
+ __ movp(function_prototype,
+ FieldOperand(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;
+ Label done, loop;
+ __ LoadRoot(rax, Heap::kTrueValueRootIndex);
__ bind(&loop);
- __ cmpp(scratch, prototype);
- __ j(equal, &is_instance, Label::kNear);
- __ cmpp(scratch, kScratchRegister);
- // The code at is_not_instance assumes that kScratchRegister contains a
- // non-zero GCable value (the null object in this case).
- __ j(equal, &is_not_instance, Label::kNear);
- __ movp(scratch, FieldOperand(scratch, HeapObject::kMapOffset));
- __ movp(scratch, FieldOperand(scratch, Map::kPrototypeOffset));
- __ jmp(&loop);
-
- __ bind(&is_instance);
- if (!HasCallSiteInlineCheck()) {
- __ xorl(rax, rax);
- // Store bitwise zero in the cache. This is a Smi in GC terms.
- STATIC_ASSERT(kSmiTag == 0);
- __ StoreRoot(rax, Heap::kInstanceofCacheAnswerRootIndex);
- if (ReturnTrueFalseObject()) {
- __ LoadRoot(rax, Heap::kTrueValueRootIndex);
- }
- } else {
- // Store offset of true in the root array at the inline check site.
- int true_offset = 0x100 +
- (Heap::kTrueValueRootIndex << kPointerSizeLog2) - kRootRegisterBias;
- // Assert it is a 1-byte signed value.
- DCHECK(true_offset >= 0 && true_offset < 0x100);
- __ movl(rax, Immediate(true_offset));
- __ movq(kScratchRegister, StackOperandForReturnAddress(0));
- __ subp(kScratchRegister, args.GetArgumentOperand(2));
- __ movb(Operand(kScratchRegister, kOffsetToResultValue), rax);
- if (FLAG_debug_code) {
- __ movl(rax, Immediate(kWordBeforeResultValue));
- __ cmpl(Operand(kScratchRegister, kOffsetToResultValue - 4), rax);
- __ Assert(equal, kInstanceofStubUnexpectedCallSiteCacheMov);
- }
- if (!ReturnTrueFalseObject()) {
- __ Set(rax, 0);
- }
- }
- __ ret(((HasArgsInRegisters() ? 0 : 2) + extra_argument_offset) *
- kPointerSize);
-
- __ bind(&is_not_instance);
- if (!HasCallSiteInlineCheck()) {
- // We have to store a non-zero value in the cache.
- __ StoreRoot(kScratchRegister, Heap::kInstanceofCacheAnswerRootIndex);
- if (ReturnTrueFalseObject()) {
- __ LoadRoot(rax, Heap::kFalseValueRootIndex);
- }
- } else {
- // Store offset of false in the root array at the inline check site.
- int false_offset = 0x100 +
- (Heap::kFalseValueRootIndex << kPointerSizeLog2) - kRootRegisterBias;
- // Assert it is a 1-byte signed value.
- DCHECK(false_offset >= 0 && false_offset < 0x100);
- __ movl(rax, Immediate(false_offset));
- __ movq(kScratchRegister, StackOperandForReturnAddress(0));
- __ subp(kScratchRegister, args.GetArgumentOperand(2));
- __ movb(Operand(kScratchRegister, kOffsetToResultValue), rax);
- if (FLAG_debug_code) {
- __ movl(rax, Immediate(kWordBeforeResultValue));
- __ cmpl(Operand(kScratchRegister, kOffsetToResultValue - 4), rax);
- __ Assert(equal, kInstanceofStubUnexpectedCallSiteCacheMov);
- }
- }
- __ ret(((HasArgsInRegisters() ? 0 : 2) + extra_argument_offset) *
- kPointerSize);
+ __ movp(object_prototype, FieldOperand(object_map, Map::kPrototypeOffset));
+ __ cmpp(object_prototype, function_prototype);
+ __ j(equal, &done, Label::kNear);
+ __ CompareRoot(object_prototype, Heap::kNullValueRootIndex);
+ __ movp(object_map, FieldOperand(object_prototype, HeapObject::kMapOffset));
+ __ j(not_equal, &loop);
+ __ LoadRoot(rax, Heap::kFalseValueRootIndex);
+ __ bind(&done);
+ __ StoreRoot(rax, Heap::kInstanceofCacheAnswerRootIndex);
+ __ ret(0);
- // Slow-case: Go through the JavaScript implementation.
- __ bind(&slow);
- if (!ReturnTrueFalseObject()) {
- // Tail call the builtin which returns 0 or 1.
- DCHECK(!HasArgsInRegisters());
- if (HasCallSiteInlineCheck()) {
- // Remove extra value from the stack.
- __ PopReturnAddressTo(rcx);
- __ Pop(rax);
- __ PushReturnAddressFrom(rcx);
- }
- __ InvokeBuiltin(Builtins::INSTANCE_OF, JUMP_FUNCTION);
- } else {
- // Call the builtin and convert 0/1 to true/false.
- {
- FrameScope scope(masm, StackFrame::INTERNAL);
- __ Push(object);
- __ Push(function);
- __ InvokeBuiltin(Builtins::INSTANCE_OF, CALL_FUNCTION);
- }
- Label true_value, done;
- __ testq(rax, rax);
- __ j(zero, &true_value, Label::kNear);
- __ LoadRoot(rax, Heap::kFalseValueRootIndex);
- __ jmp(&done, Label::kNear);
- __ bind(&true_value);
- __ LoadRoot(rax, Heap::kTrueValueRootIndex);
- __ bind(&done);
- __ ret(((HasArgsInRegisters() ? 0 : 2) + extra_argument_offset) *
- kPointerSize);
- }
+ // Slow-case: Call the runtime function.
+ __ bind(&slow_case);
+ __ PopReturnAddressTo(kScratchRegister);
+ __ Push(object);
+ __ Push(function);
+ __ PushReturnAddressFrom(kScratchRegister);
+ __ TailCallRuntime(Runtime::kInstanceOf, 2, 1);
}
const Register StoreGlobalViaContextDescriptor::ValueRegister() { return rax; }
-const Register InstanceofDescriptor::left() { return rax; }
-const Register InstanceofDescriptor::right() { return rdx; }
+const Register InstanceOfDescriptor::LeftRegister() { return rdx; }
+const Register InstanceOfDescriptor::RightRegister() { return rax; }
const Register ArgumentsAccessReadDescriptor::index() { return rdx; }
}
-template<class InstrType>
+template <class InstrType>
+void LCodeGen::EmitTrueBranch(InstrType instr, Condition cc) {
+ int true_block = instr->TrueDestination(chunk_);
+ __ j(cc, chunk_->GetAssemblyLabel(true_block));
+}
+
+
+template <class InstrType>
void LCodeGen::EmitFalseBranch(InstrType instr, Condition cc) {
int false_block = instr->FalseDestination(chunk_);
__ j(cc, chunk_->GetAssemblyLabel(false_block));
void LCodeGen::DoInstanceOf(LInstanceOf* instr) {
DCHECK(ToRegister(instr->context()).is(rsi));
- InstanceofStub stub(isolate(), InstanceofStub::kNoFlags);
- __ Push(ToRegister(instr->left()));
- __ Push(ToRegister(instr->right()));
+ DCHECK(ToRegister(instr->left()).is(InstanceOfDescriptor::LeftRegister()));
+ DCHECK(ToRegister(instr->right()).is(InstanceOfDescriptor::RightRegister()));
+ DCHECK(ToRegister(instr->result()).is(rax));
+ InstanceOfStub stub(isolate());
CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
- Label true_value, done;
- __ testp(rax, rax);
- __ j(zero, &true_value, Label::kNear);
- __ LoadRoot(ToRegister(instr->result()), Heap::kFalseValueRootIndex);
- __ jmp(&done, Label::kNear);
- __ bind(&true_value);
- __ LoadRoot(ToRegister(instr->result()), Heap::kTrueValueRootIndex);
- __ bind(&done);
}
-void LCodeGen::DoInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr) {
- class DeferredInstanceOfKnownGlobal final : public LDeferredCode {
- public:
- DeferredInstanceOfKnownGlobal(LCodeGen* codegen,
- LInstanceOfKnownGlobal* instr)
- : LDeferredCode(codegen), instr_(instr) { }
- void Generate() override {
- codegen()->DoDeferredInstanceOfKnownGlobal(instr_, &map_check_);
- }
- LInstruction* instr() override { return instr_; }
- Label* map_check() { return &map_check_; }
- private:
- LInstanceOfKnownGlobal* instr_;
- Label map_check_;
- };
-
- DCHECK(ToRegister(instr->context()).is(rsi));
- DeferredInstanceOfKnownGlobal* deferred;
- deferred = new(zone()) DeferredInstanceOfKnownGlobal(this, instr);
-
- Label done, false_result;
- Register object = ToRegister(instr->value());
-
- // A Smi is not an instance of anything.
- __ JumpIfSmi(object, &false_result, Label::kNear);
-
- // This is the inlined call site instanceof cache. The two occurences of the
- // hole value will be patched to the last map/result pair generated by the
- // instanceof stub.
- Label cache_miss;
- // Use a temp register to avoid memory operands with variable lengths.
- Register map = ToRegister(instr->temp());
- __ movp(map, FieldOperand(object, HeapObject::kMapOffset));
- __ bind(deferred->map_check()); // Label for calculating code patching.
- Handle<Cell> cache_cell = factory()->NewCell(factory()->the_hole_value());
- __ Move(kScratchRegister, cache_cell, RelocInfo::CELL);
- __ cmpp(map, Operand(kScratchRegister, 0));
- __ j(not_equal, &cache_miss, Label::kNear);
- // Patched to load either true or false.
- __ LoadRoot(ToRegister(instr->result()), Heap::kTheHoleValueRootIndex);
-#ifdef DEBUG
- // Check that the code size between patch label and patch sites is invariant.
- Label end_of_patched_code;
- __ bind(&end_of_patched_code);
- DCHECK(true);
-#endif
- __ jmp(&done, Label::kNear);
-
- // The inlined call site cache did not match. Check for null and string
- // before calling the deferred code.
- __ bind(&cache_miss); // Null is not an instance of anything.
- __ CompareRoot(object, Heap::kNullValueRootIndex);
- __ j(equal, &false_result, Label::kNear);
-
- // String values are not instances of anything.
- __ JumpIfNotString(object, kScratchRegister, deferred->entry());
-
- __ bind(&false_result);
- __ LoadRoot(ToRegister(instr->result()), Heap::kFalseValueRootIndex);
-
- __ bind(deferred->exit());
- __ bind(&done);
-}
-
+void LCodeGen::DoHasInPrototypeChainAndBranch(
+ LHasInPrototypeChainAndBranch* instr) {
+ Register const object = ToRegister(instr->object());
+ Register const object_map = kScratchRegister;
+ Register const object_prototype = object_map;
+ Register const prototype = ToRegister(instr->prototype());
-void LCodeGen::DoDeferredInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr,
- Label* map_check) {
- {
- PushSafepointRegistersScope scope(this);
- InstanceofStub::Flags flags = static_cast<InstanceofStub::Flags>(
- InstanceofStub::kNoFlags | InstanceofStub::kCallSiteInlineCheck);
- InstanceofStub stub(isolate(), flags);
-
- __ Push(ToRegister(instr->value()));
- __ Push(instr->function());
-
- static const int kAdditionalDelta = kPointerSize == kInt64Size ? 10 : 16;
- int delta =
- masm_->SizeOfCodeGeneratedSince(map_check) + kAdditionalDelta;
- DCHECK(delta >= 0);
- __ PushImm32(delta);
-
- // We are pushing three values on the stack but recording a
- // safepoint with two arguments because stub is going to
- // remove the third argument from the stack before jumping
- // to instanceof builtin on the slow path.
- CallCodeGeneric(stub.GetCode(),
- RelocInfo::CODE_TARGET,
- instr,
- RECORD_SAFEPOINT_WITH_REGISTERS,
- 2);
- DCHECK(delta == masm_->SizeOfCodeGeneratedSince(map_check));
- LEnvironment* env = instr->GetDeferredLazyDeoptimizationEnvironment();
- safepoints_.RecordLazyDeoptimizationIndex(env->deoptimization_index());
- // Move result to a register that survives the end of the
- // PushSafepointRegisterScope.
- __ movp(kScratchRegister, rax);
+ // The {object} must be a spec object. It's sufficient to know that {object}
+ // is not a smi, since all other non-spec objects have {null} prototypes and
+ // will be ruled out below.
+ if (instr->hydrogen()->ObjectNeedsSmiCheck()) {
+ Condition is_smi = __ CheckSmi(object);
+ EmitFalseBranch(instr, is_smi);
}
- __ testp(kScratchRegister, kScratchRegister);
- Label load_false;
- Label done;
- __ j(not_zero, &load_false, Label::kNear);
- __ LoadRoot(rax, Heap::kTrueValueRootIndex);
- __ jmp(&done, Label::kNear);
- __ bind(&load_false);
- __ LoadRoot(rax, Heap::kFalseValueRootIndex);
- __ bind(&done);
+
+ // Loop through the {object}s prototype chain looking for the {prototype}.
+ __ movp(object_map, FieldOperand(object, HeapObject::kMapOffset));
+ Label loop;
+ __ bind(&loop);
+ __ movp(object_prototype, FieldOperand(object_map, Map::kPrototypeOffset));
+ __ cmpp(object_prototype, prototype);
+ EmitTrueBranch(instr, equal);
+ __ CompareRoot(object_prototype, Heap::kNullValueRootIndex);
+ EmitFalseBranch(instr, equal);
+ __ movp(object_map, FieldOperand(object_prototype, HeapObject::kMapOffset));
+ __ jmp(&loop);
}
void DoDeferredStringCharCodeAt(LStringCharCodeAt* instr);
void DoDeferredStringCharFromCode(LStringCharFromCode* instr);
void DoDeferredAllocate(LAllocate* instr);
- void DoDeferredInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr,
- Label* map_check);
void DoDeferredInstanceMigration(LCheckMaps* instr, Register object);
void DoDeferredLoadMutableDouble(LLoadFieldByIndex* instr,
Register object,
// EmitBranch expects to be the last instruction of a block.
template<class InstrType>
void EmitBranch(InstrType instr, Condition cc);
- template<class InstrType>
+ template <class InstrType>
+ void EmitTrueBranch(InstrType instr, Condition cc);
+ template <class InstrType>
void EmitFalseBranch(InstrType instr, Condition cc);
void EmitNumberUntagD(LNumberUntagD* instr, Register input,
XMMRegister result, NumberUntagDMode mode);
if (instr->IsCall()) {
HValue* hydrogen_value_for_lazy_bailout = hydrogen_val;
- LInstruction* instruction_needing_environment = NULL;
if (hydrogen_val->HasObservableSideEffects()) {
HSimulate* sim = HSimulate::cast(hydrogen_val->next());
- instruction_needing_environment = instr;
sim->ReplayEnvironment(current_block_->last_environment());
hydrogen_value_for_lazy_bailout = sim;
}
LInstruction* bailout = AssignEnvironment(new(zone()) LLazyBailout());
bailout->set_hydrogen_value(hydrogen_value_for_lazy_bailout);
chunk_->AddInstruction(bailout, current_block_);
- if (instruction_needing_environment != NULL) {
- // Store the lazy deopt environment with the instruction if needed.
- // Right now it is only used for LInstanceOfKnownGlobal.
- instruction_needing_environment->
- SetDeferredLazyDeoptimizationEnvironment(bailout->environment());
- }
}
}
LInstruction* LChunkBuilder::DoInstanceOf(HInstanceOf* instr) {
- LOperand* left = UseFixed(instr->left(), rax);
- LOperand* right = UseFixed(instr->right(), rdx);
+ LOperand* left =
+ UseFixed(instr->left(), InstanceOfDescriptor::LeftRegister());
+ LOperand* right =
+ UseFixed(instr->right(), InstanceOfDescriptor::RightRegister());
LOperand* context = UseFixed(instr->context(), rsi);
- LInstanceOf* result = new(zone()) LInstanceOf(context, left, right);
+ LInstanceOf* result = new (zone()) LInstanceOf(context, left, right);
return MarkAsCall(DefineFixed(result, rax), instr);
}
-LInstruction* LChunkBuilder::DoInstanceOfKnownGlobal(
- HInstanceOfKnownGlobal* instr) {
- LInstanceOfKnownGlobal* result =
- new(zone()) LInstanceOfKnownGlobal(UseFixed(instr->context(), rsi),
- UseFixed(instr->left(), rax),
- FixedTemp(rdi));
- return MarkAsCall(DefineFixed(result, rax), instr);
+LInstruction* LChunkBuilder::DoHasInPrototypeChainAndBranch(
+ HHasInPrototypeChainAndBranch* instr) {
+ LOperand* object = UseRegister(instr->object());
+ LOperand* prototype = UseRegister(instr->prototype());
+ return new (zone()) LHasInPrototypeChainAndBranch(object, prototype);
}
V(GetCachedArrayIndex) \
V(Goto) \
V(HasCachedArrayIndexAndBranch) \
+ V(HasInPrototypeChainAndBranch) \
V(HasInstanceTypeAndBranch) \
V(InnerAllocatedObject) \
V(InstanceOf) \
- V(InstanceOfKnownGlobal) \
V(InstructionGap) \
V(Integer32ToDouble) \
V(InvokeFunction) \
return IsCall();
}
- virtual void SetDeferredLazyDeoptimizationEnvironment(LEnvironment* env) { }
-
// Interface to the register allocator and iterators.
bool IsMarkedAsCall() const { return IsCall(); }
};
-class LInstanceOfKnownGlobal final : public LTemplateInstruction<1, 2, 1> {
+class LHasInPrototypeChainAndBranch final : public LControlInstruction<2, 0> {
public:
- LInstanceOfKnownGlobal(LOperand* context, LOperand* value, LOperand* temp) {
- inputs_[0] = context;
- inputs_[1] = value;
- temps_[0] = temp;
+ LHasInPrototypeChainAndBranch(LOperand* object, LOperand* prototype) {
+ inputs_[0] = object;
+ inputs_[1] = prototype;
}
- LOperand* context() { return inputs_[0]; }
- LOperand* value() { return inputs_[1]; }
- LOperand* temp() { return temps_[0]; }
-
- DECLARE_CONCRETE_INSTRUCTION(InstanceOfKnownGlobal,
- "instance-of-known-global")
- DECLARE_HYDROGEN_ACCESSOR(InstanceOfKnownGlobal)
-
- Handle<JSFunction> function() const { return hydrogen()->function(); }
- LEnvironment* GetDeferredLazyDeoptimizationEnvironment() {
- return lazy_deopt_env_;
- }
- virtual void SetDeferredLazyDeoptimizationEnvironment(
- LEnvironment* env) override {
- lazy_deopt_env_ = env;
- }
+ LOperand* object() const { return inputs_[0]; }
+ LOperand* prototype() const { return inputs_[1]; }
- private:
- LEnvironment* lazy_deopt_env_;
+ DECLARE_CONCRETE_INSTRUCTION(HasInPrototypeChainAndBranch,
+ "has-in-prototype-chain-and-branch")
+ DECLARE_HYDROGEN_ACCESSOR(HasInPrototypeChainAndBranch)
};
}
-void MacroAssembler::TryGetFunctionPrototype(Register function,
- Register result,
- Label* miss,
- bool miss_on_bound_function) {
- Label non_instance;
- if (miss_on_bound_function) {
- // Check that the receiver isn't a smi.
- testl(function, Immediate(kSmiTagMask));
- j(zero, miss);
-
- // Check that the function really is a function.
- CmpObjectType(function, JS_FUNCTION_TYPE, result);
- j(not_equal, miss);
-
- movp(kScratchRegister,
- FieldOperand(function, JSFunction::kSharedFunctionInfoOffset));
- // It's not smi-tagged (stored in the top half of a smi-tagged 8-byte
- // field).
- TestBitSharedFunctionInfoSpecialField(kScratchRegister,
- SharedFunctionInfo::kCompilerHintsOffset,
- SharedFunctionInfo::kBoundFunction);
- j(not_zero, miss);
-
- // Make sure that the function has an instance prototype.
- testb(FieldOperand(result, Map::kBitFieldOffset),
- Immediate(1 << Map::kHasNonInstancePrototype));
- j(not_zero, &non_instance, Label::kNear);
- }
-
+void MacroAssembler::TryGetFunctionPrototype(Register function, Register result,
+ Label* miss) {
// Get the prototype or initial map from the function.
movp(result,
FieldOperand(function, JSFunction::kPrototypeOrInitialMapOffset));
// Get the prototype from the initial map.
movp(result, FieldOperand(result, Map::kPrototypeOffset));
- if (miss_on_bound_function) {
- jmp(&done, Label::kNear);
-
- // Non-instance prototype: Fetch prototype from constructor field
- // in initial map.
- bind(&non_instance);
- GetMapConstructor(result, result, kScratchRegister);
- }
-
// All done.
bind(&done);
}
// function and jumps to the miss label if the fast checks fail. The
// function register will be untouched; the other register may be
// clobbered.
- void TryGetFunctionPrototype(Register function,
- Register result,
- Label* miss,
- bool miss_on_bound_function = false);
+ void TryGetFunctionPrototype(Register function, Register result, Label* miss);
// Picks out an array index from the hash field.
// Register use:
--- /dev/null
+// Copyright 2015 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+function Bar() { }
+function Baz() { }
+Baz.prototype = { __proto__: new Bar() }
+var x = new Baz();
+function foo(y) { return y instanceof Bar; }
+assertTrue(foo(x));
+Baz.prototype.__proto__ = null;
+assertFalse(foo(x));
--- /dev/null
+// Copyright 2015 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+function Foo() {}
+var x = new Foo();
+Foo.prototype = 1;
+function foo() { return x instanceof Foo; }
+assertThrows(foo, TypeError);
--- /dev/null
+// Copyright 2015 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+function Foo() {}
+var x = new Foo();
+function foo() { return x instanceof Foo; }
+assertTrue(foo());
+Foo.prototype = 1;
+assertThrows(foo, TypeError);
PASS Object.prototype.valueOf.call(undefined) threw exception TypeError: Cannot convert undefined or null to object.
PASS Object.prototype.hasOwnProperty.call(undefined, 'hasOwnProperty') threw exception TypeError: Cannot convert undefined or null to object.
PASS Object.prototype.propertyIsEnumerable.call(undefined, 'propertyIsEnumerable') threw exception TypeError: Cannot convert undefined or null to object.
-PASS Object.prototype.isPrototypeOf.call(undefined, this) threw exception TypeError: Object.prototype.isPrototypeOf called on null or undefined.
+PASS Object.prototype.isPrototypeOf.call(undefined, this) threw exception TypeError: Cannot convert undefined or null to object.
PASS successfullyParsed is true
TEST COMPLETE