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 {
void Initialize(Expression* tag, ZoneList<CaseClause*>* cases) {
tag_ = tag;
cases_ = cases;
switch_type_ = UNKNOWN_SWITCH;
}
Expression* tag() const { return tag_; }
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:
SwitchStatement(Isolate* isolate, ZoneStringList* labels, int pos)
: BreakableStatement(isolate, labels, TARGET_FOR_ANONYMOUS, pos),
......@@ -1163,7 +1158,6 @@ class SwitchStatement V8_FINAL : public BreakableStatement {
private:
Expression* tag_;
ZoneList<CaseClause*>* cases_;
SwitchType switch_type_;
};
......
......@@ -1176,10 +1176,6 @@ class ICCompareStub: public PlatformCodeStub {
CompareIC::State* handler_state,
Token::Value* op);
static CompareIC::State CompareState(int minor_key) {
return static_cast<CompareIC::State>(HandlerStateField::decode(minor_key));
}
virtual InlineCacheState GetICState();
private:
......
......@@ -4136,8 +4136,7 @@ void HOptimizedGraphBuilder::VisitSwitchStatement(SwitchStatement* stmt) {
ASSERT(current_block() != NULL);
ASSERT(current_block()->HasPredecessor());
// We only optimize switch statements with smi-literal smi comparisons,
// with a bounded number of clauses.
// We only optimize switch statements with a bounded number of clauses.
const int kCaseClauseLimit = 128;
ZoneList<CaseClause*>* clauses = stmt->cases();
int clause_count = clauses->length();
......@@ -4146,28 +4145,10 @@ void HOptimizedGraphBuilder::VisitSwitchStatement(SwitchStatement* stmt) {
return Bailout(kSwitchStatementTooManyClauses);
}
ASSERT(stmt->switch_type() != SwitchStatement::UNKNOWN_SWITCH);
CHECK_ALIVE(VisitForValue(stmt->tag()));
Add<HSimulate>(stmt->EntryId());
HValue* tag_value = Top();
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);
}
Handle<Type> tag_type = stmt->tag()->bounds().lower;
// 1. Build all the tests, with dangling true branches
BailoutId default_id = BailoutId::None();
......@@ -4183,35 +4164,12 @@ void HOptimizedGraphBuilder::VisitSwitchStatement(SwitchStatement* stmt) {
CHECK_ALIVE(VisitForValue(clause->label()));
HValue* label_value = Pop();
HControlInstruction* compare;
if (stmt->switch_type() == SwitchStatement::SMI_SWITCH) {
if (!clause->compare_type()->Is(Type::Smi())) {
Add<HDeoptimize>("Non-smi switch type", Deoptimizer::SOFT);
}
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);
}
Handle<Type> label_type = clause->label()->bounds().lower;
Handle<Type> combined_type = clause->compare_type();
HControlInstruction* compare = BuildCompareInstruction(
Token::EQ_STRICT, tag_value, label_value, tag_type, label_type,
combined_type, stmt->tag()->position(), clause->label()->position(),
clause->id());
HBasicBlock* next_test_block = graph()->CreateBasicBlock();
HBasicBlock* body_block = graph()->CreateBasicBlock();
......@@ -4231,11 +4189,6 @@ void HOptimizedGraphBuilder::VisitSwitchStatement(SwitchStatement* stmt) {
HBasicBlock* last_block = current_block();
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,
// translating the clause bodies.
HBasicBlock* fall_through_block = NULL;
......@@ -9142,9 +9095,6 @@ void HOptimizedGraphBuilder::VisitCompareOperation(CompareOperation* expr) {
Handle<Type> left_type = expr->left()->bounds().lower;
Handle<Type> right_type = expr->right()->bounds().lower;
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->right()));
......@@ -9218,34 +9168,54 @@ void HOptimizedGraphBuilder::VisitCompareOperation(CompareOperation* expr) {
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())) {
switch (op) {
case Token::EQ:
case Token::EQ_STRICT: {
// Can we get away with map check and not instance type check?
if (combined_type->IsClass()) {
Handle<Map> map = combined_type->AsClass();
AddCheckMap(left, map);
AddCheckMap(right, map);
HCompareObjectEqAndBranch* result =
New<HCompareObjectEqAndBranch>(left, right);
if (FLAG_emit_opt_code_positions) {
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());
if (Token::IsEqualityOp(op)) {
// Can we get away with map check and not instance type check?
if (combined_type->IsClass()) {
Handle<Map> map = combined_type->AsClass();
AddCheckMap(left, map);
AddCheckMap(right, map);
HCompareObjectEqAndBranch* result =
New<HCompareObjectEqAndBranch>(left, right);
if (FLAG_emit_opt_code_positions) {
result->set_operand_position(zone(), 0, left_position);
result->set_operand_position(zone(), 1, right_position);
}
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:
return Bailout(kUnsupportedNonPrimitiveCompare);
} else {
Bailout(kUnsupportedNonPrimitiveCompare);
return NULL;
}
} else if (combined_type->Is(Type::InternalizedString()) &&
Token::IsEqualityOp(op)) {
......@@ -9255,7 +9225,7 @@ void HOptimizedGraphBuilder::VisitCompareOperation(CompareOperation* expr) {
Add<HCheckInstanceType>(right, HCheckInstanceType::IS_INTERNALIZED_STRING);
HCompareObjectEqAndBranch* result =
New<HCompareObjectEqAndBranch>(left, right);
return ast_context()->ReturnControl(result, expr->id());
return result;
} else if (combined_type->Is(Type::String())) {
BuildCheckHeapObject(left);
Add<HCheckInstanceType>(left, HCheckInstanceType::IS_STRING);
......@@ -9263,23 +9233,28 @@ void HOptimizedGraphBuilder::VisitCompareOperation(CompareOperation* expr) {
Add<HCheckInstanceType>(right, HCheckInstanceType::IS_STRING);
HStringCompareAndBranch* result =
New<HStringCompareAndBranch>(left, right, op);
return ast_context()->ReturnControl(result, expr->id());
return result;
} else {
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(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 {
HCompareNumericAndBranch* result =
New<HCompareNumericAndBranch>(left, right, op);
result->set_observed_input_representation(left_rep, right_rep);
if (FLAG_emit_opt_code_positions) {
result->SetOperandPositions(zone(),
expr->left()->position(),
expr->right()->position());
result->SetOperandPositions(zone(), left_position, right_position);
}
return ast_context()->ReturnControl(result, expr->id());
return result;
}
}
}
......
......@@ -2323,6 +2323,15 @@ class HOptimizedGraphBuilder : public HGraphBuilder, public AstVisitor {
void HandleLiteralCompareNil(CompareOperation* expr,
Expression* sub_expr,
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,
HValue* index);
......
......@@ -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<Object> object = GetInfo(id);
if (!object->IsCode()) return handle(Type::None(), isolate_);
......
......@@ -303,8 +303,6 @@ class TypeFeedbackOracle: public ZoneObject {
Handle<Type> CountType(TypeFeedbackId id);
Handle<Type> ClauseType(TypeFeedbackId id);
Zone* zone() const { return zone_; }
Isolate* isolate() const { return isolate_; }
......
......@@ -222,24 +222,23 @@ void AstTyper::VisitSwitchStatement(SwitchStatement* stmt) {
RECURSE(Visit(stmt->tag()));
ZoneList<CaseClause*>* clauses = stmt->cases();
SwitchStatement::SwitchType switch_type = stmt->switch_type();
Effects local_effects(zone());
bool complex_effects = false; // True for label effects or fall-through.
for (int i = 0; i < clauses->length(); ++i) {
CaseClause* clause = clauses->at(i);
Effects clause_effects = EnterEffects();
if (!clause->is_default()) {
Expression* label = clause->label();
SwitchStatement::SwitchType label_switch_type =
label->IsSmiLiteral() ? SwitchStatement::SMI_SWITCH :
label->IsStringLiteral() ? SwitchStatement::STRING_SWITCH :
SwitchStatement::GENERIC_SWITCH;
if (switch_type == SwitchStatement::UNKNOWN_SWITCH)
switch_type = label_switch_type;
else if (switch_type != label_switch_type)
switch_type = SwitchStatement::GENERIC_SWITCH;
// Collect type feedback.
Handle<Type> tag_type, label_type, combined_type;
oracle()->CompareType(clause->CompareId(),
&tag_type, &label_type, &combined_type);
NarrowLowerType(stmt->tag(), tag_type);
NarrowLowerType(label, label_type);
clause->set_compare_type(combined_type);
RECURSE(Visit(label));
if (!clause_effects.IsEmpty()) complex_effects = true;
......@@ -260,20 +259,6 @@ void AstTyper::VisitSwitchStatement(SwitchStatement* stmt) {
} else {
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