Landing: [hydrogen] optimize switch with string clauses. Patch by Fedor Indutny <fedor.indutny@gmail.com>.
Original code review: http://codereview.chromium.org/8373029/ Review URL: http://codereview.chromium.org/8589019 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@10019 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
3c5b91029b
commit
8fbf1d5017
@ -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 string_compare(");
|
||||
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);
|
||||
@ -1452,6 +1467,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()));
|
||||
@ -1466,6 +1488,17 @@ 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,
|
||||
"string-compare-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) {
|
||||
|
@ -1812,6 +1812,31 @@ void LCodeGen::DoIsObjectAndBranch(LIsObjectAndBranch* instr) {
|
||||
}
|
||||
|
||||
|
||||
Condition LCodeGen::EmitIsString(Register input,
|
||||
Register temp1,
|
||||
Label* is_not_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* false_label = chunk_->GetAssemblyLabel(false_block);
|
||||
|
||||
Condition true_cond =
|
||||
EmitIsString(reg, temp1, false_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());
|
||||
@ -1837,6 +1862,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();
|
||||
@ -2125,26 +2185,6 @@ void LCodeGen::DoDeferredInstanceOfKnownGlobal(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();
|
||||
|
||||
|
@ -284,6 +284,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,
|
||||
Register temp1,
|
||||
Label* is_not_string);
|
||||
|
||||
// Emits optimized code for %_IsConstructCall().
|
||||
// Caller should branch on equal condition.
|
||||
void EmitIsConstructCall(Register temp1, Register temp2);
|
||||
|
@ -710,6 +710,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 {
|
||||
|
10
src/ast.h
10
src/ast.h
@ -726,6 +726,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:
|
||||
@ -733,7 +735,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) \
|
||||
@ -2716,6 +2718,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:
|
||||
@ -2746,6 +2760,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) {
|
||||
|
102
src/hydrogen.cc
102
src/hydrogen.cc
@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// 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
|
||||
|
@ -1683,6 +1683,31 @@ void LCodeGen::DoIsObjectAndBranch(LIsObjectAndBranch* instr) {
|
||||
}
|
||||
|
||||
|
||||
Condition LCodeGen::EmitIsString(Register input,
|
||||
Register temp1,
|
||||
Label* is_not_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* false_label = chunk_->GetAssemblyLabel(false_block);
|
||||
|
||||
Condition true_cond = EmitIsString(reg, temp, false_label);
|
||||
|
||||
EmitBranch(true_block, false_block, true_cond);
|
||||
}
|
||||
|
||||
|
||||
void LCodeGen::DoIsSmiAndBranch(LIsSmiAndBranch* instr) {
|
||||
Operand input = ToOperand(instr->InputAt(0));
|
||||
|
||||
@ -1710,6 +1735,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();
|
||||
@ -1987,26 +2047,6 @@ void LCodeGen::DoDeferredInstanceOfKnownGlobal(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();
|
||||
|
||||
|
@ -280,6 +280,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,
|
||||
Register temp1,
|
||||
Label* is_not_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 string_compare(");
|
||||
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);
|
||||
@ -1499,6 +1514,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()));
|
||||
@ -1513,6 +1535,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) \
|
||||
@ -633,6 +635,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) {
|
||||
@ -660,6 +675,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,
|
||||
"string-compare-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) {
|
||||
|
@ -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
|
||||
};
|
||||
|
@ -1614,6 +1614,30 @@ void LCodeGen::DoIsObjectAndBranch(LIsObjectAndBranch* instr) {
|
||||
}
|
||||
|
||||
|
||||
Condition LCodeGen::EmitIsString(Register input,
|
||||
Register temp1,
|
||||
Label* is_not_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* false_label = chunk_->GetAssemblyLabel(false_block);
|
||||
|
||||
Condition true_cond = EmitIsString(reg, temp, false_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());
|
||||
@ -1645,6 +1669,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();
|
||||
|
@ -269,6 +269,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,
|
||||
Register temp1,
|
||||
Label* is_not_string);
|
||||
|
||||
// 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 string_compare(");
|
||||
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,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,20 @@ class LIsObjectAndBranch: public LControlInstruction<1, 0> {
|
||||
};
|
||||
|
||||
|
||||
class LIsStringAndBranch: public LControlInstruction<1, 1> {
|
||||
public:
|
||||
explicit 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) {
|
||||
@ -668,6 +684,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,
|
||||
"string-compare-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) {
|
||||
|
@ -25,6 +25,8 @@
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// Flags: --allow-natives-syntax
|
||||
|
||||
function f0() {
|
||||
switch (0) {
|
||||
// switch deliberately left empty
|
||||
@ -126,6 +128,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) {
|
||||
@ -287,3 +325,138 @@ var verylong_size = 1000;
|
||||
var verylong = makeVeryLong(verylong_size);
|
||||
|
||||
assertEquals(verylong_size * 2 + 1, verylong());
|
||||
|
||||
//
|
||||
// Test suite below aims to cover all possible combinations of following:
|
||||
//
|
||||
// clauses | tags | type feedback | optimization
|
||||
// =========================================================
|
||||
// strings | symbol | all | on
|
||||
// smis | string | target | off
|
||||
// mixed | oddball | non-target |
|
||||
// | smis | none |
|
||||
// | heapnum | |
|
||||
// =========================================================
|
||||
|
||||
// Function-with-switch generator
|
||||
var test_id = 0,
|
||||
clause_values = {
|
||||
string: ['abc', 'def', 'ghi', 'jkl'],
|
||||
smi: [1, 2, 3, 4],
|
||||
mixed: ['abc', 1, 'def', 2, 'ghi', 3, 'jkl', 4]
|
||||
};
|
||||
|
||||
function switch_gen(clause_type, feedback, optimize) {
|
||||
var values = clause_values[clause_type];
|
||||
|
||||
function opt(fn) {
|
||||
if (feedback === 'all') {
|
||||
values.forEach(fn);
|
||||
} else if (Array.isArray(feedback)) {
|
||||
// Non-target
|
||||
values.filter(function(v) {
|
||||
return feedback.indexOf(v) === -1;
|
||||
}).forEach(fn);
|
||||
} else if (feedback !== undefined) {
|
||||
// Target
|
||||
fn(feedback);
|
||||
} else {
|
||||
// None
|
||||
}
|
||||
|
||||
if (optimize) %OptimizeFunctionOnNextCall(fn);
|
||||
|
||||
return fn;
|
||||
};
|
||||
|
||||
return opt(new Function(
|
||||
'tag',
|
||||
'"' + (test_id++) + '";' +
|
||||
'switch(tag) {' +
|
||||
values.map(function(value) {
|
||||
return 'case ' + JSON.stringify(value) + ': return' +
|
||||
JSON.stringify('ok-' + value);
|
||||
}).join(';') +
|
||||
'}'
|
||||
));
|
||||
};
|
||||
|
||||
function test_switch(clause_type, test_type, feedback, optimize) {
|
||||
var pairs = [],
|
||||
fn = switch_gen(clause_type, feedback, optimize);
|
||||
|
||||
if (Array.isArray(test_type)) {
|
||||
pairs = test_type.map(function(v) {
|
||||
return {
|
||||
value: v,
|
||||
expected: 'ok-' + v
|
||||
};
|
||||
});
|
||||
} else if (test_type === 'symbols') {
|
||||
pairs = clause_values.string.map(function(v) {
|
||||
return {
|
||||
value: v,
|
||||
expected: clause_type !== 'smi' ? 'ok-' + v : undefined
|
||||
};
|
||||
});
|
||||
} else if (test_type === 'strings') {
|
||||
pairs = clause_values.string.map(function(v) {
|
||||
return {
|
||||
value: ('%%' + v).slice(2),
|
||||
expected: clause_type !== 'smi' ? 'ok-' + v : undefined
|
||||
};
|
||||
});
|
||||
} else if (test_type === 'oddball') {
|
||||
pairs = [
|
||||
{ value: null, expected: undefined },
|
||||
{ value: NaN, expected: undefined },
|
||||
{ value: undefined, expected: undefined }
|
||||
];
|
||||
} else if (test_type === 'smi') {
|
||||
pairs = clause_values.smi.map(function(v) {
|
||||
return {
|
||||
value: v,
|
||||
expected: clause_type !== 'string' ? 'ok-' + v : undefined
|
||||
};
|
||||
});
|
||||
} else if (test_type === 'heapnum') {
|
||||
pairs = clause_values.smi.map(function(v) {
|
||||
return {
|
||||
value: ((v * 17)/16) - ((v*17)%16/16),
|
||||
expected: clause_type !== 'string' ? 'ok-' + v : undefined
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
pairs.forEach(function(pair) {
|
||||
assertEquals(fn(pair.value), pair.expected);
|
||||
});
|
||||
};
|
||||
|
||||
// test_switch(clause_type, test_type, feedback, optimize);
|
||||
|
||||
function test_switches(opt) {
|
||||
var test_types = ['symbols', 'strings', 'oddball', 'smi', 'heapnum'];
|
||||
|
||||
function test(clause_type) {
|
||||
var values = clause_values[clause_type];
|
||||
|
||||
test_types.forEach(function(test_type) {
|
||||
test_switch(clause_type, test_type, 'all', opt);
|
||||
test_switch(clause_type, test_type, 'none', opt);
|
||||
|
||||
// Targeting specific clause feedback
|
||||
values.forEach(function(value) {
|
||||
test_switch(clause_type, test_type, [value], value, opt);
|
||||
test_switch(clause_type, test_type, value, value, opt);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
test('string');
|
||||
test('smi');
|
||||
test('mixed');
|
||||
};
|
||||
|
||||
test_switches(false);
|
||||
test_switches(true);
|
||||
|
Loading…
Reference in New Issue
Block a user