[hydrogen] optimize switch with string clauses

Hydrogen should optimize not only SMI clauses, but clauses with string literals
too.

Patch from fedor.indutny <fedor.indutny@gmail.com>.

R=vegorov@chromium.org
BUG=
TEST=
Review URL: http://codereview.chromium.org/8373029

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@9901 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 36b715b1
......@@ -228,6 +228,13 @@ void LIsObjectAndBranch::PrintDataTo(StringStream* stream) {
}
void LIsStringAndBranch::PrintDataTo(StringStream* stream) {
stream->Add("if is_string(");
InputAt(0)->PrintTo(stream);
stream->Add(") then B%d else B%d", true_block_id(), false_block_id());
}
void LIsSmiAndBranch::PrintDataTo(StringStream* stream) {
stream->Add("if is_smi(");
InputAt(0)->PrintTo(stream);
......@@ -242,6 +249,14 @@ void LIsUndetectableAndBranch::PrintDataTo(StringStream* stream) {
}
void LStringCompareAndBranch::PrintDataTo(StringStream* stream) {
stream->Add("if compare_generic(");
InputAt(0)->PrintTo(stream);
InputAt(1)->PrintTo(stream);
stream->Add(") then B%d else B%d", true_block_id(), false_block_id());
}
void LHasInstanceTypeAndBranch::PrintDataTo(StringStream* stream) {
stream->Add("if has_instance_type(");
InputAt(0)->PrintTo(stream);
......@@ -1451,6 +1466,13 @@ LInstruction* LChunkBuilder::DoIsObjectAndBranch(HIsObjectAndBranch* instr) {
}
LInstruction* LChunkBuilder::DoIsStringAndBranch(HIsStringAndBranch* instr) {
ASSERT(instr->value()->representation().IsTagged());
LOperand* temp = TempRegister();
return new LIsStringAndBranch(UseRegisterAtStart(instr->value()), temp);
}
LInstruction* LChunkBuilder::DoIsSmiAndBranch(HIsSmiAndBranch* instr) {
ASSERT(instr->value()->representation().IsTagged());
return new LIsSmiAndBranch(Use(instr->value()));
......@@ -1465,6 +1487,18 @@ LInstruction* LChunkBuilder::DoIsUndetectableAndBranch(
}
LInstruction* LChunkBuilder::DoStringCompareAndBranch(
HStringCompareAndBranch* instr) {
ASSERT(instr->left()->representation().IsTagged());
ASSERT(instr->right()->representation().IsTagged());
LOperand* left = UseFixed(instr->left(), r1);
LOperand* right = UseFixed(instr->right(), r0);
LStringCompareAndBranch* result = new LStringCompareAndBranch(left, right);
return MarkAsCall(result, instr);
}
LInstruction* LChunkBuilder::DoHasInstanceTypeAndBranch(
HHasInstanceTypeAndBranch* instr) {
ASSERT(instr->value()->representation().IsTagged());
......
......@@ -109,8 +109,10 @@ class LCodeGen;
V(IsConstructCallAndBranch) \
V(IsNilAndBranch) \
V(IsObjectAndBranch) \
V(IsStringAndBranch) \
V(IsSmiAndBranch) \
V(IsUndetectableAndBranch) \
V(StringCompareAndBranch) \
V(JSArrayLength) \
V(Label) \
V(LazyBailout) \
......@@ -658,6 +660,20 @@ class LIsObjectAndBranch: public LControlInstruction<1, 1> {
};
class LIsStringAndBranch: public LControlInstruction<1, 1> {
public:
LIsStringAndBranch(LOperand* value, LOperand* temp) {
inputs_[0] = value;
temps_[0] = temp;
}
DECLARE_CONCRETE_INSTRUCTION(IsStringAndBranch, "is-string-and-branch")
DECLARE_HYDROGEN_ACCESSOR(IsStringAndBranch)
virtual void PrintDataTo(StringStream* stream);
};
class LIsSmiAndBranch: public LControlInstruction<1, 0> {
public:
explicit LIsSmiAndBranch(LOperand* value) {
......@@ -686,6 +702,23 @@ class LIsUndetectableAndBranch: public LControlInstruction<1, 1> {
};
class LStringCompareAndBranch: public LControlInstruction<2, 0> {
public:
LStringCompareAndBranch(LOperand* left, LOperand* right) {
inputs_[0] = left;
inputs_[1] = right;
}
DECLARE_CONCRETE_INSTRUCTION(StringCompareAndBranch,
"compare-generic-and-branch")
DECLARE_HYDROGEN_ACCESSOR(StringCompareAndBranch)
Token::Value op() const { return hydrogen()->token(); }
virtual void PrintDataTo(StringStream* stream);
};
class LHasInstanceTypeAndBranch: public LControlInstruction<1, 0> {
public:
explicit LHasInstanceTypeAndBranch(LOperand* value) {
......
......@@ -1858,6 +1858,33 @@ void LCodeGen::DoIsObjectAndBranch(LIsObjectAndBranch* instr) {
}
Condition LCodeGen::EmitIsString(Register input,
Register temp1,
Label* is_not_string,
Label* is_string) {
__ JumpIfSmi(input, is_not_string);
__ CompareObjectType(input, temp1, temp1, FIRST_NONSTRING_TYPE);
return lt;
}
void LCodeGen::DoIsStringAndBranch(LIsStringAndBranch* instr) {
Register reg = ToRegister(instr->InputAt(0));
Register temp1 = ToRegister(instr->TempAt(0));
int true_block = chunk_->LookupDestination(instr->true_block_id());
int false_block = chunk_->LookupDestination(instr->false_block_id());
Label* true_label = chunk_->GetAssemblyLabel(true_block);
Label* false_label = chunk_->GetAssemblyLabel(false_block);
Condition true_cond =
EmitIsString(reg, temp1, false_label, true_label);
EmitBranch(true_block, false_block, true_cond);
}
void LCodeGen::DoIsSmiAndBranch(LIsSmiAndBranch* instr) {
int true_block = chunk_->LookupDestination(instr->true_block_id());
int false_block = chunk_->LookupDestination(instr->false_block_id());
......@@ -1883,6 +1910,41 @@ void LCodeGen::DoIsUndetectableAndBranch(LIsUndetectableAndBranch* instr) {
}
static Condition ComputeCompareCondition(Token::Value op) {
switch (op) {
case Token::EQ_STRICT:
case Token::EQ:
return eq;
case Token::LT:
return lt;
case Token::GT:
return gt;
case Token::LTE:
return le;
case Token::GTE:
return ge;
default:
UNREACHABLE();
return kNoCondition;
}
}
void LCodeGen::DoStringCompareAndBranch(LStringCompareAndBranch* instr) {
Token::Value op = instr->op();
int true_block = chunk_->LookupDestination(instr->true_block_id());
int false_block = chunk_->LookupDestination(instr->false_block_id());
Handle<Code> ic = CompareIC::GetUninitialized(op);
CallCode(ic, RelocInfo::CODE_TARGET, instr);
__ cmp(r0, Operand(0)); // This instruction also signals no smi code inlined.
Condition condition = ComputeCompareCondition(op);
EmitBranch(true_block, false_block, condition);
}
static InstanceType TestType(HHasInstanceTypeAndBranch* instr) {
InstanceType from = instr->from();
InstanceType to = instr->to();
......@@ -2168,26 +2230,6 @@ void LCodeGen::DoDeferredLInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr,
}
static Condition ComputeCompareCondition(Token::Value op) {
switch (op) {
case Token::EQ_STRICT:
case Token::EQ:
return eq;
case Token::LT:
return lt;
case Token::GT:
return gt;
case Token::LTE:
return le;
case Token::GTE:
return ge;
default:
UNREACHABLE();
return kNoCondition;
}
}
void LCodeGen::DoCmpT(LCmpT* instr) {
Token::Value op = instr->op();
......
......@@ -285,6 +285,14 @@ class LCodeGen BASE_EMBEDDED {
Label* is_not_object,
Label* is_object);
// Emits optimized code for %_IsString(x). Preserves input register.
// Returns the condition on which a final split to
// true and false label should be made, to optimize fallthrough.
Condition EmitIsString(Register input,
Register temp1,
Label* is_not_object,
Label* is_object);
// Emits optimized code for %_IsConstructCall().
// Caller should branch on equal condition.
void EmitIsConstructCall(Register temp1, Register temp2);
......
......@@ -695,6 +695,10 @@ void CaseClause::RecordTypeFeedback(TypeFeedbackOracle* oracle) {
TypeInfo info = oracle->SwitchType(this);
if (info.IsSmi()) {
compare_type_ = SMI_ONLY;
} else if (info.IsSymbol()) {
compare_type_ = SYMBOL_ONLY;
} else if (info.IsNonSymbol()) {
compare_type_ = STRING_ONLY;
} else if (info.IsNonPrimitive()) {
compare_type_ = OBJECT_ONLY;
} else {
......
......@@ -684,6 +684,8 @@ class CaseClause: public ZoneObject {
// Type feedback information.
void RecordTypeFeedback(TypeFeedbackOracle* oracle);
bool IsSmiCompare() { return compare_type_ == SMI_ONLY; }
bool IsSymbolCompare() { return compare_type_ == SYMBOL_ONLY; }
bool IsStringCompare() { return compare_type_ == STRING_ONLY; }
bool IsObjectCompare() { return compare_type_ == OBJECT_ONLY; }
private:
......@@ -691,7 +693,13 @@ class CaseClause: public ZoneObject {
Label body_target_;
ZoneList<Statement*>* statements_;
int position_;
enum CompareTypeFeedback { NONE, SMI_ONLY, OBJECT_ONLY };
enum CompareTypeFeedback {
NONE,
SMI_ONLY,
SYMBOL_ONLY,
STRING_ONLY,
OBJECT_ONLY
};
CompareTypeFeedback compare_type_;
int compare_id_;
int entry_id_;
......
......@@ -1325,6 +1325,13 @@ void HCompareGeneric::PrintDataTo(StringStream* stream) {
}
void HStringCompareAndBranch::PrintDataTo(StringStream* stream) {
stream->Add(Token::Name(token()));
stream->Add(" ");
HControlInstruction::PrintDataTo(stream);
}
void HCompareIDAndBranch::PrintDataTo(StringStream* stream) {
stream->Add(Token::Name(token()));
stream->Add(" ");
......
......@@ -118,8 +118,10 @@ class LChunkBuilder;
V(IsConstructCallAndBranch) \
V(IsNilAndBranch) \
V(IsObjectAndBranch) \
V(IsStringAndBranch) \
V(IsSmiAndBranch) \
V(IsUndetectableAndBranch) \
V(StringCompareAndBranch) \
V(JSArrayLength) \
V(LeaveInlined) \
V(LoadContextSlot) \
......@@ -2715,6 +2717,18 @@ class HIsObjectAndBranch: public HUnaryControlInstruction {
DECLARE_CONCRETE_INSTRUCTION(IsObjectAndBranch)
};
class HIsStringAndBranch: public HUnaryControlInstruction {
public:
explicit HIsStringAndBranch(HValue* value)
: HUnaryControlInstruction(value, NULL, NULL) { }
virtual Representation RequiredInputRepresentation(int index) {
return Representation::Tagged();
}
DECLARE_CONCRETE_INSTRUCTION(IsStringAndBranch)
};
class HIsSmiAndBranch: public HUnaryControlInstruction {
public:
......@@ -2745,6 +2759,42 @@ class HIsUndetectableAndBranch: public HUnaryControlInstruction {
};
class HStringCompareAndBranch: public HTemplateControlInstruction<2, 3> {
public:
HStringCompareAndBranch(HValue* context,
HValue* left,
HValue* right,
Token::Value token)
: token_(token) {
ASSERT(Token::IsCompareOp(token));
SetOperandAt(0, context);
SetOperandAt(1, left);
SetOperandAt(2, right);
set_representation(Representation::Tagged());
}
HValue* context() { return OperandAt(0); }
HValue* left() { return OperandAt(1); }
HValue* right() { return OperandAt(2); }
Token::Value token() const { return token_; }
virtual void PrintDataTo(StringStream* stream);
virtual Representation RequiredInputRepresentation(int index) {
return Representation::Tagged();
}
Representation GetInputRepresentation() const {
return Representation::Tagged();
}
DECLARE_CONCRETE_INSTRUCTION(StringCompareAndBranch)
private:
Token::Value token_;
};
class HIsConstructCallAndBranch: public HTemplateControlInstruction<2, 0> {
public:
virtual Representation RequiredInputRepresentation(int index) {
......
......@@ -734,6 +734,7 @@ void HGraph::Postorder(HBasicBlock* block,
Postorder(it.Current(), visited, order, block);
}
} else {
ASSERT(block->IsFinished());
for (HSuccessorIterator it(block->end()); !it.Done(); it.Advance()) {
Postorder(it.Current(), visited, order, loop_header);
}
......@@ -2708,43 +2709,95 @@ void HGraphBuilder::VisitSwitchStatement(SwitchStatement* stmt) {
return Bailout("SwitchStatement: too many clauses");
}
HValue* context = environment()->LookupContext();
CHECK_ALIVE(VisitForValue(stmt->tag()));
AddSimulate(stmt->EntryId());
HValue* tag_value = Pop();
HBasicBlock* first_test_block = current_block();
// 1. Build all the tests, with dangling true branches. Unconditionally
// deoptimize if we encounter a non-smi comparison.
SwitchType switch_type = UNKNOWN_SWITCH;
// 1. Extract clause type
for (int i = 0; i < clause_count; ++i) {
CaseClause* clause = clauses->at(i);
if (clause->is_default()) continue;
if (!clause->label()->IsSmiLiteral()) {
return Bailout("SwitchStatement: non-literal switch label");
if (switch_type == UNKNOWN_SWITCH) {
if (clause->label()->IsSmiLiteral()) {
switch_type = SMI_SWITCH;
} else if (clause->label()->IsStringLiteral()) {
switch_type = STRING_SWITCH;
} else {
return Bailout("SwitchStatement: non-literal switch label");
}
} else if ((switch_type == STRING_SWITCH &&
!clause->label()->IsStringLiteral()) ||
(switch_type == SMI_SWITCH &&
!clause->label()->IsSmiLiteral())) {
return Bailout("SwitchStatemnt: mixed label types are not supported");
}
}
// Unconditionally deoptimize on the first non-smi compare.
clause->RecordTypeFeedback(oracle());
if (!clause->IsSmiCompare()) {
// Finish with deoptimize and add uses of enviroment values to
// account for invisible uses.
current_block()->FinishExitWithDeoptimization(HDeoptimize::kUseAll);
set_current_block(NULL);
break;
HUnaryControlInstruction* string_check = NULL;
HBasicBlock* not_string_block = NULL;
// Test switch's tag value if all clauses are string literals
if (switch_type == STRING_SWITCH) {
string_check = new(zone()) HIsStringAndBranch(tag_value);
first_test_block = graph()->CreateBasicBlock();
not_string_block = graph()->CreateBasicBlock();
string_check->SetSuccessorAt(0, first_test_block);
string_check->SetSuccessorAt(1, not_string_block);
current_block()->Finish(string_check);
set_current_block(first_test_block);
}
// 2. Build all the tests, with dangling true branches
for (int i = 0; i < clause_count; ++i) {
CaseClause* clause = clauses->at(i);
if (clause->is_default()) continue;
if (switch_type == SMI_SWITCH) {
clause->RecordTypeFeedback(oracle());
}
// Otherwise generate a compare and branch.
// Generate a compare and branch.
CHECK_ALIVE(VisitForValue(clause->label()));
HValue* label_value = Pop();
HCompareIDAndBranch* compare =
new(zone()) HCompareIDAndBranch(tag_value,
label_value,
Token::EQ_STRICT);
compare->SetInputRepresentation(Representation::Integer32());
HBasicBlock* body_block = graph()->CreateBasicBlock();
HBasicBlock* next_test_block = graph()->CreateBasicBlock();
HBasicBlock* body_block = graph()->CreateBasicBlock();
HControlInstruction* compare;
if (switch_type == SMI_SWITCH) {
if (!clause->IsSmiCompare()) {
// Finish with deoptimize and add uses of enviroment values to
// account for invisible uses.
current_block()->FinishExitWithDeoptimization(HDeoptimize::kUseAll);
set_current_block(NULL);
break;
}
HCompareIDAndBranch* compare_ =
new(zone()) HCompareIDAndBranch(tag_value,
label_value,
Token::EQ_STRICT);
compare_->SetInputRepresentation(Representation::Integer32());
compare = compare_;
} else {
compare = new(zone()) HStringCompareAndBranch(context, tag_value,
label_value,
Token::EQ_STRICT);
}
compare->SetSuccessorAt(0, body_block);
compare->SetSuccessorAt(1, next_test_block);
current_block()->Finish(compare);
set_current_block(next_test_block);
}
......@@ -2752,10 +2805,15 @@ void HGraphBuilder::VisitSwitchStatement(SwitchStatement* stmt) {
// exit. This block is NULL if we deoptimized.
HBasicBlock* last_block = current_block();
// 2. Loop over the clauses and the linked list of tests in lockstep,
if (not_string_block != NULL) {
last_block = CreateJoin(last_block, not_string_block, stmt->ExitId());
}
// 3. Loop over the clauses and the linked list of tests in lockstep,
// translating the clause bodies.
HBasicBlock* curr_test_block = first_test_block;
HBasicBlock* fall_through_block = NULL;
BreakAndContinueInfo break_info(stmt);
{ BreakAndContinueScope push(&break_info, this);
for (int i = 0; i < clause_count; ++i) {
......
......@@ -657,6 +657,7 @@ class FunctionState {
class HGraphBuilder: public AstVisitor {
public:
enum BreakType { BREAK, CONTINUE };
enum SwitchType { UNKNOWN_SWITCH, SMI_SWITCH, STRING_SWITCH };
// A class encapsulating (lazily-allocated) break and continue blocks for
// a breakable statement. Separated from BreakAndContinueScope so that it
......
......@@ -1716,6 +1716,33 @@ void LCodeGen::DoIsObjectAndBranch(LIsObjectAndBranch* instr) {
}
Condition LCodeGen::EmitIsString(Register input,
Register temp1,
Label* is_not_string,
Label* is_string) {
__ JumpIfSmi(input, is_not_string);
Condition cond = masm_->IsObjectStringType(input, temp1, temp1);
return cond;
}
void LCodeGen::DoIsStringAndBranch(LIsStringAndBranch* instr) {
Register reg = ToRegister(instr->InputAt(0));
Register temp = ToRegister(instr->TempAt(0));
int true_block = chunk_->LookupDestination(instr->true_block_id());
int false_block = chunk_->LookupDestination(instr->false_block_id());
Label* true_label = chunk_->GetAssemblyLabel(true_block);
Label* false_label = chunk_->GetAssemblyLabel(false_block);
Condition true_cond = EmitIsString(reg, temp, false_label, true_label);
EmitBranch(true_block, false_block, true_cond);
}
void LCodeGen::DoIsSmiAndBranch(LIsSmiAndBranch* instr) {
Operand input = ToOperand(instr->InputAt(0));
......@@ -1743,6 +1770,41 @@ void LCodeGen::DoIsUndetectableAndBranch(LIsUndetectableAndBranch* instr) {
}
static Condition ComputeCompareCondition(Token::Value op) {
switch (op) {
case Token::EQ_STRICT:
case Token::EQ:
return equal;
case Token::LT:
return less;
case Token::GT:
return greater;
case Token::LTE:
return less_equal;
case Token::GTE:
return greater_equal;
default:
UNREACHABLE();
return no_condition;
}
}
void LCodeGen::DoStringCompareAndBranch(LStringCompareAndBranch* instr) {
Token::Value op = instr->op();
int true_block = chunk_->LookupDestination(instr->true_block_id());
int false_block = chunk_->LookupDestination(instr->false_block_id());
Handle<Code> ic = CompareIC::GetUninitialized(op);
CallCode(ic, RelocInfo::CODE_TARGET, instr);
Condition condition = ComputeCompareCondition(op);
__ test(eax, Operand(eax));
EmitBranch(true_block, false_block, condition);
}
static InstanceType TestType(HHasInstanceTypeAndBranch* instr) {
InstanceType from = instr->from();
InstanceType to = instr->to();
......@@ -2016,26 +2078,6 @@ void LCodeGen::DoDeferredLInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr,
}
static Condition ComputeCompareCondition(Token::Value op) {
switch (op) {
case Token::EQ_STRICT:
case Token::EQ:
return equal;
case Token::LT:
return less;
case Token::GT:
return greater;
case Token::LTE:
return less_equal;
case Token::GTE:
return greater_equal;
default:
UNREACHABLE();
return no_condition;
}
}
void LCodeGen::DoCmpT(LCmpT* instr) {
Token::Value op = instr->op();
......
......@@ -283,6 +283,14 @@ class LCodeGen BASE_EMBEDDED {
Label* is_not_object,
Label* is_object);
// Emits optimized code for %_IsString(x). Preserves input register.
// Returns the condition on which a final split to
// true and false label should be made, to optimize fallthrough.
Condition EmitIsString(Register input,
Register temp1,
Label* is_not_string,
Label* is_string);
// Emits optimized code for %_IsConstructCall().
// Caller should branch on equal condition.
void EmitIsConstructCall(Register temp);
......
......@@ -225,6 +225,13 @@ void LIsObjectAndBranch::PrintDataTo(StringStream* stream) {
}
void LIsStringAndBranch::PrintDataTo(StringStream* stream) {
stream->Add("if is_string(");
InputAt(0)->PrintTo(stream);
stream->Add(") then B%d else B%d", true_block_id(), false_block_id());
}
void LIsSmiAndBranch::PrintDataTo(StringStream* stream) {
stream->Add("if is_smi(");
InputAt(0)->PrintTo(stream);
......@@ -239,6 +246,14 @@ void LIsUndetectableAndBranch::PrintDataTo(StringStream* stream) {
}
void LStringCompareAndBranch::PrintDataTo(StringStream* stream) {
stream->Add("if compare_generic(");
InputAt(1)->PrintTo(stream);
InputAt(2)->PrintTo(stream);
stream->Add(") then B%d else B%d", true_block_id(), false_block_id());
}
void LHasInstanceTypeAndBranch::PrintDataTo(StringStream* stream) {
stream->Add("if has_instance_type(");
InputAt(0)->PrintTo(stream);
......@@ -1498,6 +1513,13 @@ LInstruction* LChunkBuilder::DoIsObjectAndBranch(HIsObjectAndBranch* instr) {
}
LInstruction* LChunkBuilder::DoIsStringAndBranch(HIsStringAndBranch* instr) {
ASSERT(instr->value()->representation().IsTagged());
LOperand* temp = TempRegister();
return new LIsStringAndBranch(UseRegister(instr->value()), temp);
}
LInstruction* LChunkBuilder::DoIsSmiAndBranch(HIsSmiAndBranch* instr) {
ASSERT(instr->value()->representation().IsTagged());
return new(zone()) LIsSmiAndBranch(Use(instr->value()));
......@@ -1512,6 +1534,21 @@ LInstruction* LChunkBuilder::DoIsUndetectableAndBranch(
}
LInstruction* LChunkBuilder::DoStringCompareAndBranch(
HStringCompareAndBranch* instr) {
ASSERT(instr->left()->representation().IsTagged());
ASSERT(instr->right()->representation().IsTagged());
LOperand* context = UseFixed(instr->context(), esi);
LOperand* left = UseFixed(instr->left(), edx);
LOperand* right = UseFixed(instr->right(), eax);
LStringCompareAndBranch* result = new
LStringCompareAndBranch(context, left, right);
return MarkAsCall(result, instr);
}
LInstruction* LChunkBuilder::DoHasInstanceTypeAndBranch(
HHasInstanceTypeAndBranch* instr) {
ASSERT(instr->value()->representation().IsTagged());
......
......@@ -103,8 +103,10 @@ class LCodeGen;
V(IsConstructCallAndBranch) \
V(IsNilAndBranch) \
V(IsObjectAndBranch) \
V(IsStringAndBranch) \
V(IsSmiAndBranch) \
V(IsUndetectableAndBranch) \
V(StringCompareAndBranch) \
V(JSArrayLength) \
V(Label) \
V(LazyBailout) \
......@@ -643,6 +645,19 @@ class LIsObjectAndBranch: public LControlInstruction<1, 1> {
};
class LIsStringAndBranch: public LControlInstruction<1, 1> {
public:
LIsStringAndBranch(LOperand* value, LOperand* temp) {
inputs_[0] = value;
temps_[0] = temp;
}
DECLARE_CONCRETE_INSTRUCTION(IsStringAndBranch, "is-string-and-branch")
virtual void PrintDataTo(StringStream* stream);
};
class LIsSmiAndBranch: public LControlInstruction<1, 0> {
public:
explicit LIsSmiAndBranch(LOperand* value) {
......@@ -670,6 +685,24 @@ class LIsUndetectableAndBranch: public LControlInstruction<1, 1> {
};
class LStringCompareAndBranch: public LControlInstruction<3, 0> {
public:
LStringCompareAndBranch(LOperand* context, LOperand* left, LOperand* right) {
inputs_[0] = context;
inputs_[1] = left;
inputs_[2] = right;
}
DECLARE_CONCRETE_INSTRUCTION(StringCompareAndBranch,
"compare-generic-and-branch")
DECLARE_HYDROGEN_ACCESSOR(StringCompareAndBranch)
virtual void PrintDataTo(StringStream* stream);
Token::Value op() const { return hydrogen()->token(); }
};
class LHasInstanceTypeAndBranch: public LControlInstruction<1, 1> {
public:
LHasInstanceTypeAndBranch(LOperand* value, LOperand* temp) {
......
......@@ -1728,6 +1728,36 @@ void LCodeGen::DoIsObjectAndBranch(LIsObjectAndBranch* instr) {
}
Condition LCodeGen::EmitIsString(Register input,
Register temp1,
Label* is_not_string,
Label* is_string) {
__ JumpIfSmi(input, is_not_string);
__ GetObjectType(input, temp1, temp1);
__ Branch(is_not_string, ge, temp1, Operand(FIRST_NONSTRING_TYPE));
return lt;
}
void LCodeGen::DoIsStringAndBranch(LIsStringAndBranch* instr) {
Register reg = ToRegister(instr->InputAt(0));
Register temp1 = ToRegister(instr->TempAt(0));
Register temp2 = scratch0();
int true_block = chunk_->LookupDestination(instr->true_block_id());
int false_block = chunk_->LookupDestination(instr->false_block_id());
Label* true_label = chunk_->GetAssemblyLabel(true_block);
Label* false_label = chunk_->GetAssemblyLabel(false_block);
Condition true_cond =
EmitIsString(reg, temp1, false_label, true_label);
EmitBranch(true_block, false_block, true_cond, temp2,
Operand(LAST_NONCALLABLE_SPEC_OBJECT_TYPE));
}
void LCodeGen::DoIsSmiAndBranch(LIsSmiAndBranch* instr) {
int true_block = chunk_->LookupDestination(instr->true_block_id());
int false_block = chunk_->LookupDestination(instr->false_block_id());
......@@ -1753,6 +1783,42 @@ void LCodeGen::DoIsUndetectableAndBranch(LIsUndetectableAndBranch* instr) {
}
static Condition ComputeCompareCondition(Token::Value op) {
switch (op) {
case Token::EQ_STRICT:
case Token::EQ:
return eq;
case Token::LT:
return lt;
case Token::GT:
return gt;
case Token::LTE:
return le;
case Token::GTE:
return ge;
default:
UNREACHABLE();
return kNoCondition;
}
}
void LCodeGen::DoStringCompareAndBranch(LStringCompareAndBranch* instr) {
Token::Value op = instr->op();
int true_block = chunk_->LookupDestination(instr->true_block_id());
int false_block = chunk_->LookupDestination(instr->false_block_id());
Handle<Code> ic = CompareIC::GetUninitialized(op);
CallCode(ic, RelocInfo::CODE_TARGET, instr);
// On MIPS there is no need for a "no inlined smi code" marker (nop).
Condition condition = ComputeCompareCondition(op);
EmitBranch(true_block, false_block, condition, at,
Operand(LAST_NONCALLABLE_SPEC_OBJECT_TYPE));
}
static InstanceType TestType(HHasInstanceTypeAndBranch* instr) {
InstanceType from = instr->from();
InstanceType to = instr->to();
......@@ -2050,26 +2116,6 @@ void LCodeGen::DoDeferredLInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr,
}
static Condition ComputeCompareCondition(Token::Value op) {
switch (op) {
case Token::EQ_STRICT:
case Token::EQ:
return eq;
case Token::LT:
return lt;
case Token::GT:
return gt;
case Token::LTE:
return le;
case Token::GTE:
return ge;
default:
UNREACHABLE();
return kNoCondition;
}
}
void LCodeGen::DoCmpT(LCmpT* instr) {
Token::Value op = instr->op();
......
......@@ -303,6 +303,14 @@ class LCodeGen BASE_EMBEDDED {
Label* is_not_object,
Label* is_object);
// Emits optimized code for %_IsString(x). Preserves input register.
// Returns the condition on which a final split to
// true and false label should be made, to optimize fallthrough.
Condition EmitIsString(Register input,
Register temp1,
Label* is_not_object,
Label* is_object);
// Emits optimized code for %_IsConstructCall().
// Caller should branch on equal condition.
void EmitIsConstructCall(Register temp1, Register temp2);
......
......@@ -228,6 +228,13 @@ void LIsObjectAndBranch::PrintDataTo(StringStream* stream) {
}
void LIsStringAndBranch::PrintDataTo(StringStream* stream) {
stream->Add("if is_string(");
InputAt(0)->PrintTo(stream);
stream->Add(") then B%d else B%d", true_block_id(), false_block_id());
}
void LIsSmiAndBranch::PrintDataTo(StringStream* stream) {
stream->Add("if is_smi(");
InputAt(0)->PrintTo(stream);
......@@ -242,6 +249,14 @@ void LIsUndetectableAndBranch::PrintDataTo(StringStream* stream) {
}
void LStringCompareAndBranch::PrintDataTo(StringStream* stream) {
stream->Add("if compare_generic(");
InputAt(0)->PrintTo(stream);
InputAt(1)->PrintTo(stream);
stream->Add(") then B%d else B%d", true_block_id(), false_block_id());
}
void LHasInstanceTypeAndBranch::PrintDataTo(StringStream* stream) {
stream->Add("if has_instance_type(");
InputAt(0)->PrintTo(stream);
......@@ -1400,7 +1415,7 @@ LInstruction* LChunkBuilder::DoCompareGeneric(HCompareGeneric* instr) {
LOperand* left = UseFixed(instr->left(), a1);
LOperand* right = UseFixed(instr->right(), a0);
LCmpT* result = new LCmpT(left, right);
return MarkAsCall(DefineFixed(result, v0), instr);
return AssignEnvironment(MarkAsCall(DefineFixed(result, v0), instr));
}
......@@ -1451,6 +1466,13 @@ LInstruction* LChunkBuilder::DoIsObjectAndBranch(HIsObjectAndBranch* instr) {
}
LInstruction* LChunkBuilder::DoIsStringAndBranch(HIsStringAndBranch* instr) {
ASSERT(instr->value()->representation().IsTagged());
LOperand* temp = TempRegister();
return new LIsStringAndBranch(UseRegisterAtStart(instr->value()), temp);
}
LInstruction* LChunkBuilder::DoIsSmiAndBranch(HIsSmiAndBranch* instr) {
ASSERT(instr->value()->representation().IsTagged());
return new LIsSmiAndBranch(Use(instr->value()));
......@@ -1465,6 +1487,18 @@ LInstruction* LChunkBuilder::DoIsUndetectableAndBranch(
}
LInstruction* LChunkBuilder::DoStringCompareAndBranch(
HStringCompareAndBranch* instr) {
Representation r = instr->GetInputRepresentation();
ASSERT(instr->left()->representation().IsTagged());
ASSERT(instr->right()->representation().IsTagged());
LOperand* left = UseFixed(instr->left(), a1);
LOperand* right = UseFixed(instr->right(), a0);
LStringCompareAndBranch* result = new LStringCompareAndBranch(left, right);
return MarkAsCall(result, instr);
}
LInstruction* LChunkBuilder::DoHasInstanceTypeAndBranch(
HHasInstanceTypeAndBranch* instr) {
ASSERT(instr->value()->representation().IsTagged());
......
......@@ -109,8 +109,10 @@ class LCodeGen;
V(IsConstructCallAndBranch) \
V(IsNilAndBranch) \
V(IsObjectAndBranch) \
V(IsStringAndBranch) \
V(IsSmiAndBranch) \
V(IsUndetectableAndBranch) \
V(StringCompareAndBranch) \
V(JSArrayLength) \
V(Label) \
V(LazyBailout) \
......@@ -658,6 +660,20 @@ class LIsObjectAndBranch: public LControlInstruction<1, 1> {
};
class LIsStringAndBranch: public LControlInstruction<1, 1> {
public:
LIsStringAndBranch(LOperand* value, LOperand* temp) {
inputs_[0] = value;
temps_[0] = temp;
}
DECLARE_CONCRETE_INSTRUCTION(IsStringAndBranch, "is-string-and-branch")
DECLARE_HYDROGEN_ACCESSOR(IsStringAndBranch)
virtual void PrintDataTo(StringStream* stream);
};
class LIsSmiAndBranch: public LControlInstruction<1, 0> {
public:
explicit LIsSmiAndBranch(LOperand* value) {
......@@ -686,6 +702,23 @@ class LIsUndetectableAndBranch: public LControlInstruction<1, 1> {
};
class LStringCompareAndBranch: public LControlInstruction<2, 0> {
public:
LStringCompareAndBranch(LOperand* left, LOperand* right) {
inputs_[0] = left;
inputs_[1] = right;
}
DECLARE_CONCRETE_INSTRUCTION(StringCompareAndBranch,
"compare-generic-and-branch")
DECLARE_HYDROGEN_ACCESSOR(StringCompareAndBranch)
Token::Value op() const { return hydrogen()->token(); }
virtual void PrintDataTo(StringStream* stream);
};
class LHasInstanceTypeAndBranch: public LControlInstruction<1, 0> {
public:
explicit LHasInstanceTypeAndBranch(LOperand* value) {
......
......@@ -360,6 +360,10 @@ TypeInfo TypeFeedbackOracle::SwitchType(CaseClause* clause) {
return unknown;
case CompareIC::SMIS:
return TypeInfo::Smi();
case CompareIC::STRINGS:
return TypeInfo::String();
case CompareIC::SYMBOLS:
return TypeInfo::Symbol();
case CompareIC::HEAP_NUMBERS:
return TypeInfo::Number();
case CompareIC::OBJECTS:
......
......@@ -64,6 +64,8 @@ class TypeInfo {
static TypeInfo Integer32() { return TypeInfo(kInteger32); }
// We know it's a Smi.
static TypeInfo Smi() { return TypeInfo(kSmi); }
// We know it's a Symbol.
static TypeInfo Symbol() { return TypeInfo(kSymbol); }
// We know it's a heap number.
static TypeInfo Double() { return TypeInfo(kDouble); }
// We know it's a string.
......@@ -137,6 +139,16 @@ class TypeInfo {
return ((type_ & kSmi) == kSmi);
}
inline bool IsSymbol() {
ASSERT(type_ != kUninitialized);
return ((type_ & kSymbol) == kSymbol);
}
inline bool IsNonSymbol() {
ASSERT(type_ != kUninitialized);
return ((type_ & kSymbol) == kString);
}
inline bool IsInteger32() {
ASSERT(type_ != kUninitialized);
return ((type_ & kInteger32) == kInteger32);
......@@ -168,6 +180,7 @@ class TypeInfo {
case kNumber: return "Number";
case kInteger32: return "Integer32";
case kSmi: return "Smi";
case kSymbol: return "Symbol";
case kDouble: return "Double";
case kString: return "String";
case kNonPrimitive: return "Object";
......@@ -186,6 +199,7 @@ class TypeInfo {
kSmi = 0x17, // 0010111
kDouble = 0x19, // 0011001
kString = 0x30, // 0110000
kSymbol = 0x32, // 0110010
kNonPrimitive = 0x40, // 1000000
kUninitialized = 0x7f // 1111111
};
......
......@@ -1680,6 +1680,31 @@ void LCodeGen::DoIsObjectAndBranch(LIsObjectAndBranch* instr) {
}
Condition LCodeGen::EmitIsString(Register input,
Label* is_not_string,
Label* is_string) {
__ JumpIfSmi(input, is_not_string);
__ CmpObjectType(input, FIRST_NONSTRING_TYPE, rcx);
return below;
}
void LCodeGen::DoIsStringAndBranch(LIsStringAndBranch* instr) {
Register reg = ToRegister(instr->InputAt(0));
int true_block = chunk_->LookupDestination(instr->true_block_id());
int false_block = chunk_->LookupDestination(instr->false_block_id());
Label* true_label = chunk_->GetAssemblyLabel(true_block);
Label* false_label = chunk_->GetAssemblyLabel(false_block);
Condition true_cond = EmitIsString(reg, false_label, true_label);
EmitBranch(true_block, false_block, true_cond);
}
void LCodeGen::DoIsSmiAndBranch(LIsSmiAndBranch* instr) {
int true_block = chunk_->LookupDestination(instr->true_block_id());
int false_block = chunk_->LookupDestination(instr->false_block_id());
......@@ -1711,6 +1736,21 @@ void LCodeGen::DoIsUndetectableAndBranch(LIsUndetectableAndBranch* instr) {
}
void LCodeGen::DoStringCompareAndBranch(LStringCompareAndBranch* instr) {
Token::Value op = instr->op();
int true_block = chunk_->LookupDestination(instr->true_block_id());
int false_block = chunk_->LookupDestination(instr->false_block_id());
Handle<Code> ic = CompareIC::GetUninitialized(op);
CallCode(ic, RelocInfo::CODE_TARGET, instr);
Condition condition = TokenToCondition(op, false);
__ testq(rax, rax);
EmitBranch(true_block, false_block, condition);
}
static InstanceType TestType(HHasInstanceTypeAndBranch* instr) {
InstanceType from = instr->from();
InstanceType to = instr->to();
......
......@@ -271,6 +271,13 @@ class LCodeGen BASE_EMBEDDED {
Label* is_not_object,
Label* is_object);
// Emits optimized code for %_IsString(x). Preserves input register.
// Returns the condition on which a final split to
// true and false label should be made, to optimize fallthrough.
Condition EmitIsString(Register input,
Label* is_not_object,
Label* is_object);
// Emits optimized code for %_IsConstructCall().
// Caller should branch on equal condition.
void EmitIsConstructCall(Register temp);
......
......@@ -230,6 +230,13 @@ void LIsObjectAndBranch::PrintDataTo(StringStream* stream) {
}
void LIsStringAndBranch::PrintDataTo(StringStream* stream) {
stream->Add("if is_string(");
InputAt(0)->PrintTo(stream);
stream->Add(") then B%d else B%d", true_block_id(), false_block_id());
}
void LIsSmiAndBranch::PrintDataTo(StringStream* stream) {
stream->Add("if is_smi(");
InputAt(0)->PrintTo(stream);
......@@ -244,6 +251,14 @@ void LIsUndetectableAndBranch::PrintDataTo(StringStream* stream) {
}
void LStringCompareAndBranch::PrintDataTo(StringStream* stream) {
stream->Add("if compare_generic(");
InputAt(0)->PrintTo(stream);
InputAt(1)->PrintTo(stream);
stream->Add(") then B%d else B%d", true_block_id(), false_block_id());
}
void LHasInstanceTypeAndBranch::PrintDataTo(StringStream* stream) {
stream->Add("if has_instance_type(");
InputAt(0)->PrintTo(stream);
......@@ -1392,7 +1407,7 @@ LInstruction* LChunkBuilder::DoCompareGeneric(HCompareGeneric* instr) {
LOperand* left = UseFixed(instr->left(), rdx);
LOperand* right = UseFixed(instr->right(), rax);
LCmpT* result = new LCmpT(left, right);
return MarkAsCall(DefineFixed(result, rax), instr);
return AssignEnvironment(MarkAsCall(DefineFixed(result, rax), instr));
}
......@@ -1450,6 +1465,12 @@ LInstruction* LChunkBuilder::DoIsObjectAndBranch(HIsObjectAndBranch* instr) {
}
LInstruction* LChunkBuilder::DoIsStringAndBranch(HIsStringAndBranch* instr) {
ASSERT(instr->value()->representation().IsTagged());
return new LIsStringAndBranch(UseRegisterAtStart(instr->value()));
}
LInstruction* LChunkBuilder::DoIsSmiAndBranch(HIsSmiAndBranch* instr) {
ASSERT(instr->value()->representation().IsTagged());
return new LIsSmiAndBranch(Use(instr->value()));
......@@ -1464,6 +1485,19 @@ LInstruction* LChunkBuilder::DoIsUndetectableAndBranch(
}
LInstruction* LChunkBuilder::DoStringCompareAndBranch(
HStringCompareAndBranch* instr) {
ASSERT(instr->left()->representation().IsTagged());
ASSERT(instr->right()->representation().IsTagged());
LOperand* left = UseFixed(instr->left(), rdx);
LOperand* right = UseFixed(instr->right(), rax);
LStringCompareAndBranch* result = new LStringCompareAndBranch(left, right);
return MarkAsCall(result, instr);
}
LInstruction* LChunkBuilder::DoHasInstanceTypeAndBranch(
HHasInstanceTypeAndBranch* instr) {
ASSERT(instr->value()->representation().IsTagged());
......
......@@ -109,8 +109,10 @@ class LCodeGen;
V(IsConstructCallAndBranch) \
V(IsNilAndBranch) \
V(IsObjectAndBranch) \
V(IsStringAndBranch) \
V(IsSmiAndBranch) \
V(IsUndetectableAndBranch) \
V(StringCompareAndBranch) \
V(JSArrayLength) \
V(Label) \
V(LazyBailout) \
......@@ -640,6 +642,19 @@ class LIsObjectAndBranch: public LControlInstruction<1, 0> {
};
class LIsStringAndBranch: public LControlInstruction<1, 0> {
public:
explicit LIsStringAndBranch(LOperand* value) {
inputs_[0] = value;
}
DECLARE_CONCRETE_INSTRUCTION(IsStringAndBranch, "is-string-and-branch")
DECLARE_HYDROGEN_ACCESSOR(IsStringAndBranch)
virtual void PrintDataTo(StringStream* stream);
};
class LIsSmiAndBranch: public LControlInstruction<1, 0> {
public:
explicit LIsSmiAndBranch(LOperand* value) {
......@@ -668,6 +683,23 @@ class LIsUndetectableAndBranch: public LControlInstruction<1, 1> {
};
class LStringCompareAndBranch: public LControlInstruction<2, 0> {
public:
explicit LStringCompareAndBranch(LOperand* left, LOperand* right) {
inputs_[0] = left;
inputs_[1] = right;
}
DECLARE_CONCRETE_INSTRUCTION(StringCompareAndBranch,
"compare-generic-and-branch")
DECLARE_HYDROGEN_ACCESSOR(StringCompareAndBranch)
virtual void PrintDataTo(StringStream* stream);
Token::Value op() const { return hydrogen()->token(); }
};
class LHasInstanceTypeAndBranch: public LControlInstruction<1, 0> {
public:
explicit LHasInstanceTypeAndBranch(LOperand* value) {
......
......@@ -126,6 +126,42 @@ assertEquals(3, f4(1), "fallthrough-switch.1");
assertEquals(3, f4(2), "fallthrough-switch.2");
assertEquals(5, f4(3), "fallthrough-switch.3");
function f4_string(tag, x) {
switch(tag) {
case 'zero':
x++;
case 'two':
x++;
}
return x;
}
// Symbols
assertEquals(2, f4_string('zero', 0), "fallthrough-string-switch.0");
assertEquals(1, f4_string('one', 1), "fallthrough-string-switch.1");
assertEquals(3, f4_string('two', 2), "fallthrough-string-switch.2");
// Strings
assertEquals(2, f4_string('_zero'.slice(1), 0), "fallthrough-string-switch.3");
assertEquals(1, f4_string('_one'.slice(1), 1), "fallthrough-string-switch.4");
assertEquals(3, f4_string('_two'.slice(1), 2), "fallthrough-string-switch.5");
// Oddball
assertEquals(3, f4_string(null, 3), "fallthrough-string-switch.6");
// Test for regression
function regress_string(value) {
var json = 1;
switch (typeof value) {
case 'object':
break;
default:
}
return json;
};
assertEquals(1, regress_string('object'), 'regression-string');
function f5(x) {
switch(x) {
......
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