}
+void CompareNilICStub::InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor) {
+ static Register registers[] = { r0 };
+ descriptor->register_param_count_ = 1;
+ descriptor->register_params_ = registers;
+ descriptor->deoptimization_handler_ =
+ FUNCTION_ADDR(CompareNilIC_Miss);
+ descriptor->miss_handler_ =
+ ExternalReference(IC_Utility(IC::kCompareNilIC_Miss), isolate);
+}
+
+
static void InitializeArrayConstructorDescriptor(Isolate* isolate,
CodeStubInterfaceDescriptor* descriptor) {
// register state
VisitForAccumulatorValue(sub_expr);
PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
- Heap::RootListIndex nil_value = nil == kNullValue ?
- Heap::kNullValueRootIndex :
- Heap::kUndefinedValueRootIndex;
- __ LoadRoot(r1, nil_value);
- __ cmp(r0, r1);
- if (expr->op() == Token::EQ_STRICT) {
- Split(eq, if_true, if_false, fall_through);
- } else {
- Heap::RootListIndex other_nil_value = nil == kNullValue ?
- Heap::kUndefinedValueRootIndex :
- Heap::kNullValueRootIndex;
- __ b(eq, if_true);
- __ LoadRoot(r1, other_nil_value);
+ EqualityKind kind = expr->op() == Token::EQ_STRICT
+ ? kStrictEquality : kNonStrictEquality;
+ if (kind == kStrictEquality) {
+ Heap::RootListIndex nil_value = nil == kNullValue ?
+ Heap::kNullValueRootIndex :
+ Heap::kUndefinedValueRootIndex;
+ __ LoadRoot(r1, nil_value);
__ cmp(r0, r1);
- __ b(eq, if_true);
- __ JumpIfSmi(r0, if_false);
- // It can be an undetectable object.
- __ ldr(r1, FieldMemOperand(r0, HeapObject::kMapOffset));
- __ ldrb(r1, FieldMemOperand(r1, Map::kBitFieldOffset));
- __ and_(r1, r1, Operand(1 << Map::kIsUndetectable));
- __ cmp(r1, Operand(1 << Map::kIsUndetectable));
Split(eq, if_true, if_false, fall_through);
+ } else {
+ Handle<Code> ic = CompareNilICStub::GetUninitialized(isolate(),
+ kNonStrictEquality,
+ nil);
+ CallIC(ic, RelocInfo::CODE_TARGET, expr->CompareOperationFeedbackId());
+ __ cmp(r0, Operand(0));
+ Split(ne, if_true, if_false, fall_through);
}
context()->Plug(if_true, if_false);
}
}
+bool Expression::IsUndefinedLiteral() {
+ return AsLiteral() != NULL && AsLiteral()->handle()->IsUndefined();
+}
+
+
VariableProxy::VariableProxy(Isolate* isolate, Variable* var)
: Expression(isolate),
name_(var->name()),
}
-// Check for the pattern: void <literal> equals <expression>
+// Check for the pattern: void <literal> equals <expression> or
+// undefined equals <expression>
static bool MatchLiteralCompareUndefined(Expression* left,
Token::Value op,
Expression* right,
*expr = right;
return true;
}
+ if (left->IsUndefinedLiteral() && Token::IsEqualityOp(op)) {
+ *expr = right;
+ return true;
+ }
return false;
}
// True iff the expression is the null literal.
bool IsNullLiteral();
+ // True iff the expression is the undefined literal.
+ bool IsUndefinedLiteral();
+
// Type feedback information for assignments and properties.
virtual bool IsMonomorphic() {
UNREACHABLE();
stack_pop_count->ClearFlag(HValue::kCanOverflow);
}
- HReturn* hreturn_instruction = new(zone) HReturn(return_value,
- context_,
- stack_pop_count);
- current_block()->Finish(hreturn_instruction);
+ if (!current_block()->IsFinished()) {
+ HReturn* hreturn_instruction = new(zone) HReturn(return_value,
+ context_,
+ stack_pop_count);
+ current_block()->Finish(hreturn_instruction);
+ }
return true;
}
return DoGenerateCode(this);
}
+
+template <>
+HValue* CodeStubGraphBuilder<CompareNilICStub>::BuildCodeUninitializedStub() {
+ CompareNilICStub* stub = casted_stub();
+ HIfContinuation continuation;
+ Handle<Map> sentinel_map(graph()->isolate()->heap()->meta_map());
+ BuildCompareNil(GetParameter(0), stub->GetKind(),
+ stub->GetTypes(), sentinel_map,
+ RelocInfo::kNoPosition, &continuation);
+ IfBuilder if_nil(this, &continuation);
+ if_nil.Then();
+ if (continuation.IsFalseReachable()) {
+ if_nil.Else();
+ if_nil.Return(graph()->GetConstantSmi0());
+ }
+ if_nil.End();
+ return continuation.IsTrueReachable()
+ ? graph()->GetConstantSmi1()
+ : graph()->GetConstantUndefined();
+}
+
+
+Handle<Code> CompareNilICStub::GenerateCode() {
+ return DoGenerateCode(this);
+}
+
} } // namespace v8::internal
}
+CompareNilICStub::Types CompareNilICStub::GetPatchedICFlags(
+ Code::ExtraICState extra_ic_state,
+ Handle<Object> object,
+ bool* already_monomorphic) {
+ Types types = TypesField::decode(extra_ic_state);
+ NilValue nil = NilValueField::decode(extra_ic_state);
+ EqualityKind kind = EqualityKindField::decode(extra_ic_state);
+ ASSERT(types != CompareNilICStub::kFullCompare);
+ *already_monomorphic =
+ (types & CompareNilICStub::kCompareAgainstMonomorphicMap) != 0;
+ if (kind == kStrictEquality) {
+ if (nil == kNullValue) {
+ return CompareNilICStub::kCompareAgainstNull;
+ } else {
+ return CompareNilICStub::kCompareAgainstUndefined;
+ }
+ } else {
+ if (object->IsNull()) {
+ types = static_cast<CompareNilICStub::Types>(
+ types | CompareNilICStub::kCompareAgainstNull);
+ } else if (object->IsUndefined()) {
+ types = static_cast<CompareNilICStub::Types>(
+ types | CompareNilICStub::kCompareAgainstUndefined);
+ } else if (object->IsUndetectableObject() || !object->IsHeapObject()) {
+ types = CompareNilICStub::kFullCompare;
+ } else if ((types & CompareNilICStub::kCompareAgainstMonomorphicMap) != 0) {
+ types = CompareNilICStub::kFullCompare;
+ } else {
+ types = static_cast<CompareNilICStub::Types>(
+ types | CompareNilICStub::kCompareAgainstMonomorphicMap);
+ }
+ }
+ return types;
+}
+
+
void InstanceofStub::PrintName(StringStream* stream) {
const char* args = "";
if (HasArgsInRegisters()) {
V(StringCompare) \
V(Compare) \
V(CompareIC) \
+ V(CompareNilIC) \
V(MathPow) \
V(StringLength) \
V(FunctionPrototype) \
};
+class CompareNilICStub : public HydrogenCodeStub {
+ public:
+ enum Types {
+ kCompareAgainstNull = 1 << 0,
+ kCompareAgainstUndefined = 1 << 1,
+ kCompareAgainstMonomorphicMap = 1 << 2,
+ kCompareAgainstUndetectable = 1 << 3,
+ kFullCompare = kCompareAgainstNull | kCompareAgainstUndefined |
+ kCompareAgainstUndetectable
+ };
+
+ CompareNilICStub(EqualityKind kind, NilValue nil, Types types)
+ : HydrogenCodeStub(CODE_STUB_IS_NOT_MISS), bit_field_(0) {
+ bit_field_ = EqualityKindField::encode(kind) |
+ NilValueField::encode(nil) |
+ TypesField::encode(types);
+ }
+
+ virtual InlineCacheState GetICState() {
+ Types types = GetTypes();
+ if (types == kFullCompare) {
+ return MEGAMORPHIC;
+ } else if ((types & kCompareAgainstMonomorphicMap) != 0) {
+ return MONOMORPHIC;
+ } else {
+ return PREMONOMORPHIC;
+ }
+ }
+
+ virtual Code::Kind GetCodeKind() const { return Code::COMPARE_NIL_IC; }
+
+ Handle<Code> GenerateCode();
+
+ static Handle<Code> GetUninitialized(Isolate* isolate,
+ EqualityKind kind,
+ NilValue nil) {
+ return CompareNilICStub(kind, nil).GetCode(isolate);
+ }
+
+ virtual void InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor);
+
+ static void InitializeForIsolate(Isolate* isolate) {
+ CompareNilICStub compare_stub(kStrictEquality, kNullValue);
+ compare_stub.InitializeInterfaceDescriptor(
+ isolate,
+ isolate->code_stub_interface_descriptor(CodeStub::CompareNilIC));
+ }
+
+ virtual Code::ExtraICState GetExtraICState() {
+ return bit_field_;
+ }
+
+ EqualityKind GetKind() { return EqualityKindField::decode(bit_field_); }
+ NilValue GetNilValue() { return NilValueField::decode(bit_field_); }
+ Types GetTypes() { return TypesField::decode(bit_field_); }
+
+ static Types TypesFromExtraICState(
+ Code::ExtraICState state) {
+ return TypesField::decode(state);
+ }
+ static EqualityKind EqualityKindFromExtraICState(
+ Code::ExtraICState state) {
+ return EqualityKindField::decode(state);
+ }
+ static NilValue NilValueFromExtraICState(Code::ExtraICState state) {
+ return NilValueField::decode(state);
+ }
+
+ static Types GetPatchedICFlags(Code::ExtraICState extra_ic_state,
+ Handle<Object> object,
+ bool* already_monomorphic);
+
+ private:
+ friend class CompareNilIC;
+
+ class EqualityKindField : public BitField<EqualityKind, 0, 1> {};
+ class NilValueField : public BitField<NilValue, 1, 1> {};
+ class TypesField : public BitField<Types, 3, 4> {};
+
+ CompareNilICStub(EqualityKind kind, NilValue nil)
+ : HydrogenCodeStub(CODE_STUB_IS_MISS), bit_field_(0) {
+ bit_field_ = EqualityKindField::encode(kind) |
+ NilValueField::encode(nil);
+ }
+
+ virtual CodeStub::Major MajorKey() { return CompareNilIC; }
+ virtual int NotMissMinorKey() { return bit_field_; }
+
+ int bit_field_;
+
+ DISALLOW_COPY_AND_ASSIGN(CompareNilICStub);
+};
+
+
class CEntryStub : public PlatformCodeStub {
public:
explicit CEntryStub(int result_size,
return Code::ComputeFlags(code_stub()->GetCodeKind(),
code_stub()->GetICState(),
code_stub()->GetExtraICState(),
- Code::NORMAL,
- 0);
+ Code::NORMAL, -1);
} else {
return Code::ComputeFlags(Code::OPTIMIZED_FUNCTION);
}
return Representation::Tagged();
}
+ virtual Representation observed_input_representation(int index) {
+ return Representation::Tagged();
+ }
+
DECLARE_CONCRETE_INSTRUCTION(CompareObjectEqAndBranch)
};
}
+HConstant* HGraph::GetConstantSmi(SetOncePointer<HConstant>* pointer,
+ int32_t value) {
+ if (!pointer->is_set()) {
+ HConstant* constant =
+ new(zone()) HConstant(Handle<Object>(Smi::FromInt(value), isolate()),
+ Representation::Tagged());
+ constant->InsertAfter(GetConstantUndefined());
+ pointer->set(constant);
+ }
+ return pointer->get();
+}
+
+
HConstant* HGraph::GetConstant0() {
return GetConstantInt32(&constant_0_, 0);
}
DEFINE_GET_CONSTANT(True, true, HType::Boolean(), true)
DEFINE_GET_CONSTANT(False, false, HType::Boolean(), false)
DEFINE_GET_CONSTANT(Hole, the_hole, HType::Tagged(), false)
+DEFINE_GET_CONSTANT(Null, null, HType::Tagged(), false)
+
+
+HConstant* HGraph::GetConstantSmi0() {
+ return GetConstantSmi(&constant_smi_0_, 0);
+}
+
+
+HConstant* HGraph::GetConstantSmi1() {
+ return GetConstantSmi(&constant_smi_1_, 1);
+}
+
#undef DEFINE_GET_CONSTANT
HBasicBlock* true_block = last_true_block_ == NULL
? first_true_block_
: last_true_block_;
- HBasicBlock* false_block =
- did_else_ ? builder_->current_block() : first_false_block_;
+ HBasicBlock* false_block = did_else_ && (first_false_block_ != NULL)
+ ? builder_->current_block()
+ : first_false_block_;
continuation->Capture(true_block, false_block, position_);
captured_ = true;
End();
void HGraphBuilder::IfBuilder::Deopt() {
- ASSERT(!(did_then_ ^ did_else_));
HBasicBlock* block = builder_->current_block();
block->FinishExitWithDeoptimization(HDeoptimize::kUseAll);
if (did_else_) {
}
+void HGraphBuilder::IfBuilder::Return(HValue* value) {
+ HBasicBlock* block = builder_->current_block();
+ block->Finish(new(zone()) HReturn(value,
+ builder_->environment()->LookupContext(),
+ builder_->graph()->GetConstantMinus1()));
+ if (did_else_) {
+ first_false_block_ = NULL;
+ } else {
+ first_true_block_ = NULL;
+ }
+}
+
+
void HGraphBuilder::IfBuilder::End() {
if (!captured_) {
ASSERT(did_then_);
}
+void HGraphBuilder::BuildCompareNil(
+ HValue* value,
+ EqualityKind kind,
+ CompareNilICStub::Types types,
+ Handle<Map> map,
+ int position,
+ HIfContinuation* continuation) {
+ IfBuilder if_nil(this, position);
+ bool needs_or = false;
+ if ((types & CompareNilICStub::kCompareAgainstNull) != 0) {
+ if (needs_or) if_nil.Or();
+ if_nil.If<HCompareObjectEqAndBranch>(value, graph()->GetConstantNull());
+ needs_or = true;
+ }
+ if ((types & CompareNilICStub::kCompareAgainstUndefined) != 0) {
+ if (needs_or) if_nil.Or();
+ if_nil.If<HCompareObjectEqAndBranch>(value,
+ graph()->GetConstantUndefined());
+ needs_or = true;
+ }
+ // Handle either undetectable or monomorphic, not both.
+ ASSERT(((types & CompareNilICStub::kCompareAgainstUndetectable) == 0) ||
+ ((types & CompareNilICStub::kCompareAgainstMonomorphicMap) == 0));
+ if ((types & CompareNilICStub::kCompareAgainstUndetectable) != 0) {
+ if (needs_or) if_nil.Or();
+ if_nil.If<HIsUndetectableAndBranch>(value);
+ } else {
+ if_nil.Then();
+ if_nil.Else();
+ if ((types & CompareNilICStub::kCompareAgainstMonomorphicMap) != 0) {
+ BuildCheckNonSmi(value);
+ // For ICs, the map checked below is a sentinel map that gets replaced by
+ // the monomorphic map when the code is used as a template to generate a
+ // new IC. For optimized functions, there is no sentinel map, the map
+ // emitted below is the actual monomorphic map.
+ BuildCheckMap(value, map);
+ } else {
+ if (kind == kNonStrictEquality) {
+ if_nil.Deopt();
+ }
+ }
+ }
+
+ if_nil.CaptureContinuation(continuation);
+}
+
+
HOptimizedGraphBuilder::HOptimizedGraphBuilder(CompilationInfo* info,
TypeFeedbackOracle* oracle)
: HGraphBuilder(info),
ASSERT(current_block()->HasPredecessor());
EqualityKind kind =
expr->op() == Token::EQ_STRICT ? kStrictEquality : kNonStrictEquality;
- HIsNilAndBranch* instr = new(zone()) HIsNilAndBranch(value, kind, nil);
- instr->set_position(expr->position());
- return ast_context()->ReturnControl(instr, expr->id());
+ HIfContinuation continuation;
+ TypeFeedbackId id = expr->CompareOperationFeedbackId();
+ CompareNilICStub::Types types;
+ if (kind == kStrictEquality) {
+ if (nil == kNullValue) {
+ types = CompareNilICStub::kCompareAgainstNull;
+ } else {
+ types = CompareNilICStub::kCompareAgainstUndefined;
+ }
+ } else {
+ types = static_cast<CompareNilICStub::Types>(
+ oracle()->CompareNilTypes(id));
+ if (types == 0) types = CompareNilICStub::kFullCompare;
+ }
+ Handle<Map> map_handle(oracle()->CompareNilMonomorphicReceiverType(id));
+ BuildCompareNil(value, kind, types, map_handle,
+ expr->position(), &continuation);
+ return ast_context()->ReturnContinuation(&continuation, expr->id());
}
HConstant* GetConstantUndefined() const { return undefined_constant_.get(); }
HConstant* GetConstant0();
HConstant* GetConstant1();
+ HConstant* GetConstantSmi0();
+ HConstant* GetConstantSmi1();
HConstant* GetConstantMinus1();
HConstant* GetConstantTrue();
HConstant* GetConstantFalse();
HConstant* GetConstantHole();
+ HConstant* GetConstantNull();
HConstant* GetInvalidContext();
HBasicBlock* CreateBasicBlock();
private:
HConstant* GetConstantInt32(SetOncePointer<HConstant>* pointer,
int32_t integer_value);
+ HConstant* GetConstantSmi(SetOncePointer<HConstant>* pointer,
+ int32_t integer_value);
void MarkAsDeoptimizingRecursively(HBasicBlock* block);
void NullifyUnreachableInstructions();
SetOncePointer<HConstant> undefined_constant_;
SetOncePointer<HConstant> constant_0_;
SetOncePointer<HConstant> constant_1_;
+ SetOncePointer<HConstant> constant_smi_0_;
+ SetOncePointer<HConstant> constant_smi_1_;
SetOncePointer<HConstant> constant_minus1_;
SetOncePointer<HConstant> constant_true_;
SetOncePointer<HConstant> constant_false_;
SetOncePointer<HConstant> constant_the_hole_;
+ SetOncePointer<HConstant> constant_null_;
SetOncePointer<HConstant> constant_invalid_context_;
SetOncePointer<HArgumentsObject> arguments_object_;
HBasicBlock* false_branch,
int position) {
ASSERT(!continuation_captured_);
- ASSERT(true_branch != NULL || false_branch != NULL);
true_branch_ = true_branch;
false_branch_ = false_branch;
position_ = position;
End();
}
+ void Return(HValue* value);
+
private:
void AddCompare(HControlInstruction* compare);
ElementsKind kind,
int length);
+ void BuildCompareNil(
+ HValue* value,
+ EqualityKind kind,
+ CompareNilICStub::Types types,
+ Handle<Map> map,
+ int position,
+ HIfContinuation* continuation);
+
private:
HGraphBuilder();
CompilationInfo* info_;
}
+void CompareNilICStub::InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor) {
+ static Register registers[] = { eax };
+ descriptor->register_param_count_ = 1;
+ descriptor->register_params_ = registers;
+ descriptor->deoptimization_handler_ =
+ FUNCTION_ADDR(CompareNilIC_Miss);
+ descriptor->miss_handler_ =
+ ExternalReference(IC_Utility(IC::kCompareNilIC_Miss), isolate);
+}
+
+
#define __ ACCESS_MASM(masm)
VisitForAccumulatorValue(sub_expr);
PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
- Handle<Object> nil_value = nil == kNullValue ?
- isolate()->factory()->null_value() :
- isolate()->factory()->undefined_value();
- __ cmp(eax, nil_value);
- if (expr->op() == Token::EQ_STRICT) {
+
+ EqualityKind kind = expr->op() == Token::EQ_STRICT
+ ? kStrictEquality : kNonStrictEquality;
+ Handle<Object> nil_value = nil == kNullValue
+ ? isolate()->factory()->null_value()
+ : isolate()->factory()->undefined_value();
+ if (kind == kStrictEquality) {
+ __ cmp(eax, nil_value);
Split(equal, if_true, if_false, fall_through);
} else {
- Handle<Object> other_nil_value = nil == kNullValue ?
- isolate()->factory()->undefined_value() :
- isolate()->factory()->null_value();
- __ j(equal, if_true);
- __ cmp(eax, other_nil_value);
- __ j(equal, if_true);
- __ JumpIfSmi(eax, if_false);
- // It can be an undetectable object.
- __ mov(edx, FieldOperand(eax, HeapObject::kMapOffset));
- __ movzx_b(edx, FieldOperand(edx, Map::kBitFieldOffset));
- __ test(edx, Immediate(1 << Map::kIsUndetectable));
+ Handle<Code> ic = CompareNilICStub::GetUninitialized(isolate(),
+ kNonStrictEquality,
+ nil);
+ CallIC(ic, RelocInfo::CODE_TARGET, expr->CompareOperationFeedbackId());
+ __ test(eax, eax);
Split(not_zero, if_true, if_false, fall_through);
}
context()->Plug(if_true, if_false);
case Code::CALL_IC: return CallIC::Clear(address, target);
case Code::KEYED_CALL_IC: return KeyedCallIC::Clear(address, target);
case Code::COMPARE_IC: return CompareIC::Clear(address, target);
+ case Code::COMPARE_NIL_IC: return CompareNilIC::Clear(address, target);
case Code::UNARY_OP_IC:
case Code::BINARY_OP_IC:
case Code::TO_BOOLEAN_IC:
}
+Code* CompareNilIC::GetRawUninitialized(EqualityKind kind,
+ NilValue nil) {
+ CompareNilICStub stub(kind, nil);
+ Code* code = NULL;
+ CHECK(stub.FindCodeInCache(&code, Isolate::Current()));
+ return code;
+}
+
+
+void CompareNilIC::Clear(Address address, Code* target) {
+ if (target->ic_state() == UNINITIALIZED) return;
+ Code::ExtraICState state = target->extended_extra_ic_state();
+
+ EqualityKind kind =
+ CompareNilICStub::EqualityKindFromExtraICState(state);
+ NilValue nil =
+ CompareNilICStub::NilValueFromExtraICState(state);
+
+ SetTargetAtAddress(address, GetRawUninitialized(kind, nil));
+}
+
+
+MaybeObject* CompareNilIC::DoCompareNilSlow(EqualityKind kind,
+ NilValue nil,
+ Handle<Object> object) {
+ if (kind == kStrictEquality) {
+ if (nil == kNullValue) {
+ return Smi::FromInt(object->IsNull());
+ } else {
+ return Smi::FromInt(object->IsUndefined());
+ }
+ }
+ if (object->IsNull() || object->IsUndefined()) {
+ return Smi::FromInt(true);
+ }
+ return Smi::FromInt(object->IsUndetectableObject());
+}
+
+
+MaybeObject* CompareNilIC::CompareNil(Handle<Object> object) {
+ Code::ExtraICState extra_ic_state = target()->extended_extra_ic_state();
+
+ // Extract the current supported types from the patched IC and calculate what
+ // types must be supported as a result of the miss.
+ bool already_monomorphic;
+ CompareNilICStub::Types types =
+ CompareNilICStub::GetPatchedICFlags(extra_ic_state,
+ object, &already_monomorphic);
+
+ EqualityKind kind =
+ CompareNilICStub::EqualityKindFromExtraICState(extra_ic_state);
+ NilValue nil =
+ CompareNilICStub::NilValueFromExtraICState(extra_ic_state);
+
+ // Find or create the specialized stub to support the new set of types.
+ CompareNilICStub stub(kind, nil, types);
+ Handle<Code> code;
+ if ((types & CompareNilICStub::kCompareAgainstMonomorphicMap) != 0) {
+ Handle<Map> monomorphic_map(already_monomorphic
+ ? target()->FindFirstMap()
+ : HeapObject::cast(*object)->map());
+ code = isolate()->stub_cache()->ComputeCompareNil(monomorphic_map,
+ nil,
+ stub.GetTypes());
+ } else {
+ code = stub.GetCode(isolate());
+ }
+
+ patch(*code);
+
+ return DoCompareNilSlow(kind, nil, object);
+}
+
+
+void CompareNilIC::patch(Code* code) {
+ set_target(code);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, CompareNilIC_Miss) {
+ HandleScope scope(isolate);
+ Handle<Object> object = args.at<Object>(0);
+ CompareNilIC ic(isolate);
+ return ic.CompareNil(object);
+}
+
+
RUNTIME_FUNCTION(MaybeObject*, Unreachable) {
UNREACHABLE();
CHECK(false);
ICU(UnaryOp_Patch) \
ICU(BinaryOp_Patch) \
ICU(CompareIC_Miss) \
+ ICU(CompareNilIC_Miss) \
ICU(Unreachable) \
ICU(ToBoolean_Patch)
//
};
+class CompareNilIC: public IC {
+ public:
+ explicit CompareNilIC(Isolate* isolate) : IC(EXTRA_CALL_FRAME, isolate) {}
+
+ MUST_USE_RESULT MaybeObject* CompareNil(Handle<Object> object);
+
+ static Handle<Code> GetUninitialized();
+
+ static Code* GetRawUninitialized(EqualityKind kind, NilValue nil);
+
+ static void Clear(Address address, Code* target);
+
+ void patch(Code* code);
+
+ static MUST_USE_RESULT MaybeObject* DoCompareNilSlow(EqualityKind kind,
+ NilValue nil,
+ Handle<Object> object);
+};
+
+
class ToBooleanIC: public IC {
public:
explicit ToBooleanIC(Isolate* isolate) : IC(NO_EXTRA_FRAME, isolate) { }
DECLARE_RUNTIME_FUNCTION(MaybeObject*, KeyedLoadIC_MissFromStubFailure);
DECLARE_RUNTIME_FUNCTION(MaybeObject*, KeyedStoreIC_MissFromStubFailure);
+DECLARE_RUNTIME_FUNCTION(MaybeObject*, CompareNilIC_Miss);
+
} } // namespace v8::internal
DONT_TRACK_ALLOCATION_SITE, 0);
stub.InitializeInterfaceDescriptor(
this, code_stub_interface_descriptor(CodeStub::FastCloneShallowArray));
+ CompareNilICStub::InitializeForIsolate(this);
}
if (FLAG_parallel_recompilation) optimizing_compiler_thread_.Start();
can_eliminate = false;
}
}
-
if (can_eliminate) {
label->set_replacement(GetLabel(goto_instr->block_id()));
}
case Code::UNARY_OP_IC: // fall through
case Code::BINARY_OP_IC: // fall through
case Code::COMPARE_IC: // fall through
+ case Code::COMPARE_NIL_IC: // fall through
case Code::TO_BOOLEAN_IC: // fall through
case Code::STUB:
description =
}
+Code::ExtraICState Code::extended_extra_ic_state() {
+ ASSERT(is_inline_cache_stub() || ic_state() == DEBUG_STUB);
+ return ExtractExtendedExtraICStateFromFlags(flags());
+}
+
+
Code::StubType Code::type() {
return ExtractTypeFromFlags(flags());
}
kind() == UNARY_OP_IC ||
kind() == BINARY_OP_IC ||
kind() == COMPARE_IC ||
+ kind() == COMPARE_NIL_IC ||
kind() == LOAD_IC ||
kind() == KEYED_LOAD_IC ||
kind() == TO_BOOLEAN_IC);
kind() == UNARY_OP_IC ||
kind() == BINARY_OP_IC ||
kind() == COMPARE_IC ||
+ kind() == COMPARE_NIL_IC ||
kind() == LOAD_IC ||
kind() == KEYED_LOAD_IC ||
kind() == STORE_IC ||
int argc,
InlineCacheHolderFlag holder) {
ASSERT(argc <= Code::kMaxArguments);
+ // Since the extended extra ic state overlaps with the argument count
+ // for CALL_ICs, do so checks to make sure that they don't interfere.
+ ASSERT((kind != Code::CALL_IC &&
+ kind != Code::KEYED_CALL_IC) ||
+ (ExtraICStateField::encode(extra_ic_state) | true));
// Compute the bit mask.
unsigned int bits = KindField::encode(kind)
| ICStateField::encode(ic_state)
| TypeField::encode(type)
- | ExtraICStateField::encode(extra_ic_state)
- | (argc << kArgumentsCountShift)
+ | ExtendedExtraICStateField::encode(extra_ic_state)
| CacheHolderField::encode(holder);
+ // TODO(danno): This is a bit of a hack right now since there are still
+ // clients of this API that pass "extra" values in for argc. These clients
+ // should be retrofitted to used ExtendedExtraICState.
+ if (kind != Code::COMPARE_NIL_IC) {
+ bits |= (argc << kArgumentsCountShift);
+ }
return static_cast<Flags>(bits);
}
}
+Code::ExtraICState Code::ExtractExtendedExtraICStateFromFlags(
+ Flags flags) {
+ return ExtendedExtraICStateField::decode(flags);
+}
+
+
Code::StubType Code::ExtractTypeFromFlags(Flags flags) {
return TypeField::decode(flags);
}
int Code::stub_info() {
- ASSERT(kind() == COMPARE_IC || kind() == BINARY_OP_IC || kind() == LOAD_IC);
+ ASSERT(kind() == COMPARE_IC || kind() == COMPARE_NIL_IC ||
+ kind() == BINARY_OP_IC || kind() == LOAD_IC);
Object* value = READ_FIELD(this, kTypeFeedbackInfoOffset);
return Smi::cast(value)->value();
}
void Code::set_stub_info(int value) {
ASSERT(kind() == COMPARE_IC ||
+ kind() == COMPARE_NIL_IC ||
kind() == BINARY_OP_IC ||
kind() == STUB ||
kind() == LOAD_IC ||
VisitExternalReferences(p, p + 1);
}
+byte Code::compare_nil_state() {
+ ASSERT(is_compare_nil_ic_stub());
+ return CompareNilICStub::TypesFromExtraICState(extended_extra_ic_state());
+}
+
+
void Code::InvalidateRelocation() {
set_relocation_info(GetHeap()->empty_byte_array());
}
}
+void Code::ReplaceFirstMap(Map* replace_with) {
+ ASSERT(is_inline_cache_stub());
+ AssertNoAllocation no_allocation;
+ int mask = RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT);
+ for (RelocIterator it(this, mask); !it.done(); it.next()) {
+ RelocInfo* info = it.rinfo();
+ Object* object = info->target_object();
+ if (object->IsMap()) {
+ info->set_target_object(replace_with);
+ return;
+ }
+ }
+ UNREACHABLE();
+}
+
+
void Code::FindAllMaps(MapHandleList* maps) {
ASSERT(is_inline_cache_stub());
AssertNoAllocation no_allocation;
case UNARY_OP_IC: return "UNARY_OP_IC";
case BINARY_OP_IC: return "BINARY_OP_IC";
case COMPARE_IC: return "COMPARE_IC";
+ case COMPARE_NIL_IC: return "COMPARE_NIL_IC";
case TO_BOOLEAN_IC: return "TO_BOOLEAN_IC";
}
UNREACHABLE();
V(UNARY_OP_IC) \
V(BINARY_OP_IC) \
V(COMPARE_IC) \
+ V(COMPARE_NIL_IC) \
V(TO_BOOLEAN_IC)
enum Kind {
inline Kind kind();
inline InlineCacheState ic_state(); // Only valid for IC stubs.
inline ExtraICState extra_ic_state(); // Only valid for IC stubs.
+ inline ExtraICState extended_extra_ic_state(); // Only valid for
+ // non-call IC stubs.
inline StubType type(); // Only valid for monomorphic IC stubs.
inline int arguments_count(); // Only valid for call IC stubs.
inline bool is_unary_op_stub() { return kind() == UNARY_OP_IC; }
inline bool is_binary_op_stub() { return kind() == BINARY_OP_IC; }
inline bool is_compare_ic_stub() { return kind() == COMPARE_IC; }
+ inline bool is_compare_nil_ic_stub() { return kind() == COMPARE_NIL_IC; }
inline bool is_to_boolean_ic_stub() { return kind() == TO_BOOLEAN_IC; }
// [major_key]: For kind STUB or BINARY_OP_IC, the major key.
inline byte to_boolean_state();
inline void set_to_boolean_state(byte value);
+ // [compare_nil]: For kind COMPARE_NIL_IC tells what state the stub is in.
+ byte compare_nil_state();
+
// [has_function_cache]: For kind STUB tells whether there is a function
// cache is passed to the stub.
inline bool has_function_cache();
// Find the first map in an IC stub.
Map* FindFirstMap();
void FindAllMaps(MapHandleList* maps);
+ void ReplaceFirstMap(Map* replace);
// Find the first code in an IC stub.
Code* FindFirstCode();
static inline Kind ExtractKindFromFlags(Flags flags);
static inline InlineCacheHolderFlag ExtractCacheHolderFromFlags(Flags flags);
static inline ExtraICState ExtractExtraICStateFromFlags(Flags flags);
+ static inline ExtraICState ExtractExtendedExtraICStateFromFlags(Flags flags);
static inline int ExtractArgumentsCountFromFlags(Flags flags);
static inline Flags RemoveTypeFromFlags(Flags flags);
class TypeField: public BitField<StubType, 3, 3> {};
class CacheHolderField: public BitField<InlineCacheHolderFlag, 6, 1> {};
class KindField: public BitField<Kind, 7, 4> {};
- class ExtraICStateField: public BitField<ExtraICState, 11, 5> {};
- class IsPregeneratedField: public BitField<bool, 16, 1> {};
+ class IsPregeneratedField: public BitField<bool, 11, 1> {};
+ class ExtraICStateField: public BitField<ExtraICState, 12, 5> {};
+ class ExtendedExtraICStateField: public BitField<ExtraICState, 12,
+ PlatformSmiTagging::kSmiValueSize - 12 + 1> {}; // NOLINT
+ STATIC_ASSERT(ExtraICStateField::kShift == ExtendedExtraICStateField::kShift);
// KindSpecificFlags1 layout (STUB and OPTIMIZED_FUNCTION)
static const int kStackSlotsFirstBit = 0;
PlatformSmiTagging::kSmiValueSize - Code::kArgumentsCountShift + 1;
static const int kMaxArguments = (1 << kArgumentsBits) - 1;
+ // ICs can use either argument count or ExtendedExtraIC, since their storage
+ // overlaps.
+ STATIC_ASSERT(ExtraICStateField::kShift +
+ ExtraICStateField::kSize + kArgumentsBits ==
+ ExtendedExtraICStateField::kShift +
+ ExtendedExtraICStateField::kSize);
+
// This constant should be encodable in an ARM instruction.
static const int kFlagsNotUsedInLookup =
TypeField::kMask | CacheHolderField::kMask;
CASE(UNARY_OP_IC);
CASE(BINARY_OP_IC);
CASE(COMPARE_IC);
+ CASE(COMPARE_NIL_IC);
CASE(TO_BOOLEAN_IC);
}
}
Handle<Code> StubCache::FindIC(Handle<Name> name,
- Handle<JSObject> stub_holder,
+ Handle<Map> stub_holder_map,
Code::Kind kind,
Code::StubType type,
- Code::ExtraICState extra_ic_state) {
- Code::Flags flags = Code::ComputeMonomorphicFlags(kind, extra_ic_state, type);
- Handle<Object> probe(stub_holder->map()->FindInCodeCache(*name, flags),
+ Code::ExtraICState extra_state) {
+ Code::Flags flags = Code::ComputeMonomorphicFlags(kind, extra_state, type);
+ Handle<Object> probe(stub_holder_map->FindInCodeCache(*name, flags),
isolate_);
if (probe->IsCode()) return Handle<Code>::cast(probe);
return Handle<Code>::null();
}
+Handle<Code> StubCache::FindIC(Handle<Name> name,
+ Handle<JSObject> stub_holder,
+ Code::Kind kind,
+ Code::StubType type,
+ Code::ExtraICState extra_ic_state) {
+ return FindIC(name, Handle<Map>(stub_holder->map()), kind,
+ type, extra_ic_state);
+}
+
+
Handle<Code> StubCache::FindHandler(Handle<Name> name,
Handle<JSObject> receiver,
Handle<JSObject> stub_holder,
Handle<JSGlobalPropertyCell> cell,
StrictModeFlag strict_mode) {
Handle<Code> stub = FindIC(
- name, receiver, Code::STORE_IC, Code::NORMAL, strict_mode);
+ name, Handle<JSObject>::cast(receiver),
+ Code::STORE_IC, Code::NORMAL, strict_mode);
if (!stub.is_null()) return stub;
StoreStubCompiler compiler(isolate_, strict_mode);
}
+Handle<Code> StubCache::ComputeCompareNil(Handle<Map> receiver_map,
+ NilValue nil,
+ CompareNilICStub::Types types) {
+ CompareNilICStub stub(kNonStrictEquality, nil, types);
+
+ Handle<String> name(isolate_->heap()->empty_string());
+ if (!receiver_map->is_shared()) {
+ Handle<Code> cached_ic = FindIC(name, receiver_map, Code::COMPARE_NIL_IC,
+ Code::NORMAL, stub.GetExtraICState());
+ if (!cached_ic.is_null()) return cached_ic;
+ }
+
+ Handle<Code> ic = stub.GetCode(isolate_);
+ // For monomorphic maps, use the code as a template, copying and replacing
+ // the monomorphic map that checks the object's type.
+ ic = isolate_->factory()->CopyCode(ic);
+ ic->ReplaceFirstMap(*receiver_map);
+
+ if (!receiver_map->is_shared()) {
+ Map::UpdateCodeCache(receiver_map, name, ic);
+ }
+
+ return ic;
+}
+
+
Handle<Code> StubCache::ComputeLoadElementPolymorphic(
MapHandleList* receiver_maps) {
Code::Flags flags = Code::ComputeFlags(Code::KEYED_LOAD_IC, POLYMORPHIC);
#include "allocation.h"
#include "arguments.h"
+#include "code-stubs.h"
#include "ic-inl.h"
#include "macro-assembler.h"
#include "objects.h"
Handle<JSObject> StubHolder(Handle<JSObject> receiver,
Handle<JSObject> holder);
+ Handle<Code> FindIC(Handle<Name> name,
+ Handle<Map> stub_holder_map,
+ Code::Kind kind,
+ Code::StubType type,
+ Code::ExtraICState extra_state = Code::kNoExtraICState);
+
Handle<Code> FindIC(Handle<Name> name,
Handle<JSObject> stub_holder,
Code::Kind kind,
// ---
+ Handle<Code> ComputeCompareNil(Handle<Map> receiver_map,
+ NilValue nil,
+ CompareNilICStub::Types types);
+
+ // ---
+
Handle<Code> ComputeLoadElementPolymorphic(MapHandleList* receiver_maps);
Handle<Code> ComputeStoreElementPolymorphic(MapHandleList* receiver_maps,
KeyedAccessStoreMode store_mode,
}
+Handle<Map> TypeFeedbackOracle::CompareNilMonomorphicReceiverType(
+ TypeFeedbackId id) {
+ Handle<Object> maybe_code = GetInfo(id);
+ if (maybe_code->IsCode()) {
+ Map* first_map = Handle<Code>::cast(maybe_code)->FindFirstMap();
+ if (first_map != NULL) return Handle<Map>(first_map);
+ }
+ return Handle<Map>();
+}
+
+
KeyedAccessStoreMode TypeFeedbackOracle::GetStoreMode(
TypeFeedbackId ast_id) {
Handle<Object> map_or_code = GetInfo(ast_id);
}
-byte TypeFeedbackOracle::ToBooleanTypes(TypeFeedbackId ast_id) {
- Handle<Object> object = GetInfo(ast_id);
+byte TypeFeedbackOracle::ToBooleanTypes(TypeFeedbackId id) {
+ Handle<Object> object = GetInfo(id);
return object->IsCode() ? Handle<Code>::cast(object)->to_boolean_state() : 0;
}
+byte TypeFeedbackOracle::CompareNilTypes(TypeFeedbackId id) {
+ Handle<Object> object = GetInfo(id);
+ if (object->IsCode() &&
+ Handle<Code>::cast(object)->is_compare_nil_ic_stub()) {
+ return Handle<Code>::cast(object)->compare_nil_state();
+ } else {
+ return CompareNilICStub::kFullCompare;
+ }
+}
+
+
// Things are a bit tricky here: The iterator for the RelocInfos and the infos
// themselves are not GC-safe, so we first get all infos, then we create the
// dictionary (possibly triggering GC), and finally we relocate the collected
case Code::BINARY_OP_IC:
case Code::COMPARE_IC:
case Code::TO_BOOLEAN_IC:
+ case Code::COMPARE_NIL_IC:
SetInfo(ast_id, target);
break;
bool IsForInFastCase(ForInStatement* expr);
Handle<Map> LoadMonomorphicReceiverType(Property* expr);
- Handle<Map> StoreMonomorphicReceiverType(TypeFeedbackId ast_id);
+ Handle<Map> StoreMonomorphicReceiverType(TypeFeedbackId id);
+ Handle<Map> CompareNilMonomorphicReceiverType(TypeFeedbackId id);
KeyedAccessStoreMode GetStoreMode(TypeFeedbackId ast_id);
// headers!! :-P
byte ToBooleanTypes(TypeFeedbackId ast_id);
+ // TODO(1571) We can't use CompareNilICStub::Types as the return value because
+ // of various cylces in our headers. Death to tons of implementations in
+ // headers!! :-P
+ byte CompareNilTypes(TypeFeedbackId ast_id);
+
// Get type information for arithmetic operations and compares.
TypeInfo UnaryType(UnaryOperation* expr);
void BinaryType(BinaryOperation* expr,
}
+void CompareNilICStub::InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor) {
+ static Register registers[] = { rax };
+ descriptor->register_param_count_ = 1;
+ descriptor->register_params_ = registers;
+ descriptor->deoptimization_handler_ =
+ FUNCTION_ADDR(CompareNilIC_Miss);
+ descriptor->miss_handler_ =
+ ExternalReference(IC_Utility(IC::kCompareNilIC_Miss), isolate);
+}
+
+
#define __ ACCESS_MASM(masm)
VisitForAccumulatorValue(sub_expr);
PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
- Heap::RootListIndex nil_value = nil == kNullValue ?
- Heap::kNullValueRootIndex :
- Heap::kUndefinedValueRootIndex;
- __ CompareRoot(rax, nil_value);
- if (expr->op() == Token::EQ_STRICT) {
+ EqualityKind kind = expr->op() == Token::EQ_STRICT
+ ? kStrictEquality : kNonStrictEquality;
+ if (kind == kStrictEquality) {
+ Heap::RootListIndex nil_value = nil == kNullValue ?
+ Heap::kNullValueRootIndex :
+ Heap::kUndefinedValueRootIndex;
+ __ CompareRoot(rax, nil_value);
Split(equal, if_true, if_false, fall_through);
} else {
- Heap::RootListIndex other_nil_value = nil == kNullValue ?
- Heap::kUndefinedValueRootIndex :
- Heap::kNullValueRootIndex;
- __ j(equal, if_true);
- __ CompareRoot(rax, other_nil_value);
- __ j(equal, if_true);
- __ JumpIfSmi(rax, if_false);
- // It can be an undetectable object.
- __ movq(rdx, FieldOperand(rax, HeapObject::kMapOffset));
- __ testb(FieldOperand(rdx, Map::kBitFieldOffset),
- Immediate(1 << Map::kIsUndetectable));
+ Handle<Code> ic = CompareNilICStub::GetUninitialized(isolate(),
+ kNonStrictEquality,
+ nil);
+ CallIC(ic, RelocInfo::CODE_TARGET, expr->CompareOperationFeedbackId());
+ __ testq(rax, rax);
Split(not_zero, if_true, if_false, fall_through);
}
context()->Plug(if_true, if_false);