Commit 3aeed04d authored by chunyang.dai's avatar chunyang.dai Committed by Commit bot

X87: Correctify instanceof and make it optimizable.

port 5d875a57 (r30342).

original commit message:

    The previous hack with HInstanceOfKnownGlobal was not only slower,
    but also very brittle and required a lot of weird hacks to support it. And
    what's even more important it wasn't even correct (because a map check
    on the lhs is never enough for instanceof).

    The new implementation provides a sane runtime implementation
    for InstanceOf plus a fast case in the InstanceOfStub, combined with
    a proper specialization in the case of a known global in CrankShaft,
    which does only the prototype chain walk (coupled with a code
    dependency on the known global).

    As a drive-by-fix: Also fix the incorrect Object.prototype.isPrototypeOf
    implementation.

R=weiliang.lin@intel.com
BUG=

Review URL: https://codereview.chromium.org/1318663003

Cr-Commit-Position: refs/heads/master@{#30376}
parent cbd4f5aa
......@@ -5023,13 +5023,13 @@ void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) {
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;
}
......
This diff is collapsed.
......@@ -53,8 +53,8 @@ const Register StoreGlobalViaContextDescriptor::SlotRegister() { return ebx; }
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; }
......
......@@ -2330,6 +2330,17 @@ void LCodeGen::EmitBranch(InstrType instr, Condition cc) {
}
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_);
......@@ -2895,121 +2906,41 @@ void LCodeGen::DoCmpMapAndBranch(LCmpMapAndBranch* instr) {
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,
const X87Stack& x87_stack)
: LDeferredCode(codegen, x87_stack), 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, x87_stack_);
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);
}
......
......@@ -138,8 +138,6 @@ class LCodeGen: public LCodeGenBase {
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,
......@@ -287,7 +285,9 @@ class LCodeGen: public LCodeGenBase {
// 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 EmitNumberUntagDNoSSE2(LNumberUntagD* instr, Register input,
Register temp, X87Register res_reg,
......
......@@ -980,22 +980,14 @@ void LChunkBuilder::AddInstruction(LInstruction* 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());
}
}
}
......@@ -1052,22 +1044,22 @@ LInstruction* LChunkBuilder::DoArgumentsElements(HArgumentsElements* elems) {
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);
}
......
......@@ -88,10 +88,10 @@ class LCodeGen;
V(GetCachedArrayIndex) \
V(Goto) \
V(HasCachedArrayIndexAndBranch) \
V(HasInPrototypeChainAndBranch) \
V(HasInstanceTypeAndBranch) \
V(InnerAllocatedObject) \
V(InstanceOf) \
V(InstanceOfKnownGlobal) \
V(InstructionGap) \
V(Integer32ToDouble) \
V(InvokeFunction) \
......@@ -237,8 +237,6 @@ class LInstruction : public ZoneObject {
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_); }
......@@ -1199,39 +1197,30 @@ class LInstanceOf final : public LTemplateInstruction<1, 3, 0> {
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)
};
......
......@@ -1761,33 +1761,8 @@ void MacroAssembler::GetMapConstructor(Register result, Register map,
}
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));
......@@ -1801,20 +1776,11 @@ void MacroAssembler::TryGetFunctionPrototype(Register function,
// 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);
}
......
......@@ -696,11 +696,8 @@ class MacroAssembler: public Assembler {
// 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:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment