Hydrogen: Re-use regular comparisons infrastructure for switch statements

R=rossberg@chromium.org

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

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@18348 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent fb7218dc
...@@ -1144,16 +1144,11 @@ class SwitchStatement V8_FINAL : public BreakableStatement { ...@@ -1144,16 +1144,11 @@ class SwitchStatement V8_FINAL : public BreakableStatement {
void Initialize(Expression* tag, ZoneList<CaseClause*>* cases) { void Initialize(Expression* tag, ZoneList<CaseClause*>* cases) {
tag_ = tag; tag_ = tag;
cases_ = cases; cases_ = cases;
switch_type_ = UNKNOWN_SWITCH;
} }
Expression* tag() const { return tag_; } Expression* tag() const { return tag_; }
ZoneList<CaseClause*>* cases() const { return cases_; } ZoneList<CaseClause*>* cases() const { return cases_; }
enum SwitchType { UNKNOWN_SWITCH, SMI_SWITCH, STRING_SWITCH, GENERIC_SWITCH };
SwitchType switch_type() const { return switch_type_; }
void set_switch_type(SwitchType switch_type) { switch_type_ = switch_type; }
protected: protected:
SwitchStatement(Isolate* isolate, ZoneStringList* labels, int pos) SwitchStatement(Isolate* isolate, ZoneStringList* labels, int pos)
: BreakableStatement(isolate, labels, TARGET_FOR_ANONYMOUS, pos), : BreakableStatement(isolate, labels, TARGET_FOR_ANONYMOUS, pos),
...@@ -1163,7 +1158,6 @@ class SwitchStatement V8_FINAL : public BreakableStatement { ...@@ -1163,7 +1158,6 @@ class SwitchStatement V8_FINAL : public BreakableStatement {
private: private:
Expression* tag_; Expression* tag_;
ZoneList<CaseClause*>* cases_; ZoneList<CaseClause*>* cases_;
SwitchType switch_type_;
}; };
......
...@@ -1176,10 +1176,6 @@ class ICCompareStub: public PlatformCodeStub { ...@@ -1176,10 +1176,6 @@ class ICCompareStub: public PlatformCodeStub {
CompareIC::State* handler_state, CompareIC::State* handler_state,
Token::Value* op); Token::Value* op);
static CompareIC::State CompareState(int minor_key) {
return static_cast<CompareIC::State>(HandlerStateField::decode(minor_key));
}
virtual InlineCacheState GetICState(); virtual InlineCacheState GetICState();
private: private:
......
...@@ -4136,8 +4136,7 @@ void HOptimizedGraphBuilder::VisitSwitchStatement(SwitchStatement* stmt) { ...@@ -4136,8 +4136,7 @@ void HOptimizedGraphBuilder::VisitSwitchStatement(SwitchStatement* stmt) {
ASSERT(current_block() != NULL); ASSERT(current_block() != NULL);
ASSERT(current_block()->HasPredecessor()); ASSERT(current_block()->HasPredecessor());
// We only optimize switch statements with smi-literal smi comparisons, // We only optimize switch statements with a bounded number of clauses.
// with a bounded number of clauses.
const int kCaseClauseLimit = 128; const int kCaseClauseLimit = 128;
ZoneList<CaseClause*>* clauses = stmt->cases(); ZoneList<CaseClause*>* clauses = stmt->cases();
int clause_count = clauses->length(); int clause_count = clauses->length();
...@@ -4146,28 +4145,10 @@ void HOptimizedGraphBuilder::VisitSwitchStatement(SwitchStatement* stmt) { ...@@ -4146,28 +4145,10 @@ void HOptimizedGraphBuilder::VisitSwitchStatement(SwitchStatement* stmt) {
return Bailout(kSwitchStatementTooManyClauses); return Bailout(kSwitchStatementTooManyClauses);
} }
ASSERT(stmt->switch_type() != SwitchStatement::UNKNOWN_SWITCH);
CHECK_ALIVE(VisitForValue(stmt->tag())); CHECK_ALIVE(VisitForValue(stmt->tag()));
Add<HSimulate>(stmt->EntryId()); Add<HSimulate>(stmt->EntryId());
HValue* tag_value = Top(); HValue* tag_value = Top();
Handle<Type> tag_type = stmt->tag()->bounds().lower;
HUnaryControlInstruction* string_check = NULL;
HBasicBlock* not_string_block = NULL;
// Test switch's tag value if all clauses are string literals
if (stmt->switch_type() == SwitchStatement::STRING_SWITCH) {
HBasicBlock* first_test_block = graph()->CreateBasicBlock();
not_string_block = graph()->CreateBasicBlock();
string_check = New<HIsStringAndBranch>(
tag_value, first_test_block, not_string_block);
FinishCurrentBlock(string_check);
set_current_block(not_string_block);
Drop(1); // tag_value
set_current_block(first_test_block);
}
// 1. Build all the tests, with dangling true branches // 1. Build all the tests, with dangling true branches
BailoutId default_id = BailoutId::None(); BailoutId default_id = BailoutId::None();
...@@ -4183,35 +4164,12 @@ void HOptimizedGraphBuilder::VisitSwitchStatement(SwitchStatement* stmt) { ...@@ -4183,35 +4164,12 @@ void HOptimizedGraphBuilder::VisitSwitchStatement(SwitchStatement* stmt) {
CHECK_ALIVE(VisitForValue(clause->label())); CHECK_ALIVE(VisitForValue(clause->label()));
HValue* label_value = Pop(); HValue* label_value = Pop();
HControlInstruction* compare; Handle<Type> label_type = clause->label()->bounds().lower;
Handle<Type> combined_type = clause->compare_type();
if (stmt->switch_type() == SwitchStatement::SMI_SWITCH) { HControlInstruction* compare = BuildCompareInstruction(
if (!clause->compare_type()->Is(Type::Smi())) { Token::EQ_STRICT, tag_value, label_value, tag_type, label_type,
Add<HDeoptimize>("Non-smi switch type", Deoptimizer::SOFT); combined_type, stmt->tag()->position(), clause->label()->position(),
} clause->id());
HCompareNumericAndBranch* compare_ =
New<HCompareNumericAndBranch>(tag_value,
label_value,
Token::EQ_STRICT);
compare_->set_observed_input_representation(
Representation::Smi(), Representation::Smi());
compare = compare_;
} else if (stmt->switch_type() == SwitchStatement::STRING_SWITCH) {
compare = New<HStringCompareAndBranch>(tag_value,
label_value,
Token::EQ_STRICT);
} else {
HValue* test = Add<HCompareGeneric>(tag_value,
label_value,
Token::EQ_STRICT);
if (test->HasObservableSideEffects()) {
Push(test);
Add<HSimulate>(clause->id(), REMOVABLE_SIMULATE);
Drop(1);
}
compare = New<HBranch>(test);
}
HBasicBlock* next_test_block = graph()->CreateBasicBlock(); HBasicBlock* next_test_block = graph()->CreateBasicBlock();
HBasicBlock* body_block = graph()->CreateBasicBlock(); HBasicBlock* body_block = graph()->CreateBasicBlock();
...@@ -4231,11 +4189,6 @@ void HOptimizedGraphBuilder::VisitSwitchStatement(SwitchStatement* stmt) { ...@@ -4231,11 +4189,6 @@ void HOptimizedGraphBuilder::VisitSwitchStatement(SwitchStatement* stmt) {
HBasicBlock* last_block = current_block(); HBasicBlock* last_block = current_block();
Drop(1); // tag_value Drop(1); // tag_value
if (not_string_block != NULL) {
BailoutId join_id = !default_id.IsNone() ? default_id : stmt->ExitId();
last_block = CreateJoin(last_block, not_string_block, join_id);
}
// 2. Loop over the clauses and the linked list of tests in lockstep, // 2. Loop over the clauses and the linked list of tests in lockstep,
// translating the clause bodies. // translating the clause bodies.
HBasicBlock* fall_through_block = NULL; HBasicBlock* fall_through_block = NULL;
...@@ -9142,9 +9095,6 @@ void HOptimizedGraphBuilder::VisitCompareOperation(CompareOperation* expr) { ...@@ -9142,9 +9095,6 @@ void HOptimizedGraphBuilder::VisitCompareOperation(CompareOperation* expr) {
Handle<Type> left_type = expr->left()->bounds().lower; Handle<Type> left_type = expr->left()->bounds().lower;
Handle<Type> right_type = expr->right()->bounds().lower; Handle<Type> right_type = expr->right()->bounds().lower;
Handle<Type> combined_type = expr->combined_type(); Handle<Type> combined_type = expr->combined_type();
Representation combined_rep = Representation::FromType(combined_type);
Representation left_rep = Representation::FromType(left_type);
Representation right_rep = Representation::FromType(right_type);
CHECK_ALIVE(VisitForValue(expr->left())); CHECK_ALIVE(VisitForValue(expr->left()));
CHECK_ALIVE(VisitForValue(expr->right())); CHECK_ALIVE(VisitForValue(expr->right()));
...@@ -9218,34 +9168,54 @@ void HOptimizedGraphBuilder::VisitCompareOperation(CompareOperation* expr) { ...@@ -9218,34 +9168,54 @@ void HOptimizedGraphBuilder::VisitCompareOperation(CompareOperation* expr) {
combined_type = left_type = right_type = handle(Type::Any(), isolate()); combined_type = left_type = right_type = handle(Type::Any(), isolate());
} }
HControlInstruction* compare = BuildCompareInstruction(
op, left, right, left_type, right_type, combined_type,
expr->left()->position(), expr->right()->position(), expr->id());
if (compare == NULL) return; // Bailed out.
return ast_context()->ReturnControl(compare, expr->id());
}
HControlInstruction* HOptimizedGraphBuilder::BuildCompareInstruction(
Token::Value op,
HValue* left,
HValue* right,
Handle<Type> left_type,
Handle<Type> right_type,
Handle<Type> combined_type,
int left_position,
int right_position,
BailoutId bailout_id) {
Representation left_rep = Representation::FromType(left_type);
Representation right_rep = Representation::FromType(right_type);
Representation combined_rep = Representation::FromType(combined_type);
if (combined_type->Is(Type::Receiver())) { if (combined_type->Is(Type::Receiver())) {
switch (op) { if (Token::IsEqualityOp(op)) {
case Token::EQ: // Can we get away with map check and not instance type check?
case Token::EQ_STRICT: { if (combined_type->IsClass()) {
// Can we get away with map check and not instance type check? Handle<Map> map = combined_type->AsClass();
if (combined_type->IsClass()) { AddCheckMap(left, map);
Handle<Map> map = combined_type->AsClass(); AddCheckMap(right, map);
AddCheckMap(left, map); HCompareObjectEqAndBranch* result =
AddCheckMap(right, map); New<HCompareObjectEqAndBranch>(left, right);
HCompareObjectEqAndBranch* result = if (FLAG_emit_opt_code_positions) {
New<HCompareObjectEqAndBranch>(left, right); result->set_operand_position(zone(), 0, left_position);
if (FLAG_emit_opt_code_positions) { result->set_operand_position(zone(), 1, right_position);
result->set_operand_position(zone(), 0, expr->left()->position());
result->set_operand_position(zone(), 1, expr->right()->position());
}
return ast_context()->ReturnControl(result, expr->id());
} else {
BuildCheckHeapObject(left);
Add<HCheckInstanceType>(left, HCheckInstanceType::IS_SPEC_OBJECT);
BuildCheckHeapObject(right);
Add<HCheckInstanceType>(right, HCheckInstanceType::IS_SPEC_OBJECT);
HCompareObjectEqAndBranch* result =
New<HCompareObjectEqAndBranch>(left, right);
return ast_context()->ReturnControl(result, expr->id());
} }
return result;
} else {
BuildCheckHeapObject(left);
Add<HCheckInstanceType>(left, HCheckInstanceType::IS_SPEC_OBJECT);
BuildCheckHeapObject(right);
Add<HCheckInstanceType>(right, HCheckInstanceType::IS_SPEC_OBJECT);
HCompareObjectEqAndBranch* result =
New<HCompareObjectEqAndBranch>(left, right);
return result;
} }
default: } else {
return Bailout(kUnsupportedNonPrimitiveCompare); Bailout(kUnsupportedNonPrimitiveCompare);
return NULL;
} }
} else if (combined_type->Is(Type::InternalizedString()) && } else if (combined_type->Is(Type::InternalizedString()) &&
Token::IsEqualityOp(op)) { Token::IsEqualityOp(op)) {
...@@ -9255,7 +9225,7 @@ void HOptimizedGraphBuilder::VisitCompareOperation(CompareOperation* expr) { ...@@ -9255,7 +9225,7 @@ void HOptimizedGraphBuilder::VisitCompareOperation(CompareOperation* expr) {
Add<HCheckInstanceType>(right, HCheckInstanceType::IS_INTERNALIZED_STRING); Add<HCheckInstanceType>(right, HCheckInstanceType::IS_INTERNALIZED_STRING);
HCompareObjectEqAndBranch* result = HCompareObjectEqAndBranch* result =
New<HCompareObjectEqAndBranch>(left, right); New<HCompareObjectEqAndBranch>(left, right);
return ast_context()->ReturnControl(result, expr->id()); return result;
} else if (combined_type->Is(Type::String())) { } else if (combined_type->Is(Type::String())) {
BuildCheckHeapObject(left); BuildCheckHeapObject(left);
Add<HCheckInstanceType>(left, HCheckInstanceType::IS_STRING); Add<HCheckInstanceType>(left, HCheckInstanceType::IS_STRING);
...@@ -9263,23 +9233,28 @@ void HOptimizedGraphBuilder::VisitCompareOperation(CompareOperation* expr) { ...@@ -9263,23 +9233,28 @@ void HOptimizedGraphBuilder::VisitCompareOperation(CompareOperation* expr) {
Add<HCheckInstanceType>(right, HCheckInstanceType::IS_STRING); Add<HCheckInstanceType>(right, HCheckInstanceType::IS_STRING);
HStringCompareAndBranch* result = HStringCompareAndBranch* result =
New<HStringCompareAndBranch>(left, right, op); New<HStringCompareAndBranch>(left, right, op);
return ast_context()->ReturnControl(result, expr->id()); return result;
} else { } else {
if (combined_rep.IsTagged() || combined_rep.IsNone()) { if (combined_rep.IsTagged() || combined_rep.IsNone()) {
HCompareGeneric* result = New<HCompareGeneric>(left, right, op); HCompareGeneric* result = Add<HCompareGeneric>(left, right, op);
result->set_observed_input_representation(1, left_rep); result->set_observed_input_representation(1, left_rep);
result->set_observed_input_representation(2, right_rep); result->set_observed_input_representation(2, right_rep);
return ast_context()->ReturnInstruction(result, expr->id()); if (result->HasObservableSideEffects()) {
Push(result);
AddSimulate(bailout_id, REMOVABLE_SIMULATE);
Drop(1);
}
// TODO(jkummerow): Can we make this more efficient?
HBranch* branch = New<HBranch>(result);
return branch;
} else { } else {
HCompareNumericAndBranch* result = HCompareNumericAndBranch* result =
New<HCompareNumericAndBranch>(left, right, op); New<HCompareNumericAndBranch>(left, right, op);
result->set_observed_input_representation(left_rep, right_rep); result->set_observed_input_representation(left_rep, right_rep);
if (FLAG_emit_opt_code_positions) { if (FLAG_emit_opt_code_positions) {
result->SetOperandPositions(zone(), result->SetOperandPositions(zone(), left_position, right_position);
expr->left()->position(),
expr->right()->position());
} }
return ast_context()->ReturnControl(result, expr->id()); return result;
} }
} }
} }
......
...@@ -2323,6 +2323,15 @@ class HOptimizedGraphBuilder : public HGraphBuilder, public AstVisitor { ...@@ -2323,6 +2323,15 @@ class HOptimizedGraphBuilder : public HGraphBuilder, public AstVisitor {
void HandleLiteralCompareNil(CompareOperation* expr, void HandleLiteralCompareNil(CompareOperation* expr,
Expression* sub_expr, Expression* sub_expr,
NilValue nil); NilValue nil);
HControlInstruction* BuildCompareInstruction(Token::Value op,
HValue* left,
HValue* right,
Handle<Type> left_type,
Handle<Type> right_type,
Handle<Type> combined_type,
int left_position,
int right_position,
BailoutId bailout_id);
HInstruction* BuildStringCharCodeAt(HValue* string, HInstruction* BuildStringCharCodeAt(HValue* string,
HValue* index); HValue* index);
......
...@@ -320,18 +320,6 @@ void TypeFeedbackOracle::BinaryType(TypeFeedbackId id, ...@@ -320,18 +320,6 @@ void TypeFeedbackOracle::BinaryType(TypeFeedbackId id,
} }
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;
}
Handle<Type> TypeFeedbackOracle::CountType(TypeFeedbackId id) { Handle<Type> TypeFeedbackOracle::CountType(TypeFeedbackId id) {
Handle<Object> object = GetInfo(id); Handle<Object> object = GetInfo(id);
if (!object->IsCode()) return handle(Type::None(), isolate_); if (!object->IsCode()) return handle(Type::None(), isolate_);
......
...@@ -303,8 +303,6 @@ class TypeFeedbackOracle: public ZoneObject { ...@@ -303,8 +303,6 @@ class TypeFeedbackOracle: public ZoneObject {
Handle<Type> CountType(TypeFeedbackId id); Handle<Type> CountType(TypeFeedbackId id);
Handle<Type> ClauseType(TypeFeedbackId id);
Zone* zone() const { return zone_; } Zone* zone() const { return zone_; }
Isolate* isolate() const { return isolate_; } Isolate* isolate() const { return isolate_; }
......
...@@ -222,24 +222,23 @@ void AstTyper::VisitSwitchStatement(SwitchStatement* stmt) { ...@@ -222,24 +222,23 @@ void AstTyper::VisitSwitchStatement(SwitchStatement* stmt) {
RECURSE(Visit(stmt->tag())); RECURSE(Visit(stmt->tag()));
ZoneList<CaseClause*>* clauses = stmt->cases(); ZoneList<CaseClause*>* clauses = stmt->cases();
SwitchStatement::SwitchType switch_type = stmt->switch_type();
Effects local_effects(zone()); Effects local_effects(zone());
bool complex_effects = false; // True for label effects or fall-through. bool complex_effects = false; // True for label effects or fall-through.
for (int i = 0; i < clauses->length(); ++i) { for (int i = 0; i < clauses->length(); ++i) {
CaseClause* clause = clauses->at(i); CaseClause* clause = clauses->at(i);
Effects clause_effects = EnterEffects(); Effects clause_effects = EnterEffects();
if (!clause->is_default()) { if (!clause->is_default()) {
Expression* label = clause->label(); Expression* label = clause->label();
SwitchStatement::SwitchType label_switch_type = // Collect type feedback.
label->IsSmiLiteral() ? SwitchStatement::SMI_SWITCH : Handle<Type> tag_type, label_type, combined_type;
label->IsStringLiteral() ? SwitchStatement::STRING_SWITCH : oracle()->CompareType(clause->CompareId(),
SwitchStatement::GENERIC_SWITCH; &tag_type, &label_type, &combined_type);
if (switch_type == SwitchStatement::UNKNOWN_SWITCH) NarrowLowerType(stmt->tag(), tag_type);
switch_type = label_switch_type; NarrowLowerType(label, label_type);
else if (switch_type != label_switch_type) clause->set_compare_type(combined_type);
switch_type = SwitchStatement::GENERIC_SWITCH;
RECURSE(Visit(label)); RECURSE(Visit(label));
if (!clause_effects.IsEmpty()) complex_effects = true; if (!clause_effects.IsEmpty()) complex_effects = true;
...@@ -260,20 +259,6 @@ void AstTyper::VisitSwitchStatement(SwitchStatement* stmt) { ...@@ -260,20 +259,6 @@ void AstTyper::VisitSwitchStatement(SwitchStatement* stmt) {
} else { } else {
store_.Seq(local_effects); store_.Seq(local_effects);
} }
if (switch_type == SwitchStatement::UNKNOWN_SWITCH)
switch_type = SwitchStatement::GENERIC_SWITCH;
stmt->set_switch_type(switch_type);
// Collect type feedback.
// TODO(rossberg): can we eliminate this special case and extra loop?
if (switch_type == SwitchStatement::SMI_SWITCH) {
for (int i = 0; i < clauses->length(); ++i) {
CaseClause* clause = clauses->at(i);
if (!clause->is_default())
clause->set_compare_type(oracle()->ClauseType(clause->CompareId()));
}
}
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment