Migrate Compare ICs to new type rep
(Does not yet use common AST expression type field.) R=jkummerow@chromium.org BUG= Review URL: https://codereview.chromium.org/16361015 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@15093 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
0a2f7acaab
commit
94f651bc1b
30
src/ast.cc
30
src/ast.cc
@ -503,19 +503,7 @@ void CountOperation::RecordTypeFeedback(TypeFeedbackOracle* oracle,
|
||||
|
||||
|
||||
void CaseClause::RecordTypeFeedback(TypeFeedbackOracle* oracle) {
|
||||
TypeInfo info = oracle->SwitchType(this);
|
||||
if (info.IsUninitialized()) info = TypeInfo::Unknown();
|
||||
if (info.IsSmi()) {
|
||||
compare_type_ = SMI_ONLY;
|
||||
} else if (info.IsInternalizedString()) {
|
||||
compare_type_ = NAME_ONLY;
|
||||
} else if (info.IsNonInternalizedString()) {
|
||||
compare_type_ = STRING_ONLY;
|
||||
} else if (info.IsNonPrimitive()) {
|
||||
compare_type_ = OBJECT_ONLY;
|
||||
} else {
|
||||
ASSERT(compare_type_ == NONE);
|
||||
}
|
||||
compare_type_ = oracle->ClauseType(CompareId());
|
||||
}
|
||||
|
||||
|
||||
@ -685,17 +673,11 @@ void BinaryOperation::RecordTypeFeedback(TypeFeedbackOracle* oracle) {
|
||||
}
|
||||
|
||||
|
||||
// TODO(rossberg): this function (and all other RecordTypeFeedback functions)
|
||||
// should disappear once we use the common type field in the AST consistently.
|
||||
void CompareOperation::RecordTypeFeedback(TypeFeedbackOracle* oracle) {
|
||||
oracle->CompareType(this, &left_type_, &right_type_, &overall_type_);
|
||||
if (!overall_type_.IsUninitialized() && overall_type_.IsNonPrimitive() &&
|
||||
(op_ == Token::EQ || op_ == Token::EQ_STRICT)) {
|
||||
map_ = oracle->GetCompareMap(this);
|
||||
} else {
|
||||
// May be a compare to nil.
|
||||
map_ = oracle->CompareNilMonomorphicReceiverType(this);
|
||||
if (op_ != Token::EQ_STRICT)
|
||||
compare_nil_types_ = oracle->CompareNilTypes(this);
|
||||
}
|
||||
oracle->CompareTypes(CompareOperationFeedbackId(),
|
||||
&left_type_, &right_type_, &overall_type_, &compare_nil_type_);
|
||||
}
|
||||
|
||||
|
||||
@ -1072,7 +1054,7 @@ CaseClause::CaseClause(Isolate* isolate,
|
||||
: label_(label),
|
||||
statements_(statements),
|
||||
position_(pos),
|
||||
compare_type_(NONE),
|
||||
compare_type_(Type::None(), isolate),
|
||||
compare_id_(AstNode::GetNextId(isolate)),
|
||||
entry_id_(AstNode::GetNextId(isolate)) {
|
||||
}
|
||||
|
36
src/ast.h
36
src/ast.h
@ -358,6 +358,7 @@ class Expression: public AstNode {
|
||||
|
||||
// Expression type
|
||||
Handle<Type> type() { return type_; }
|
||||
void set_type(Handle<Type> type) { type_ = type; }
|
||||
|
||||
// Type feedback information for assignments and properties.
|
||||
virtual bool IsMonomorphic() {
|
||||
@ -388,7 +389,7 @@ class Expression: public AstNode {
|
||||
|
||||
protected:
|
||||
explicit Expression(Isolate* isolate)
|
||||
: type_(Type::Any(), isolate),
|
||||
: type_(Type::None(), isolate),
|
||||
id_(GetNextId(isolate)),
|
||||
test_id_(GetNextId(isolate)) {}
|
||||
|
||||
@ -1106,24 +1107,15 @@ class CaseClause: public ZoneObject {
|
||||
// Type feedback information.
|
||||
TypeFeedbackId CompareId() { return compare_id_; }
|
||||
void RecordTypeFeedback(TypeFeedbackOracle* oracle);
|
||||
bool IsSmiCompare() { return compare_type_ == SMI_ONLY; }
|
||||
bool IsNameCompare() { return compare_type_ == NAME_ONLY; }
|
||||
bool IsStringCompare() { return compare_type_ == STRING_ONLY; }
|
||||
bool IsObjectCompare() { return compare_type_ == OBJECT_ONLY; }
|
||||
Handle<Type> compare_type() { return compare_type_; }
|
||||
|
||||
private:
|
||||
Expression* label_;
|
||||
Label body_target_;
|
||||
ZoneList<Statement*>* statements_;
|
||||
int position_;
|
||||
enum CompareTypeFeedback {
|
||||
NONE,
|
||||
SMI_ONLY,
|
||||
NAME_ONLY,
|
||||
STRING_ONLY,
|
||||
OBJECT_ONLY
|
||||
};
|
||||
CompareTypeFeedback compare_type_;
|
||||
Handle<Type> compare_type_;
|
||||
|
||||
const TypeFeedbackId compare_id_;
|
||||
const BailoutId entry_id_;
|
||||
};
|
||||
@ -2003,11 +1995,10 @@ class CompareOperation: public Expression {
|
||||
// Type feedback information.
|
||||
TypeFeedbackId CompareOperationFeedbackId() const { return reuse(id()); }
|
||||
void RecordTypeFeedback(TypeFeedbackOracle* oracle);
|
||||
TypeInfo left_type() const { return left_type_; }
|
||||
TypeInfo right_type() const { return right_type_; }
|
||||
TypeInfo overall_type() const { return overall_type_; }
|
||||
byte compare_nil_types() const { return compare_nil_types_; }
|
||||
Handle<Map> map() const { return map_; }
|
||||
Handle<Type> left_type() const { return left_type_; }
|
||||
Handle<Type> right_type() const { return right_type_; }
|
||||
Handle<Type> overall_type() const { return overall_type_; }
|
||||
Handle<Type> compare_nil_type() const { return compare_nil_type_; }
|
||||
|
||||
// Match special cases.
|
||||
bool IsLiteralCompareTypeof(Expression** expr, Handle<String>* check);
|
||||
@ -2034,11 +2025,10 @@ class CompareOperation: public Expression {
|
||||
Expression* right_;
|
||||
int pos_;
|
||||
|
||||
TypeInfo left_type_;
|
||||
TypeInfo right_type_;
|
||||
TypeInfo overall_type_;
|
||||
byte compare_nil_types_;
|
||||
Handle<Map> map_;
|
||||
Handle<Type> left_type_;
|
||||
Handle<Type> right_type_;
|
||||
Handle<Type> overall_type_;
|
||||
Handle<Type> compare_nil_type_;
|
||||
};
|
||||
|
||||
|
||||
|
@ -733,12 +733,13 @@ Handle<Code> InternalArrayNArgumentsConstructorStub::GenerateCode() {
|
||||
|
||||
template <>
|
||||
HValue* CodeStubGraphBuilder<CompareNilICStub>::BuildCodeInitializedStub() {
|
||||
Isolate* isolate = graph()->isolate();
|
||||
CompareNilICStub* stub = casted_stub();
|
||||
HIfContinuation continuation;
|
||||
Handle<Map> sentinel_map(graph()->isolate()->heap()->meta_map());
|
||||
BuildCompareNil(GetParameter(0),
|
||||
stub->GetTypes(), sentinel_map,
|
||||
RelocInfo::kNoPosition, &continuation);
|
||||
Handle<Map> sentinel_map(isolate->heap()->meta_map());
|
||||
Handle<Type> type =
|
||||
CompareNilICStub::StateToType(isolate, stub->GetState(), sentinel_map);
|
||||
BuildCompareNil(GetParameter(0), type, RelocInfo::kNoPosition, &continuation);
|
||||
IfBuilder if_nil(this, &continuation);
|
||||
if_nil.Then();
|
||||
if (continuation.IsFalseReachable()) {
|
||||
|
@ -431,24 +431,24 @@ void ICCompareStub::Generate(MacroAssembler* masm) {
|
||||
|
||||
|
||||
void CompareNilICStub::Record(Handle<Object> object) {
|
||||
ASSERT(types_ != Types::FullCompare());
|
||||
ASSERT(state_ != State::Generic());
|
||||
if (object->IsNull()) {
|
||||
types_.Add(NULL_TYPE);
|
||||
state_.Add(NULL_TYPE);
|
||||
} else if (object->IsUndefined()) {
|
||||
types_.Add(UNDEFINED);
|
||||
state_.Add(UNDEFINED);
|
||||
} else if (object->IsUndetectableObject() ||
|
||||
object->IsOddball() ||
|
||||
!object->IsHeapObject()) {
|
||||
types_ = Types::FullCompare();
|
||||
state_ = State::Generic();
|
||||
} else if (IsMonomorphic()) {
|
||||
types_ = Types::FullCompare();
|
||||
state_ = State::Generic();
|
||||
} else {
|
||||
types_.Add(MONOMORPHIC_MAP);
|
||||
state_.Add(MONOMORPHIC_MAP);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CompareNilICStub::Types::TraceTransition(Types to) const {
|
||||
void CompareNilICStub::State::TraceTransition(State to) const {
|
||||
#ifdef DEBUG
|
||||
if (!FLAG_trace_ic) return;
|
||||
char buffer[100];
|
||||
@ -467,13 +467,13 @@ void CompareNilICStub::Types::TraceTransition(Types to) const {
|
||||
|
||||
void CompareNilICStub::PrintName(StringStream* stream) {
|
||||
stream->Add("CompareNilICStub_");
|
||||
types_.Print(stream);
|
||||
state_.Print(stream);
|
||||
stream->Add((nil_value_ == kNullValue) ? "(NullValue|":
|
||||
"(UndefinedValue|");
|
||||
}
|
||||
|
||||
|
||||
void CompareNilICStub::Types::Print(StringStream* stream) const {
|
||||
void CompareNilICStub::State::Print(StringStream* stream) const {
|
||||
stream->Add("(");
|
||||
SimpleListPrinter printer(stream);
|
||||
if (IsEmpty()) printer.Add("None");
|
||||
@ -481,10 +481,40 @@ void CompareNilICStub::Types::Print(StringStream* stream) const {
|
||||
if (Contains(NULL_TYPE)) printer.Add("Null");
|
||||
if (Contains(MONOMORPHIC_MAP)) printer.Add("MonomorphicMap");
|
||||
if (Contains(UNDETECTABLE)) printer.Add("Undetectable");
|
||||
if (Contains(GENERIC)) printer.Add("Generic");
|
||||
stream->Add(")");
|
||||
}
|
||||
|
||||
|
||||
Handle<Type> CompareNilICStub::StateToType(
|
||||
Isolate* isolate,
|
||||
State state,
|
||||
Handle<Map> map) {
|
||||
if (state.Contains(CompareNilICStub::GENERIC)) {
|
||||
return handle(Type::Any(), isolate);
|
||||
}
|
||||
|
||||
Handle<Type> result(Type::None(), isolate);
|
||||
if (state.Contains(CompareNilICStub::UNDEFINED)) {
|
||||
result = handle(Type::Union(result, handle(Type::Undefined(), isolate)),
|
||||
isolate);
|
||||
}
|
||||
if (state.Contains(CompareNilICStub::NULL_TYPE)) {
|
||||
result = handle(Type::Union(result, handle(Type::Null(), isolate)),
|
||||
isolate);
|
||||
}
|
||||
if (state.Contains(CompareNilICStub::UNDETECTABLE)) {
|
||||
result = handle(Type::Union(result, handle(Type::Undetectable(), isolate)),
|
||||
isolate);
|
||||
} else if (state.Contains(CompareNilICStub::MONOMORPHIC_MAP)) {
|
||||
Type* type = map.is_null() ? Type::Detectable() : Type::Class(map);
|
||||
result = handle(Type::Union(result, handle(type, isolate)), isolate);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
void InstanceofStub::PrintName(StringStream* stream) {
|
||||
const char* args = "";
|
||||
if (HasArgsInRegisters()) {
|
||||
|
@ -1124,46 +1124,50 @@ class ICCompareStub: public PlatformCodeStub {
|
||||
|
||||
class CompareNilICStub : public HydrogenCodeStub {
|
||||
public:
|
||||
enum Type {
|
||||
enum CompareNilType {
|
||||
UNDEFINED,
|
||||
NULL_TYPE,
|
||||
MONOMORPHIC_MAP,
|
||||
UNDETECTABLE,
|
||||
GENERIC,
|
||||
NUMBER_OF_TYPES
|
||||
};
|
||||
|
||||
class Types : public EnumSet<Type, byte> {
|
||||
class State : public EnumSet<CompareNilType, byte> {
|
||||
public:
|
||||
Types() : EnumSet<Type, byte>(0) { }
|
||||
explicit Types(byte bits) : EnumSet<Type, byte>(bits) { }
|
||||
State() : EnumSet<CompareNilType, byte>(0) { }
|
||||
explicit State(byte bits) : EnumSet<CompareNilType, byte>(bits) { }
|
||||
|
||||
static Types FullCompare() {
|
||||
Types set;
|
||||
static State Generic() {
|
||||
State set;
|
||||
set.Add(UNDEFINED);
|
||||
set.Add(NULL_TYPE);
|
||||
set.Add(UNDETECTABLE);
|
||||
set.Add(GENERIC);
|
||||
return set;
|
||||
}
|
||||
|
||||
void Print(StringStream* stream) const;
|
||||
void TraceTransition(Types to) const;
|
||||
void TraceTransition(State to) const;
|
||||
};
|
||||
|
||||
static Handle<Type> StateToType(
|
||||
Isolate* isolate, State state, Handle<Map> map = Handle<Map>());
|
||||
|
||||
// At most 6 different types can be distinguished, because the Code object
|
||||
// only has room for a single byte to hold a set and there are two more
|
||||
// boolean flags we need to store. :-P
|
||||
STATIC_ASSERT(NUMBER_OF_TYPES <= 6);
|
||||
|
||||
CompareNilICStub(NilValue nil, Types types = Types())
|
||||
: types_(types) {
|
||||
nil_value_ = nil;
|
||||
CompareNilICStub(NilValue nil, State state = State())
|
||||
: nil_value_(nil), state_(state) {
|
||||
}
|
||||
|
||||
CompareNilICStub(Code::ExtraICState ic_state,
|
||||
InitializationState init_state = INITIALIZED)
|
||||
: HydrogenCodeStub(init_state) {
|
||||
nil_value_ = NilValueField::decode(ic_state);
|
||||
types_ = Types(ExtractTypesFromExtraICState(ic_state));
|
||||
state_ = State(ExtractTypesFromExtraICState(ic_state));
|
||||
}
|
||||
|
||||
static Handle<Code> GetUninitialized(Isolate* isolate,
|
||||
@ -1183,9 +1187,9 @@ class CompareNilICStub : public HydrogenCodeStub {
|
||||
}
|
||||
|
||||
virtual InlineCacheState GetICState() {
|
||||
if (types_ == Types::FullCompare()) {
|
||||
if (state_ == State::Generic()) {
|
||||
return MEGAMORPHIC;
|
||||
} else if (types_.Contains(MONOMORPHIC_MAP)) {
|
||||
} else if (state_.Contains(MONOMORPHIC_MAP)) {
|
||||
return MONOMORPHIC;
|
||||
} else {
|
||||
return PREMONOMORPHIC;
|
||||
@ -1198,20 +1202,18 @@ class CompareNilICStub : public HydrogenCodeStub {
|
||||
|
||||
// extra ic state = nil_value | type_n-1 | ... | type_0
|
||||
virtual Code::ExtraICState GetExtraICState() {
|
||||
return NilValueField::encode(nil_value_) |
|
||||
types_.ToIntegral();
|
||||
return NilValueField::encode(nil_value_) | state_.ToIntegral();
|
||||
}
|
||||
static byte ExtractTypesFromExtraICState(
|
||||
Code::ExtraICState state) {
|
||||
static byte ExtractTypesFromExtraICState(Code::ExtraICState state) {
|
||||
return state & ((1 << NUMBER_OF_TYPES) - 1);
|
||||
}
|
||||
|
||||
void Record(Handle<Object> object);
|
||||
|
||||
bool IsMonomorphic() const { return types_.Contains(MONOMORPHIC_MAP); }
|
||||
bool IsMonomorphic() const { return state_.Contains(MONOMORPHIC_MAP); }
|
||||
NilValue GetNilValue() const { return nil_value_; }
|
||||
Types GetTypes() const { return types_; }
|
||||
void ClearTypes() { types_.RemoveAll(); }
|
||||
State GetState() const { return state_; }
|
||||
void ClearState() { state_.RemoveAll(); }
|
||||
|
||||
virtual void PrintName(StringStream* stream);
|
||||
|
||||
@ -1229,7 +1231,7 @@ class CompareNilICStub : public HydrogenCodeStub {
|
||||
virtual int NotMissMinorKey() { return GetExtraICState(); }
|
||||
|
||||
NilValue nil_value_;
|
||||
Types types_;
|
||||
State state_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(CompareNilICStub);
|
||||
};
|
||||
|
@ -402,7 +402,7 @@ OptimizingCompiler::Status OptimizingCompiler::CreateGraph() {
|
||||
}
|
||||
|
||||
// Type-check the function.
|
||||
AstTyper::Type(info());
|
||||
AstTyper::Run(info());
|
||||
|
||||
graph_builder_ = new(info()->zone()) HOptimizedGraphBuilder(info());
|
||||
|
||||
|
@ -1698,39 +1698,35 @@ HValue* HGraphBuilder::BuildCloneShallowArray(HContext* context,
|
||||
|
||||
void HGraphBuilder::BuildCompareNil(
|
||||
HValue* value,
|
||||
CompareNilICStub::Types types,
|
||||
Handle<Map> map,
|
||||
Handle<Type> type,
|
||||
int position,
|
||||
HIfContinuation* continuation) {
|
||||
IfBuilder if_nil(this, position);
|
||||
bool needs_or = false;
|
||||
if (types.Contains(CompareNilICStub::NULL_TYPE)) {
|
||||
if (type->Maybe(Type::Null())) {
|
||||
if (needs_or) if_nil.Or();
|
||||
if_nil.If<HCompareObjectEqAndBranch>(value, graph()->GetConstantNull());
|
||||
needs_or = true;
|
||||
}
|
||||
if (types.Contains(CompareNilICStub::UNDEFINED)) {
|
||||
if (type->Maybe(Type::Undefined())) {
|
||||
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.Contains(CompareNilICStub::UNDETECTABLE) ||
|
||||
!types.Contains(CompareNilICStub::MONOMORPHIC_MAP));
|
||||
if (types.Contains(CompareNilICStub::UNDETECTABLE)) {
|
||||
if (type->Maybe(Type::Undetectable())) {
|
||||
if (needs_or) if_nil.Or();
|
||||
if_nil.If<HIsUndetectableAndBranch>(value);
|
||||
} else {
|
||||
if_nil.Then();
|
||||
if_nil.Else();
|
||||
if (!map.is_null() && types.Contains(CompareNilICStub::MONOMORPHIC_MAP)) {
|
||||
if (type->NumClasses() == 1) {
|
||||
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);
|
||||
BuildCheckMap(value, type->Classes().Current());
|
||||
} else {
|
||||
if_nil.Deopt();
|
||||
}
|
||||
@ -5028,7 +5024,7 @@ void HOptimizedGraphBuilder::VisitSwitchStatement(SwitchStatement* stmt) {
|
||||
HControlInstruction* compare;
|
||||
|
||||
if (stmt->switch_type() == SwitchStatement::SMI_SWITCH) {
|
||||
if (!clause->IsSmiCompare()) {
|
||||
if (!clause->compare_type()->Is(Type::Integer31())) {
|
||||
AddSoftDeoptimize();
|
||||
}
|
||||
|
||||
@ -8002,7 +7998,7 @@ bool HOptimizedGraphBuilder::TryInline(CallKind call_kind,
|
||||
|
||||
// Type-check the inlined function.
|
||||
ASSERT(target_shared->has_deoptimization_support());
|
||||
AstTyper::Type(&target_info);
|
||||
AstTyper::Run(&target_info);
|
||||
|
||||
// Save the pending call context. Set up new one for the inlined function.
|
||||
// The function state is new-allocated because we need to delete it
|
||||
@ -9685,6 +9681,7 @@ void HOptimizedGraphBuilder::VisitArithmeticExpression(BinaryOperation* expr) {
|
||||
}
|
||||
|
||||
|
||||
// TODO(rossberg): this should die eventually.
|
||||
Representation HOptimizedGraphBuilder::ToRepresentation(TypeInfo info) {
|
||||
if (info.IsUninitialized()) return Representation::None();
|
||||
if (info.IsSmi()) return Representation::Integer32();
|
||||
@ -9695,6 +9692,14 @@ Representation HOptimizedGraphBuilder::ToRepresentation(TypeInfo info) {
|
||||
}
|
||||
|
||||
|
||||
Representation HOptimizedGraphBuilder::ToRepresentation(Handle<Type> type) {
|
||||
if (type->Is(Type::None())) return Representation::None();
|
||||
if (type->Is(Type::Integer32())) return Representation::Integer32();
|
||||
if (type->Is(Type::Number())) return Representation::Double();
|
||||
return Representation::Tagged();
|
||||
}
|
||||
|
||||
|
||||
void HOptimizedGraphBuilder::HandleLiteralCompareTypeof(CompareOperation* expr,
|
||||
HTypeof* typeof_expr,
|
||||
Handle<String> check) {
|
||||
@ -9784,17 +9789,17 @@ void HOptimizedGraphBuilder::VisitCompareOperation(CompareOperation* expr) {
|
||||
return ast_context()->ReturnControl(instr, expr->id());
|
||||
}
|
||||
|
||||
TypeInfo left_type = expr->left_type();
|
||||
TypeInfo right_type = expr->right_type();
|
||||
TypeInfo overall_type = expr->overall_type();
|
||||
Handle<Type> left_type = expr->left_type();
|
||||
Handle<Type> right_type = expr->right_type();
|
||||
Handle<Type> overall_type = expr->overall_type();
|
||||
Representation combined_rep = ToRepresentation(overall_type);
|
||||
Representation left_rep = ToRepresentation(left_type);
|
||||
Representation right_rep = ToRepresentation(right_type);
|
||||
// Check if this expression was ever executed according to type feedback.
|
||||
// Note that for the special typeof/null/undefined cases we get unknown here.
|
||||
if (overall_type.IsUninitialized()) {
|
||||
if (overall_type->Is(Type::None())) {
|
||||
AddSoftDeoptimize();
|
||||
overall_type = left_type = right_type = TypeInfo::Unknown();
|
||||
overall_type = left_type = right_type = handle(Type::Any(), isolate());
|
||||
}
|
||||
|
||||
CHECK_ALIVE(VisitForValue(expr->left()));
|
||||
@ -9866,13 +9871,13 @@ void HOptimizedGraphBuilder::VisitCompareOperation(CompareOperation* expr) {
|
||||
HIn* result = new(zone()) HIn(context, left, right);
|
||||
result->set_position(expr->position());
|
||||
return ast_context()->ReturnInstruction(result, expr->id());
|
||||
} else if (overall_type.IsNonPrimitive()) {
|
||||
} else if (overall_type->Is(Type::Receiver())) {
|
||||
switch (op) {
|
||||
case Token::EQ:
|
||||
case Token::EQ_STRICT: {
|
||||
// Can we get away with map check and not instance type check?
|
||||
Handle<Map> map = expr->map();
|
||||
if (!map.is_null()) {
|
||||
if (overall_type->IsClass()) {
|
||||
Handle<Map> map = overall_type->AsClass();
|
||||
AddCheckMapsWithTransitions(left, map);
|
||||
AddCheckMapsWithTransitions(right, map);
|
||||
HCompareObjectEqAndBranch* result =
|
||||
@ -9893,7 +9898,7 @@ void HOptimizedGraphBuilder::VisitCompareOperation(CompareOperation* expr) {
|
||||
default:
|
||||
return Bailout("Unsupported non-primitive compare");
|
||||
}
|
||||
} else if (overall_type.IsInternalizedString() &&
|
||||
} else if (overall_type->Is(Type::InternalizedString()) &&
|
||||
Token::IsEqualityOp(op)) {
|
||||
BuildCheckNonSmi(left);
|
||||
AddInstruction(HCheckInstanceType::NewIsInternalizedString(left, zone()));
|
||||
@ -9928,8 +9933,8 @@ void HOptimizedGraphBuilder::HandleLiteralCompareNil(CompareOperation* expr,
|
||||
ASSERT(!HasStackOverflow());
|
||||
ASSERT(current_block() != NULL);
|
||||
ASSERT(current_block()->HasPredecessor());
|
||||
ASSERT(expr->op() == Token::EQ || expr->op() == Token::EQ_STRICT);
|
||||
HIfContinuation continuation;
|
||||
CompareNilICStub::Types types;
|
||||
if (expr->op() == Token::EQ_STRICT) {
|
||||
IfBuilder if_nil(this);
|
||||
if_nil.If<HCompareObjectEqAndBranch>(
|
||||
@ -9940,11 +9945,9 @@ void HOptimizedGraphBuilder::HandleLiteralCompareNil(CompareOperation* expr,
|
||||
if_nil.CaptureContinuation(&continuation);
|
||||
return ast_context()->ReturnContinuation(&continuation, expr->id());
|
||||
}
|
||||
types = CompareNilICStub::Types(expr->compare_nil_types());
|
||||
if (types.IsEmpty()) types = CompareNilICStub::Types::FullCompare();
|
||||
Handle<Map> map_handle = expr->map();
|
||||
BuildCompareNil(value, types, map_handle,
|
||||
expr->position(), &continuation);
|
||||
Handle<Type> type = expr->compare_nil_type()->Is(Type::None())
|
||||
? handle(Type::Any(), isolate_) : expr->compare_nil_type();
|
||||
BuildCompareNil(value, type, expr->position(), &continuation);
|
||||
return ast_context()->ReturnContinuation(&continuation, expr->id());
|
||||
}
|
||||
|
||||
|
@ -1364,8 +1364,7 @@ class HGraphBuilder {
|
||||
|
||||
void BuildCompareNil(
|
||||
HValue* value,
|
||||
CompareNilICStub::Types types,
|
||||
Handle<Map> map,
|
||||
Handle<Type> type,
|
||||
int position,
|
||||
HIfContinuation* continuation);
|
||||
|
||||
@ -1636,6 +1635,7 @@ class HOptimizedGraphBuilder: public HGraphBuilder, public AstVisitor {
|
||||
template <class Instruction> HInstruction* PreProcessCall(Instruction* call);
|
||||
|
||||
static Representation ToRepresentation(TypeInfo info);
|
||||
static Representation ToRepresentation(Handle<Type> type);
|
||||
|
||||
void SetUpScope(Scope* scope);
|
||||
virtual void VisitStatements(ZoneList<Statement*>* statements);
|
||||
|
48
src/ic.cc
48
src/ic.cc
@ -2420,8 +2420,8 @@ UnaryOpIC::State UnaryOpIC::ToState(TypeInfo type_info) {
|
||||
}
|
||||
|
||||
UnaryOpIC::TypeInfo UnaryOpIC::GetTypeInfo(Handle<Object> operand) {
|
||||
::v8::internal::TypeInfo operand_type =
|
||||
::v8::internal::TypeInfo::TypeFromValue(operand);
|
||||
v8::internal::TypeInfo operand_type =
|
||||
v8::internal::TypeInfo::FromValue(operand);
|
||||
if (operand_type.IsSmi()) {
|
||||
return SMI;
|
||||
} else if (operand_type.IsNumber()) {
|
||||
@ -2545,8 +2545,7 @@ RUNTIME_FUNCTION(MaybeObject*, UnaryOp_Patch) {
|
||||
|
||||
static BinaryOpIC::TypeInfo TypeInfoFromValue(Handle<Object> value,
|
||||
Token::Value op) {
|
||||
::v8::internal::TypeInfo type =
|
||||
::v8::internal::TypeInfo::TypeFromValue(value);
|
||||
v8::internal::TypeInfo type = v8::internal::TypeInfo::FromValue(value);
|
||||
if (type.IsSmi()) return BinaryOpIC::SMI;
|
||||
if (type.IsInteger32()) {
|
||||
if (kSmiValueSize == 32) return BinaryOpIC::SMI;
|
||||
@ -2767,10 +2766,39 @@ const char* CompareIC::GetStateName(State state) {
|
||||
case OBJECT: return "OBJECT";
|
||||
case KNOWN_OBJECT: return "KNOWN_OBJECT";
|
||||
case GENERIC: return "GENERIC";
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return NULL;
|
||||
}
|
||||
UNREACHABLE();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
Handle<Type> CompareIC::StateToType(
|
||||
Isolate* isolate,
|
||||
CompareIC::State state,
|
||||
Handle<Map> map) {
|
||||
switch (state) {
|
||||
case CompareIC::UNINITIALIZED:
|
||||
return handle(Type::None(), isolate);
|
||||
case CompareIC::SMI:
|
||||
return handle(Type::Integer31(), isolate);
|
||||
case CompareIC::NUMBER:
|
||||
return handle(Type::Number(), isolate);
|
||||
case CompareIC::STRING:
|
||||
return handle(Type::String(), isolate);
|
||||
case CompareIC::INTERNALIZED_STRING:
|
||||
return handle(Type::InternalizedString(), isolate);
|
||||
case CompareIC::UNIQUE_NAME:
|
||||
return handle(Type::UniqueName(), isolate);
|
||||
case CompareIC::OBJECT:
|
||||
return handle(Type::Receiver(), isolate);
|
||||
case CompareIC::KNOWN_OBJECT:
|
||||
return handle(
|
||||
map.is_null() ? Type::Receiver() : Type::Class(map), isolate);
|
||||
case CompareIC::GENERIC:
|
||||
return handle(Type::Any(), isolate);
|
||||
}
|
||||
UNREACHABLE();
|
||||
return Handle<Type>();
|
||||
}
|
||||
|
||||
|
||||
@ -2934,7 +2962,7 @@ void CompareNilIC::Clear(Address address, Code* target) {
|
||||
Code::ExtraICState state = target->extended_extra_ic_state();
|
||||
|
||||
CompareNilICStub stub(state, HydrogenCodeStub::UNINITIALIZED);
|
||||
stub.ClearTypes();
|
||||
stub.ClearState();
|
||||
|
||||
Code* code = NULL;
|
||||
CHECK(stub.FindCodeInCache(&code, target->GetIsolate()));
|
||||
@ -2961,9 +2989,9 @@ MaybeObject* CompareNilIC::CompareNil(Handle<Object> object) {
|
||||
// types must be supported as a result of the miss.
|
||||
bool already_monomorphic = stub.IsMonomorphic();
|
||||
|
||||
CompareNilICStub::Types old_types = stub.GetTypes();
|
||||
CompareNilICStub::State old_state = stub.GetState();
|
||||
stub.Record(object);
|
||||
old_types.TraceTransition(stub.GetTypes());
|
||||
old_state.TraceTransition(stub.GetState());
|
||||
|
||||
NilValue nil = stub.GetNilValue();
|
||||
|
||||
|
3
src/ic.h
3
src/ic.h
@ -741,6 +741,9 @@ class CompareIC: public IC {
|
||||
GENERIC
|
||||
};
|
||||
|
||||
static Handle<Type> StateToType(
|
||||
Isolate* isolate, State state, Handle<Map> map = Handle<Map>());
|
||||
|
||||
CompareIC(Isolate* isolate, Token::Value op)
|
||||
: IC(EXTRA_CALL_FRAME, isolate), op_(op) { }
|
||||
|
||||
|
@ -10018,7 +10018,7 @@ void ObjectVisitor::VisitExternalReference(RelocInfo* rinfo) {
|
||||
VisitExternalReferences(p, p + 1);
|
||||
}
|
||||
|
||||
byte Code::compare_nil_types() {
|
||||
byte Code::compare_nil_state() {
|
||||
ASSERT(is_compare_nil_ic_stub());
|
||||
return CompareNilICStub::ExtractTypesFromExtraICState(
|
||||
extended_extra_ic_state());
|
||||
|
@ -4669,7 +4669,7 @@ class Code: public HeapObject {
|
||||
inline byte to_boolean_state();
|
||||
|
||||
// [compare_nil]: For kind COMPARE_NIL_IC tells what state the stub is in.
|
||||
byte compare_nil_types();
|
||||
byte compare_nil_state();
|
||||
|
||||
// [has_function_cache]: For kind STUB tells whether there is a function
|
||||
// cache is passed to the stub.
|
||||
|
147
src/type-info.cc
147
src/type-info.cc
@ -42,20 +42,17 @@ namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
|
||||
TypeInfo TypeInfo::TypeFromValue(Handle<Object> value) {
|
||||
TypeInfo info;
|
||||
TypeInfo TypeInfo::FromValue(Handle<Object> value) {
|
||||
if (value->IsSmi()) {
|
||||
info = TypeInfo::Smi();
|
||||
return TypeInfo::Smi();
|
||||
} else if (value->IsHeapNumber()) {
|
||||
info = TypeInfo::IsInt32Double(HeapNumber::cast(*value)->value())
|
||||
return TypeInfo::IsInt32Double(HeapNumber::cast(*value)->value())
|
||||
? TypeInfo::Integer32()
|
||||
: TypeInfo::Double();
|
||||
} else if (value->IsString()) {
|
||||
info = TypeInfo::String();
|
||||
} else {
|
||||
info = TypeInfo::Unknown();
|
||||
return TypeInfo::String();
|
||||
}
|
||||
return info;
|
||||
return TypeInfo::Unknown();
|
||||
}
|
||||
|
||||
|
||||
@ -234,24 +231,6 @@ Handle<Map> TypeFeedbackOracle::StoreMonomorphicReceiverType(
|
||||
}
|
||||
|
||||
|
||||
Handle<Map> TypeFeedbackOracle::CompareNilMonomorphicReceiverType(
|
||||
CompareOperation* expr) {
|
||||
Handle<Object> maybe_code = GetInfo(expr->CompareOperationFeedbackId());
|
||||
if (maybe_code->IsCode()) {
|
||||
Map* map = Handle<Code>::cast(maybe_code)->FindFirstMap();
|
||||
if (map == NULL) return Handle<Map>();
|
||||
map = map->CurrentMapForDeprecated();
|
||||
return map == NULL || CanRetainOtherContext(map, *native_context_)
|
||||
? Handle<Map>()
|
||||
: Handle<Map>(map);
|
||||
} else if (maybe_code->IsMap()) {
|
||||
ASSERT(!Handle<Map>::cast(maybe_code)->is_deprecated());
|
||||
return Handle<Map>::cast(maybe_code);
|
||||
}
|
||||
return Handle<Map>();
|
||||
}
|
||||
|
||||
|
||||
KeyedAccessStoreMode TypeFeedbackOracle::GetStoreMode(
|
||||
TypeFeedbackId ast_id) {
|
||||
Handle<Object> map_or_code = GetInfo(ast_id);
|
||||
@ -362,69 +341,38 @@ bool TypeFeedbackOracle::LoadIsStub(Property* expr, ICStub* stub) {
|
||||
}
|
||||
|
||||
|
||||
static TypeInfo TypeFromCompareType(CompareIC::State state) {
|
||||
switch (state) {
|
||||
case CompareIC::UNINITIALIZED:
|
||||
// Uninitialized means never executed.
|
||||
return TypeInfo::Uninitialized();
|
||||
case CompareIC::SMI:
|
||||
return TypeInfo::Smi();
|
||||
case CompareIC::NUMBER:
|
||||
return TypeInfo::Number();
|
||||
case CompareIC::INTERNALIZED_STRING:
|
||||
return TypeInfo::InternalizedString();
|
||||
case CompareIC::STRING:
|
||||
return TypeInfo::String();
|
||||
case CompareIC::OBJECT:
|
||||
case CompareIC::KNOWN_OBJECT:
|
||||
// TODO(kasperl): We really need a type for JS objects here.
|
||||
return TypeInfo::NonPrimitive();
|
||||
case CompareIC::GENERIC:
|
||||
default:
|
||||
return TypeInfo::Unknown();
|
||||
}
|
||||
}
|
||||
void TypeFeedbackOracle::CompareTypes(TypeFeedbackId id,
|
||||
Handle<Type>* left_type,
|
||||
Handle<Type>* right_type,
|
||||
Handle<Type>* overall_type,
|
||||
Handle<Type>* compare_nil_type) {
|
||||
*left_type = *right_type = *overall_type = *compare_nil_type =
|
||||
handle(Type::Any(), isolate_);
|
||||
Handle<Object> info = GetInfo(id);
|
||||
if (!info->IsCode()) return;
|
||||
Handle<Code> code = Handle<Code>::cast(info);
|
||||
|
||||
|
||||
void TypeFeedbackOracle::CompareType(CompareOperation* expr,
|
||||
TypeInfo* left_type,
|
||||
TypeInfo* right_type,
|
||||
TypeInfo* overall_type) {
|
||||
Handle<Object> object = GetInfo(expr->CompareOperationFeedbackId());
|
||||
TypeInfo unknown = TypeInfo::Unknown();
|
||||
if (!object->IsCode()) {
|
||||
*left_type = *right_type = *overall_type = unknown;
|
||||
return;
|
||||
}
|
||||
Handle<Code> code = Handle<Code>::cast(object);
|
||||
if (!code->is_compare_ic_stub()) {
|
||||
*left_type = *right_type = *overall_type = unknown;
|
||||
return;
|
||||
Handle<Map> map;
|
||||
Map* raw_map = code->FindFirstMap();
|
||||
if (raw_map != NULL) {
|
||||
raw_map = raw_map->CurrentMapForDeprecated();
|
||||
if (!CanRetainOtherContext(raw_map, *native_context_)) {
|
||||
map = handle(raw_map, isolate_);
|
||||
}
|
||||
}
|
||||
|
||||
int stub_minor_key = code->stub_info();
|
||||
CompareIC::State left_state, right_state, handler_state;
|
||||
ICCompareStub::DecodeMinorKey(stub_minor_key, &left_state, &right_state,
|
||||
&handler_state, NULL);
|
||||
*left_type = TypeFromCompareType(left_state);
|
||||
*right_type = TypeFromCompareType(right_state);
|
||||
*overall_type = TypeFromCompareType(handler_state);
|
||||
}
|
||||
|
||||
|
||||
Handle<Map> TypeFeedbackOracle::GetCompareMap(CompareOperation* expr) {
|
||||
Handle<Object> object = GetInfo(expr->CompareOperationFeedbackId());
|
||||
if (!object->IsCode()) return Handle<Map>::null();
|
||||
Handle<Code> code = Handle<Code>::cast(object);
|
||||
if (!code->is_compare_ic_stub()) return Handle<Map>::null();
|
||||
CompareIC::State state = ICCompareStub::CompareState(code->stub_info());
|
||||
if (state != CompareIC::KNOWN_OBJECT) {
|
||||
return Handle<Map>::null();
|
||||
if (code->is_compare_ic_stub()) {
|
||||
int stub_minor_key = code->stub_info();
|
||||
CompareIC::State left_state, right_state, handler_state;
|
||||
ICCompareStub::DecodeMinorKey(stub_minor_key, &left_state, &right_state,
|
||||
&handler_state, NULL);
|
||||
*left_type = CompareIC::StateToType(isolate_, left_state);
|
||||
*right_type = CompareIC::StateToType(isolate_, right_state);
|
||||
*overall_type = CompareIC::StateToType(isolate_, handler_state, map);
|
||||
} else if (code->is_compare_nil_ic_stub()) {
|
||||
CompareNilICStub::State state(code->compare_nil_state());
|
||||
*compare_nil_type = CompareNilICStub::StateToType(isolate_, state, map);
|
||||
}
|
||||
Map* map = code->FindFirstMap()->CurrentMapForDeprecated();
|
||||
return map == NULL || CanRetainOtherContext(map, *native_context_)
|
||||
? Handle<Map>::null()
|
||||
: Handle<Map>(map);
|
||||
}
|
||||
|
||||
|
||||
@ -495,15 +443,15 @@ void TypeFeedbackOracle::BinaryType(BinaryOperation* expr,
|
||||
}
|
||||
|
||||
|
||||
TypeInfo TypeFeedbackOracle::SwitchType(CaseClause* clause) {
|
||||
Handle<Object> object = GetInfo(clause->CompareId());
|
||||
TypeInfo unknown = TypeInfo::Unknown();
|
||||
if (!object->IsCode()) return unknown;
|
||||
Handle<Code> code = Handle<Code>::cast(object);
|
||||
if (!code->is_compare_ic_stub()) return unknown;
|
||||
|
||||
CompareIC::State state = ICCompareStub::CompareState(code->stub_info());
|
||||
return TypeFromCompareType(state);
|
||||
Handle<Type> TypeFeedbackOracle::ClauseType(TypeFeedbackId id) {
|
||||
Handle<Object> info = GetInfo(id);
|
||||
Handle<Type> result(Type::None(), isolate_);
|
||||
if (info->IsCode() && Handle<Code>::cast(info)->is_compare_ic_stub()) {
|
||||
Handle<Code> code = Handle<Code>::cast(info);
|
||||
CompareIC::State state = ICCompareStub::CompareState(code->stub_info());
|
||||
result = CompareIC::StateToType(isolate_, state);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@ -634,17 +582,6 @@ byte TypeFeedbackOracle::ToBooleanTypes(TypeFeedbackId id) {
|
||||
}
|
||||
|
||||
|
||||
byte TypeFeedbackOracle::CompareNilTypes(CompareOperation* expr) {
|
||||
Handle<Object> object = GetInfo(expr->CompareOperationFeedbackId());
|
||||
if (object->IsCode() &&
|
||||
Handle<Code>::cast(object)->is_compare_nil_ic_stub()) {
|
||||
return Handle<Code>::cast(object)->compare_nil_types();
|
||||
} else {
|
||||
return CompareNilICStub::Types::FullCompare().ToIntegral();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 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
|
||||
|
@ -30,6 +30,7 @@
|
||||
|
||||
#include "allocation.h"
|
||||
#include "globals.h"
|
||||
#include "types.h"
|
||||
#include "zone-inl.h"
|
||||
|
||||
namespace v8 {
|
||||
@ -113,7 +114,7 @@ class TypeInfo {
|
||||
return false;
|
||||
}
|
||||
|
||||
static TypeInfo TypeFromValue(Handle<Object> value);
|
||||
static TypeInfo FromValue(Handle<Object> value);
|
||||
|
||||
bool Equals(const TypeInfo& other) {
|
||||
return type_ == other.type_;
|
||||
@ -217,12 +218,12 @@ enum StringStubFeedback {
|
||||
|
||||
|
||||
// Forward declarations.
|
||||
// TODO(rossberg): these should all go away eventually.
|
||||
class Assignment;
|
||||
class BinaryOperation;
|
||||
class Call;
|
||||
class CallNew;
|
||||
class CaseClause;
|
||||
class CompareOperation;
|
||||
class CompilationInfo;
|
||||
class CountOperation;
|
||||
class Expression;
|
||||
@ -257,7 +258,6 @@ class TypeFeedbackOracle: public ZoneObject {
|
||||
|
||||
Handle<Map> LoadMonomorphicReceiverType(Property* expr);
|
||||
Handle<Map> StoreMonomorphicReceiverType(TypeFeedbackId id);
|
||||
Handle<Map> CompareNilMonomorphicReceiverType(CompareOperation* expr);
|
||||
|
||||
KeyedAccessStoreMode GetStoreMode(TypeFeedbackId ast_id);
|
||||
|
||||
@ -293,12 +293,7 @@ class TypeFeedbackOracle: public ZoneObject {
|
||||
// TODO(1571) We can't use ToBooleanStub::Types as the return value because
|
||||
// of various cycles in our headers. Death to tons of implementations in
|
||||
// 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(CompareOperation* expr);
|
||||
byte ToBooleanTypes(TypeFeedbackId id);
|
||||
|
||||
// Get type information for arithmetic operations and compares.
|
||||
TypeInfo UnaryType(UnaryOperation* expr);
|
||||
@ -308,12 +303,15 @@ class TypeFeedbackOracle: public ZoneObject {
|
||||
TypeInfo* result,
|
||||
bool* has_fixed_right_arg,
|
||||
int* fixed_right_arg_value);
|
||||
void CompareType(CompareOperation* expr,
|
||||
TypeInfo* left_type,
|
||||
TypeInfo* right_type,
|
||||
TypeInfo* overall_type);
|
||||
Handle<Map> GetCompareMap(CompareOperation* expr);
|
||||
TypeInfo SwitchType(CaseClause* clause);
|
||||
|
||||
void CompareTypes(TypeFeedbackId id,
|
||||
Handle<Type>* left_type,
|
||||
Handle<Type>* right_type,
|
||||
Handle<Type>* overall_type,
|
||||
Handle<Type>* compare_nil_type);
|
||||
|
||||
Handle<Type> ClauseType(TypeFeedbackId id);
|
||||
|
||||
TypeInfo IncrementType(CountOperation* expr);
|
||||
|
||||
Zone* zone() const { return zone_; }
|
||||
|
112
src/types.cc
112
src/types.cc
@ -30,6 +30,84 @@
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
int Type::NumClasses() {
|
||||
if (is_class()) {
|
||||
return 1;
|
||||
} else if (is_union()) {
|
||||
Handle<Unioned> unioned = as_union();
|
||||
int result = 0;
|
||||
for (int i = 0; i < unioned->length(); ++i) {
|
||||
if (union_get(unioned, i)->is_class()) ++result;
|
||||
}
|
||||
return result;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int Type::NumConstants() {
|
||||
if (is_constant()) {
|
||||
return 1;
|
||||
} else if (is_union()) {
|
||||
Handle<Unioned> unioned = as_union();
|
||||
int result = 0;
|
||||
for (int i = 0; i < unioned->length(); ++i) {
|
||||
if (union_get(unioned, i)->is_constant()) ++result;
|
||||
}
|
||||
return result;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template<class T>
|
||||
Handle<Type> Type::Iterator<T>::get_type() {
|
||||
ASSERT(!Done());
|
||||
return type_->is_union() ? union_get(type_->as_union(), index_) : type_;
|
||||
}
|
||||
|
||||
template<>
|
||||
Handle<Map> Type::Iterator<Map>::Current() {
|
||||
return get_type()->as_class();
|
||||
}
|
||||
|
||||
template<>
|
||||
Handle<v8::internal::Object> Type::Iterator<v8::internal::Object>::Current() {
|
||||
return get_type()->as_constant();
|
||||
}
|
||||
|
||||
|
||||
template<>
|
||||
bool Type::Iterator<Map>::matches(Handle<Type> type) {
|
||||
return type->is_class();
|
||||
}
|
||||
|
||||
template<>
|
||||
bool Type::Iterator<v8::internal::Object>::matches(Handle<Type> type) {
|
||||
return type->is_constant();
|
||||
}
|
||||
|
||||
|
||||
template<class T>
|
||||
void Type::Iterator<T>::Advance() {
|
||||
++index_;
|
||||
if (type_->is_union()) {
|
||||
Handle<Unioned> unioned = type_->as_union();
|
||||
for (; index_ < unioned->length(); ++index_) {
|
||||
if (matches(union_get(unioned, index_))) return;
|
||||
}
|
||||
} else if (index_ == 0 && matches(type_)) {
|
||||
return;
|
||||
}
|
||||
index_ = -1;
|
||||
}
|
||||
|
||||
template class Type::Iterator<Map>;
|
||||
template class Type::Iterator<v8::internal::Object>;
|
||||
|
||||
|
||||
// Get the smallest bitset subsuming this type.
|
||||
int Type::LubBitset() {
|
||||
if (this->is_bitset()) {
|
||||
@ -46,9 +124,14 @@ int Type::LubBitset() {
|
||||
if (this->is_class()) {
|
||||
map = *this->as_class();
|
||||
} else {
|
||||
v8::internal::Object* value = this->as_constant()->value();
|
||||
if (value->IsSmi()) return kSmi;
|
||||
map = HeapObject::cast(value)->map();
|
||||
Handle<v8::internal::Object> value = this->as_constant();
|
||||
if (value->IsSmi()) return kInteger31;
|
||||
map = HeapObject::cast(*value)->map();
|
||||
if (map->instance_type() == ODDBALL_TYPE) {
|
||||
if (value->IsUndefined()) return kUndefined;
|
||||
if (value->IsNull()) return kNull;
|
||||
if (value->IsTrue() || value->IsFalse()) return kBoolean;
|
||||
}
|
||||
}
|
||||
switch (map->instance_type()) {
|
||||
case STRING_TYPE:
|
||||
@ -56,6 +139,7 @@ int Type::LubBitset() {
|
||||
case CONS_STRING_TYPE:
|
||||
case CONS_ASCII_STRING_TYPE:
|
||||
case SLICED_STRING_TYPE:
|
||||
case SLICED_ASCII_STRING_TYPE:
|
||||
case EXTERNAL_STRING_TYPE:
|
||||
case EXTERNAL_ASCII_STRING_TYPE:
|
||||
case EXTERNAL_STRING_WITH_ONE_BYTE_DATA_TYPE:
|
||||
@ -92,6 +176,7 @@ int Type::LubBitset() {
|
||||
case JS_TYPED_ARRAY_TYPE:
|
||||
case JS_WEAK_MAP_TYPE:
|
||||
case JS_REGEXP_TYPE:
|
||||
if (map->is_undetectable()) return kUndetectable;
|
||||
return kOtherObject;
|
||||
case JS_ARRAY_TYPE:
|
||||
return kArray;
|
||||
@ -100,6 +185,17 @@ int Type::LubBitset() {
|
||||
case JS_PROXY_TYPE:
|
||||
case JS_FUNCTION_PROXY_TYPE:
|
||||
return kProxy;
|
||||
case MAP_TYPE:
|
||||
// When compiling stub templates, the meta map is used as a place holder
|
||||
// for the actual map with which the template is later instantiated.
|
||||
// We treat it as a kind of type variable whose upper bound is Any.
|
||||
// TODO(rossberg): for caching of CompareNilIC stubs to work correctly,
|
||||
// we must exclude Undetectable here. This makes no sense, really,
|
||||
// because it means that the template isn't actually parametric.
|
||||
// Also, it doesn't apply elsewhere. 8-(
|
||||
// We ought to find a cleaner solution for compiling stubs parameterised
|
||||
// over type or class variables, esp ones with bounds...
|
||||
return kDetectable;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return kNone;
|
||||
@ -122,7 +218,7 @@ int Type::GlbBitset() {
|
||||
|
||||
|
||||
// Check this <= that.
|
||||
bool Type::Is(Handle<Type> that) {
|
||||
bool Type::Is(Type* that) {
|
||||
// Fast path for bitsets.
|
||||
if (that->is_bitset()) {
|
||||
return (this->LubBitset() | that->as_bitset()) == that->as_bitset();
|
||||
@ -132,8 +228,7 @@ bool Type::Is(Handle<Type> that) {
|
||||
return this->is_class() && *this->as_class() == *that->as_class();
|
||||
}
|
||||
if (that->is_constant()) {
|
||||
return this->is_constant() &&
|
||||
this->as_constant()->value() == that->as_constant()->value();
|
||||
return this->is_constant() && *this->as_constant() == *that->as_constant();
|
||||
}
|
||||
|
||||
// (T1 \/ ... \/ Tn) <= T <=> (T1 <= T) /\ ... /\ (Tn <= T)
|
||||
@ -163,7 +258,7 @@ bool Type::Is(Handle<Type> that) {
|
||||
|
||||
|
||||
// Check this overlaps that.
|
||||
bool Type::Maybe(Handle<Type> that) {
|
||||
bool Type::Maybe(Type* that) {
|
||||
// Fast path for bitsets.
|
||||
if (this->is_bitset()) {
|
||||
return (this->as_bitset() & that->LubBitset()) != 0;
|
||||
@ -176,8 +271,7 @@ bool Type::Maybe(Handle<Type> that) {
|
||||
return that->is_class() && *this->as_class() == *that->as_class();
|
||||
}
|
||||
if (this->is_constant()) {
|
||||
return that->is_constant() &&
|
||||
this->as_constant()->value() == that->as_constant()->value();
|
||||
return that->is_constant() && *this->as_constant() == *that->as_constant();
|
||||
}
|
||||
|
||||
// (T1 \/ ... \/ Tn) overlaps T <=> (T1 overlaps T) \/ ... \/ (Tn overlaps T)
|
||||
|
96
src/types.h
96
src/types.h
@ -48,11 +48,15 @@ namespace internal {
|
||||
// T <= Any
|
||||
//
|
||||
// Oddball = Boolean \/ Null \/ Undefined
|
||||
// Number = Smi \/ Double
|
||||
// Number = Integer32 \/ Double
|
||||
// Integer31 < Integer32
|
||||
// Name = String \/ Symbol
|
||||
// UniqueName = InternalizedString \/ Symbol
|
||||
// InternalizedString < String
|
||||
//
|
||||
// Allocated = Receiver \/ Number \/ Name
|
||||
// Detectable = Allocated - Undetectable
|
||||
// Undetectable < Object
|
||||
// Receiver = Object \/ Proxy
|
||||
// Array < Object
|
||||
// Function < Object
|
||||
@ -70,8 +74,9 @@ namespace internal {
|
||||
// T1->Is(T2) -- tests whether T1 is included in T2 (i.e., T1 <= T2)
|
||||
// T1->Maybe(T2) -- tests whether T1 and T2 overlap (i.e., T1 /\ T2 =/= 0)
|
||||
//
|
||||
// Typically, the latter should be used to check whether a specific case needs
|
||||
// handling (e.g., via T->Maybe(Number)).
|
||||
// Typically, the former is to be used to select representations (e.g., via
|
||||
// T->Is(Integer31())), and the to check whether a specific case needs handling
|
||||
// (e.g., via T->Maybe(Number())).
|
||||
//
|
||||
// There is no functionality to discover whether a type is a leaf in the
|
||||
// lattice. That is intentional. It should always be possible to refine the
|
||||
@ -89,6 +94,8 @@ class Type : public Object {
|
||||
public:
|
||||
static Type* None() { return from_bitset(kNone); }
|
||||
static Type* Any() { return from_bitset(kAny); }
|
||||
static Type* Allocated() { return from_bitset(kAllocated); }
|
||||
static Type* Detectable() { return from_bitset(kDetectable); }
|
||||
|
||||
static Type* Oddball() { return from_bitset(kOddball); }
|
||||
static Type* Boolean() { return from_bitset(kBoolean); }
|
||||
@ -96,7 +103,8 @@ class Type : public Object {
|
||||
static Type* Undefined() { return from_bitset(kUndefined); }
|
||||
|
||||
static Type* Number() { return from_bitset(kNumber); }
|
||||
static Type* Smi() { return from_bitset(kSmi); }
|
||||
static Type* Integer31() { return from_bitset(kInteger31); }
|
||||
static Type* Integer32() { return from_bitset(kInteger32); }
|
||||
static Type* Double() { return from_bitset(kDouble); }
|
||||
|
||||
static Type* Name() { return from_bitset(kName); }
|
||||
@ -107,6 +115,7 @@ class Type : public Object {
|
||||
|
||||
static Type* Receiver() { return from_bitset(kReceiver); }
|
||||
static Type* Object() { return from_bitset(kObject); }
|
||||
static Type* Undetectable() { return from_bitset(kUndetectable); }
|
||||
static Type* Array() { return from_bitset(kArray); }
|
||||
static Type* Function() { return from_bitset(kFunction); }
|
||||
static Type* Proxy() { return from_bitset(kProxy); }
|
||||
@ -122,10 +131,49 @@ class Type : public Object {
|
||||
static Type* Union(Handle<Type> type1, Handle<Type> type2);
|
||||
static Type* Optional(Handle<Type> type); // type \/ Undefined
|
||||
|
||||
bool Is(Handle<Type> that);
|
||||
bool Maybe(Handle<Type> that);
|
||||
bool Is(Type* that);
|
||||
bool Is(Handle<Type> that) { return this->Is(*that); }
|
||||
bool Maybe(Type* that);
|
||||
bool Maybe(Handle<Type> that) { return this->Maybe(*that); }
|
||||
|
||||
// TODO(rossberg): method to iterate unions?
|
||||
bool IsClass() { return is_class(); }
|
||||
bool IsConstant() { return is_constant(); }
|
||||
Handle<Map> AsClass() { return as_class(); }
|
||||
Handle<v8::internal::Object> AsConstant() { return as_constant(); }
|
||||
|
||||
int NumClasses();
|
||||
int NumConstants();
|
||||
|
||||
template<class T>
|
||||
class Iterator {
|
||||
public:
|
||||
bool Done() const { return index_ < 0; }
|
||||
Handle<T> Current();
|
||||
void Advance();
|
||||
|
||||
private:
|
||||
friend class Type;
|
||||
|
||||
Iterator() : index_(-1) {}
|
||||
explicit Iterator(Handle<Type> type) : type_(type), index_(-1) {
|
||||
Advance();
|
||||
}
|
||||
|
||||
inline bool matches(Handle<Type> type);
|
||||
inline Handle<Type> get_type();
|
||||
|
||||
Handle<Type> type_;
|
||||
int index_;
|
||||
};
|
||||
|
||||
Iterator<Map> Classes() {
|
||||
if (this->is_bitset()) return Iterator<Map>();
|
||||
return Iterator<Map>(this->handle());
|
||||
}
|
||||
Iterator<v8::internal::Object> Constants() {
|
||||
if (this->is_bitset()) return Iterator<v8::internal::Object>();
|
||||
return Iterator<v8::internal::Object>(this->handle());
|
||||
}
|
||||
|
||||
private:
|
||||
// A union is a fixed array containing types. Invariants:
|
||||
@ -138,24 +186,29 @@ class Type : public Object {
|
||||
kNull = 1 << 0,
|
||||
kUndefined = 1 << 1,
|
||||
kBoolean = 1 << 2,
|
||||
kSmi = 1 << 3,
|
||||
kDouble = 1 << 4,
|
||||
kSymbol = 1 << 5,
|
||||
kInternalizedString = 1 << 6,
|
||||
kOtherString = 1 << 7,
|
||||
kArray = 1 << 8,
|
||||
kFunction = 1 << 9,
|
||||
kOtherObject = 1 << 10,
|
||||
kProxy = 1 << 11,
|
||||
kInteger31 = 1 << 3,
|
||||
kOtherInteger = 1 << 4,
|
||||
kDouble = 1 << 5,
|
||||
kSymbol = 1 << 6,
|
||||
kInternalizedString = 1 << 7,
|
||||
kOtherString = 1 << 8,
|
||||
kUndetectable = 1 << 9,
|
||||
kArray = 1 << 10,
|
||||
kFunction = 1 << 11,
|
||||
kOtherObject = 1 << 12,
|
||||
kProxy = 1 << 13,
|
||||
|
||||
kOddball = kBoolean | kNull | kUndefined,
|
||||
kNumber = kSmi | kDouble,
|
||||
kInteger32 = kInteger31 | kOtherInteger,
|
||||
kNumber = kInteger32 | kDouble,
|
||||
kString = kInternalizedString | kOtherString,
|
||||
kUniqueName = kSymbol | kInternalizedString,
|
||||
kName = kSymbol | kString,
|
||||
kObject = kArray | kFunction | kOtherObject,
|
||||
kObject = kUndetectable | kArray | kFunction | kOtherObject,
|
||||
kReceiver = kObject | kProxy,
|
||||
kAny = kOddball | kNumber | kName | kReceiver,
|
||||
kAllocated = kDouble | kName | kReceiver,
|
||||
kAny = kOddball | kNumber | kAllocated,
|
||||
kDetectable = kAllocated - kUndetectable,
|
||||
kNone = 0
|
||||
};
|
||||
|
||||
@ -166,7 +219,10 @@ class Type : public Object {
|
||||
|
||||
int as_bitset() { return Smi::cast(this)->value(); }
|
||||
Handle<Map> as_class() { return Handle<Map>::cast(handle()); }
|
||||
Handle<Box> as_constant() { return Handle<Box>::cast(handle()); }
|
||||
Handle<v8::internal::Object> as_constant() {
|
||||
Handle<Box> box = Handle<Box>::cast(handle());
|
||||
return v8::internal::handle(box->value(), box->GetIsolate());
|
||||
}
|
||||
Handle<Unioned> as_union() { return Handle<Unioned>::cast(handle()); }
|
||||
|
||||
Handle<Type> handle() { return handle_via_isolate_of(this); }
|
||||
|
@ -52,7 +52,7 @@ AstTyper::AstTyper(CompilationInfo* info)
|
||||
} while (false)
|
||||
|
||||
|
||||
void AstTyper::Type(CompilationInfo* info) {
|
||||
void AstTyper::Run(CompilationInfo* info) {
|
||||
AstTyper* visitor = new(info->zone()) AstTyper(info);
|
||||
Scope* scope = info->scope();
|
||||
|
||||
|
@ -43,7 +43,7 @@ namespace internal {
|
||||
|
||||
class AstTyper: public AstVisitor {
|
||||
public:
|
||||
static void Type(CompilationInfo* info);
|
||||
static void Run(CompilationInfo* info);
|
||||
|
||||
void* operator new(size_t size, Zone* zone) {
|
||||
return zone->New(static_cast<int>(size));
|
||||
|
@ -34,26 +34,26 @@
|
||||
|
||||
using namespace v8::internal;
|
||||
|
||||
#define Types CompareNilICStub::Types
|
||||
typedef CompareNilICStub::State State;
|
||||
|
||||
TEST(TypeConstructors) {
|
||||
Types types;
|
||||
types.Add(CompareNilICStub::MONOMORPHIC_MAP);
|
||||
Types types2(types);
|
||||
CHECK_EQ(types.ToIntegral(), types2.ToIntegral());
|
||||
TEST(StateConstructors) {
|
||||
State state;
|
||||
state.Add(CompareNilICStub::MONOMORPHIC_MAP);
|
||||
State state2(state);
|
||||
CHECK_EQ(state.ToIntegral(), state2.ToIntegral());
|
||||
}
|
||||
|
||||
TEST(ExternalICStateParsing) {
|
||||
Types types;
|
||||
types.Add(CompareNilICStub::UNDEFINED);
|
||||
CompareNilICStub stub(kUndefinedValue, types);
|
||||
State state;
|
||||
state.Add(CompareNilICStub::UNDEFINED);
|
||||
CompareNilICStub stub(kUndefinedValue, state);
|
||||
CompareNilICStub stub2(stub.GetExtraICState());
|
||||
CHECK_EQ(stub.GetNilValue(), stub2.GetNilValue());
|
||||
CHECK_EQ(stub.GetTypes().ToIntegral(), stub2.GetTypes().ToIntegral());
|
||||
CHECK_EQ(stub.GetState().ToIntegral(), stub2.GetState().ToIntegral());
|
||||
}
|
||||
|
||||
TEST(SettingTypes) {
|
||||
Types state;
|
||||
TEST(SettingState) {
|
||||
State state;
|
||||
CHECK(state.IsEmpty());
|
||||
state.Add(CompareNilICStub::NULL_TYPE);
|
||||
CHECK(!state.IsEmpty());
|
||||
@ -66,20 +66,22 @@ TEST(SettingTypes) {
|
||||
CHECK(!state.Contains(CompareNilICStub::UNDETECTABLE));
|
||||
}
|
||||
|
||||
TEST(ClearTypes) {
|
||||
Types state;
|
||||
TEST(ClearState) {
|
||||
State state;
|
||||
state.Add(CompareNilICStub::NULL_TYPE);
|
||||
state.RemoveAll();
|
||||
CHECK(state.IsEmpty());
|
||||
}
|
||||
|
||||
TEST(FullCompare) {
|
||||
Types state;
|
||||
CHECK(Types::FullCompare() != state);
|
||||
TEST(Generic) {
|
||||
State state;
|
||||
CHECK(State::Generic() != state);
|
||||
state.Add(CompareNilICStub::UNDEFINED);
|
||||
CHECK(state != Types::FullCompare());
|
||||
CHECK(state != State::Generic());
|
||||
state.Add(CompareNilICStub::NULL_TYPE);
|
||||
CHECK(state != Types::FullCompare());
|
||||
CHECK(state != State::Generic());
|
||||
state.Add(CompareNilICStub::UNDETECTABLE);
|
||||
CHECK(state == Types::FullCompare());
|
||||
CHECK(state != State::Generic());
|
||||
state.Add(CompareNilICStub::GENERIC);
|
||||
CHECK(state == State::Generic());
|
||||
}
|
||||
|
@ -51,7 +51,8 @@ class HandlifiedTypes {
|
||||
Null(Type::Null(), isolate),
|
||||
Undefined(Type::Undefined(), isolate),
|
||||
Number(Type::Number(), isolate),
|
||||
Smi(Type::Smi(), isolate),
|
||||
Integer31(Type::Integer31(), isolate),
|
||||
Integer32(Type::Integer32(), isolate),
|
||||
Double(Type::Double(), isolate),
|
||||
Name(Type::Name(), isolate),
|
||||
UniqueName(Type::UniqueName(), isolate),
|
||||
@ -72,7 +73,7 @@ class HandlifiedTypes {
|
||||
array = isolate->factory()->NewJSArray(20);
|
||||
ObjectClass = handle(Type::Class(object_map), isolate);
|
||||
ArrayClass = handle(Type::Class(array_map), isolate);
|
||||
SmiConstant = handle(Type::Constant(smi, isolate), isolate);
|
||||
Integer31Constant = handle(Type::Constant(smi, isolate), isolate);
|
||||
ObjectConstant1 = handle(Type::Constant(object1), isolate);
|
||||
ObjectConstant2 = handle(Type::Constant(object2), isolate);
|
||||
ArrayConstant = handle(Type::Constant(array), isolate);
|
||||
@ -85,7 +86,8 @@ class HandlifiedTypes {
|
||||
Handle<Type> Null;
|
||||
Handle<Type> Undefined;
|
||||
Handle<Type> Number;
|
||||
Handle<Type> Smi;
|
||||
Handle<Type> Integer31;
|
||||
Handle<Type> Integer32;
|
||||
Handle<Type> Double;
|
||||
Handle<Type> Name;
|
||||
Handle<Type> UniqueName;
|
||||
@ -101,7 +103,7 @@ class HandlifiedTypes {
|
||||
Handle<Type> ObjectClass;
|
||||
Handle<Type> ArrayClass;
|
||||
|
||||
Handle<Type> SmiConstant;
|
||||
Handle<Type> Integer31Constant;
|
||||
Handle<Type> ObjectConstant1;
|
||||
Handle<Type> ObjectConstant2;
|
||||
Handle<Type> ArrayConstant;
|
||||
@ -168,12 +170,12 @@ TEST(Constant) {
|
||||
HandleScope scope(isolate);
|
||||
HandlifiedTypes T(isolate);
|
||||
|
||||
CHECK(IsConstant(*T.SmiConstant));
|
||||
CHECK(IsConstant(*T.Integer31Constant));
|
||||
CHECK(IsConstant(*T.ObjectConstant1));
|
||||
CHECK(IsConstant(*T.ObjectConstant2));
|
||||
CHECK(IsConstant(*T.ArrayConstant));
|
||||
|
||||
CHECK(*T.smi == AsConstant(*T.SmiConstant));
|
||||
CHECK(*T.smi == AsConstant(*T.Integer31Constant));
|
||||
CHECK(*T.object1 == AsConstant(*T.ObjectConstant1));
|
||||
CHECK(*T.object2 == AsConstant(*T.ObjectConstant2));
|
||||
CHECK(*T.object1 != AsConstant(*T.ObjectConstant2));
|
||||
@ -224,9 +226,12 @@ TEST(Is) {
|
||||
CheckUnordered(T.Boolean, T.Undefined);
|
||||
|
||||
CheckSub(T.Number, T.Any);
|
||||
CheckSub(T.Smi, T.Number);
|
||||
CheckSub(T.Integer31, T.Number);
|
||||
CheckSub(T.Integer32, T.Number);
|
||||
CheckSub(T.Double, T.Number);
|
||||
CheckUnordered(T.Smi, T.Double);
|
||||
CheckSub(T.Integer31, T.Integer32);
|
||||
CheckUnordered(T.Integer31, T.Double);
|
||||
CheckUnordered(T.Integer32, T.Double);
|
||||
|
||||
CheckSub(T.Name, T.Any);
|
||||
CheckSub(T.UniqueName, T.Any);
|
||||
@ -255,8 +260,9 @@ TEST(Is) {
|
||||
CheckSub(T.ArrayClass, T.Object);
|
||||
CheckUnordered(T.ObjectClass, T.ArrayClass);
|
||||
|
||||
CheckSub(T.SmiConstant, T.Smi);
|
||||
CheckSub(T.SmiConstant, T.Number);
|
||||
CheckSub(T.Integer31Constant, T.Integer31);
|
||||
CheckSub(T.Integer31Constant, T.Integer32);
|
||||
CheckSub(T.Integer31Constant, T.Number);
|
||||
CheckSub(T.ObjectConstant1, T.Object);
|
||||
CheckSub(T.ObjectConstant2, T.Object);
|
||||
CheckSub(T.ArrayConstant, T.Object);
|
||||
@ -308,9 +314,9 @@ TEST(Maybe) {
|
||||
CheckDisjoint(T.Boolean, T.Undefined);
|
||||
|
||||
CheckOverlap(T.Number, T.Any);
|
||||
CheckOverlap(T.Smi, T.Number);
|
||||
CheckOverlap(T.Integer31, T.Number);
|
||||
CheckOverlap(T.Double, T.Number);
|
||||
CheckDisjoint(T.Smi, T.Double);
|
||||
CheckDisjoint(T.Integer32, T.Double);
|
||||
|
||||
CheckOverlap(T.Name, T.Any);
|
||||
CheckOverlap(T.UniqueName, T.Any);
|
||||
@ -340,9 +346,10 @@ TEST(Maybe) {
|
||||
CheckOverlap(T.ArrayClass, T.ArrayClass);
|
||||
CheckDisjoint(T.ObjectClass, T.ArrayClass);
|
||||
|
||||
CheckOverlap(T.SmiConstant, T.Smi);
|
||||
CheckOverlap(T.SmiConstant, T.Number);
|
||||
CheckDisjoint(T.SmiConstant, T.Double);
|
||||
CheckOverlap(T.Integer31Constant, T.Integer31);
|
||||
CheckOverlap(T.Integer31Constant, T.Integer32);
|
||||
CheckOverlap(T.Integer31Constant, T.Number);
|
||||
CheckDisjoint(T.Integer31Constant, T.Double);
|
||||
CheckOverlap(T.ObjectConstant1, T.Object);
|
||||
CheckOverlap(T.ObjectConstant2, T.Object);
|
||||
CheckOverlap(T.ArrayConstant, T.Object);
|
||||
@ -425,21 +432,22 @@ TEST(Union) {
|
||||
|
||||
CheckEqual(T.Union(T.ObjectClass, T.Object), T.Object);
|
||||
CheckSub(T.Union(T.ObjectClass, T.Number), T.Any);
|
||||
CheckSub(T.Union(T.ObjectClass, T.Smi), T.Union(T.Object, T.Number));
|
||||
CheckSub(T.Union(T.ObjectClass, T.Integer31), T.Union(T.Object, T.Number));
|
||||
CheckSub(T.Union(T.ObjectClass, T.Array), T.Object);
|
||||
CheckUnordered(T.Union(T.ObjectClass, T.String), T.Array);
|
||||
CheckOverlap(T.Union(T.ObjectClass, T.String), T.Object);
|
||||
CheckDisjoint(T.Union(T.ObjectClass, T.String), T.Number);
|
||||
|
||||
// Bitset-constant
|
||||
CHECK(IsBitset(Type::Union(T.SmiConstant, T.Number)));
|
||||
CHECK(IsBitset(Type::Union(T.Integer31Constant, T.Number)));
|
||||
CHECK(IsBitset(Type::Union(T.ObjectConstant1, T.Object)));
|
||||
CHECK(IsUnion(Type::Union(T.ObjectConstant2, T.Number)));
|
||||
|
||||
CheckEqual(T.Union(T.SmiConstant, T.Number), T.Number);
|
||||
CheckEqual(T.Union(T.Integer31Constant, T.Number), T.Number);
|
||||
CheckEqual(T.Union(T.ObjectConstant1, T.Object), T.Object);
|
||||
CheckSub(T.Union(T.ObjectConstant1, T.Number), T.Any);
|
||||
CheckSub(T.Union(T.ObjectConstant1, T.Smi), T.Union(T.Object, T.Number));
|
||||
CheckSub(
|
||||
T.Union(T.ObjectConstant1, T.Integer32), T.Union(T.Object, T.Number));
|
||||
CheckSub(T.Union(T.ObjectConstant1, T.Array), T.Object);
|
||||
CheckUnordered(T.Union(T.ObjectConstant1, T.String), T.Array);
|
||||
CheckOverlap(T.Union(T.ObjectConstant1, T.String), T.Object);
|
||||
@ -516,8 +524,8 @@ TEST(Union) {
|
||||
T.Union(T.ObjectConstant2, T.Union(T.ArrayConstant, T.ObjectConstant1)));
|
||||
|
||||
// Union-union
|
||||
CHECK(IsBitset(
|
||||
Type::Union(T.Union(T.Number, T.ArrayClass), T.Union(T.Smi, T.Array))));
|
||||
CHECK(IsBitset(Type::Union(
|
||||
T.Union(T.Number, T.ArrayClass), T.Union(T.Integer32, T.Array))));
|
||||
|
||||
CheckEqual(
|
||||
T.Union(T.Union(T.ObjectConstant2, T.ObjectConstant1),
|
||||
@ -528,6 +536,6 @@ TEST(Union) {
|
||||
T.Union(T.ObjectConstant1, T.ArrayConstant)),
|
||||
T.Union(T.Union(T.ObjectConstant1, T.ObjectConstant2), T.ArrayConstant));
|
||||
CheckEqual(
|
||||
T.Union(T.Union(T.Number, T.ArrayClass), T.Union(T.Smi, T.Array)),
|
||||
T.Union(T.Union(T.Number, T.ArrayClass), T.Union(T.Integer31, T.Array)),
|
||||
T.Union(T.Number, T.Array));
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user