Added fast-case switch to ARM.
Refactored to reuse code between ia32 and ARM. Added tests for switch. git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@424 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
4269973acd
commit
17dc503149
@ -301,6 +301,15 @@ class ArmCodeGenerator: public CodeGenerator {
|
||||
NODE_LIST(DEF_VISIT)
|
||||
#undef DEF_VISIT
|
||||
|
||||
// Fast-case switch
|
||||
static const int kFastCaseSwitchMaxOverheadFactor = 10;
|
||||
static const int kFastCaseSwitchMinCaseCount = 5;
|
||||
virtual int FastCaseSwitchMaxOverheadFactor();
|
||||
virtual int FastCaseSwitchMinCaseCount();
|
||||
virtual void GenerateFastCaseSwitchJumpTable(
|
||||
SwitchStatement* node, int min_index, int range, Label *fail_label,
|
||||
SmartPointer<Label*> &case_targets, SmartPointer<Label> &case_labels);
|
||||
|
||||
void RecordStatementPosition(Node* node);
|
||||
|
||||
// Activation frames
|
||||
@ -1617,6 +1626,48 @@ void ArmCodeGenerator::VisitWithExitStatement(WithExitStatement* node) {
|
||||
}
|
||||
|
||||
|
||||
int ArmCodeGenerator::FastCaseSwitchMaxOverheadFactor() {
|
||||
return kFastCaseSwitchMaxOverheadFactor;
|
||||
}
|
||||
|
||||
int ArmCodeGenerator::FastCaseSwitchMinCaseCount() {
|
||||
return kFastCaseSwitchMinCaseCount;
|
||||
}
|
||||
|
||||
|
||||
void ArmCodeGenerator::GenerateFastCaseSwitchJumpTable(
|
||||
SwitchStatement* node, int min_index, int range, Label *fail_label,
|
||||
SmartPointer<Label*> &case_targets, SmartPointer<Label> &case_labels) {
|
||||
|
||||
ASSERT(kSmiTag == 0 && kSmiTagSize <= 2);
|
||||
|
||||
__ pop(r0);
|
||||
if (min_index != 0) {
|
||||
// small positive numbers can be immediate operands.
|
||||
if (min_index < 0) {
|
||||
__ add(r0, r0, Operand(Smi::FromInt(-min_index)));
|
||||
} else {
|
||||
__ sub(r0, r0, Operand(Smi::FromInt(min_index)));
|
||||
}
|
||||
}
|
||||
__ tst(r0, Operand(0x80000000 | kSmiTagMask));
|
||||
__ b(ne, fail_label);
|
||||
__ cmp(r0, Operand(Smi::FromInt(range)));
|
||||
__ b(ge, fail_label);
|
||||
__ add(pc, pc, Operand(r0, LSL, 2 - kSmiTagSize));
|
||||
// One extra instruction offsets the table, so the table's start address is
|
||||
// the pc-register at the above add.
|
||||
__ stop("Unreachable: Switch table alignment");
|
||||
|
||||
// table containing branch operations.
|
||||
for (int i = 0; i < range; i++) {
|
||||
__ b(case_targets[i]);
|
||||
}
|
||||
|
||||
GenerateFastCaseSwitchCases(node, case_labels);
|
||||
}
|
||||
|
||||
|
||||
void ArmCodeGenerator::VisitSwitchStatement(SwitchStatement* node) {
|
||||
Comment cmnt(masm_, "[ SwitchStatement");
|
||||
if (FLAG_debug_info) RecordStatementPosition(node);
|
||||
@ -1624,6 +1675,10 @@ void ArmCodeGenerator::VisitSwitchStatement(SwitchStatement* node) {
|
||||
|
||||
Load(node->tag());
|
||||
|
||||
if (TryGenerateFastCaseSwitchStatement(node)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Label next, fall_through, default_case;
|
||||
ZoneList<CaseClause*>* cases = node->cases();
|
||||
int length = cases->length();
|
||||
|
@ -327,28 +327,16 @@ class Ia32CodeGenerator: public CodeGenerator {
|
||||
// optimization.
|
||||
static const int kFastSwitchMinCaseCount = 5;
|
||||
|
||||
// Create fast switch implementation if all labels are small integers
|
||||
// in a limited range. Returns false if this is not the case, and no
|
||||
// code has been generated (i.e., the default implementation should be used).
|
||||
bool TryFastCaseSwitchStatement(SwitchStatement *switchStmt);
|
||||
virtual int FastCaseSwitchMaxOverheadFactor();
|
||||
virtual int FastCaseSwitchMinCaseCount();
|
||||
|
||||
// Generate a computed jump with an empty jump table.
|
||||
// Binds a label to the start of the jump table. This table must
|
||||
// be populated later when the adresses of the targets are known.
|
||||
// be populated later when the addresses of the targets are known.
|
||||
// Used by GenerateFastCaseSwitchStatement.
|
||||
void GenerateFastCaseSwitchJumpTable(
|
||||
int min_index, int range, Label *fail_label, Label &table_start);
|
||||
|
||||
// Populate an empty jump table with the adresses of bound labels.
|
||||
// Used by GenerateFastCaseSwitchStatement.
|
||||
void PopulateFastCaseSwitchJumpTable(
|
||||
Label &table_start, SmartPointer<Label*> &case_targets, int table_size);
|
||||
|
||||
// Generates a fast-case switch statement for a switch with all-Smi labels
|
||||
// in a limited range.
|
||||
// Used by TryFastCaseSwitchStatement.
|
||||
void GenerateFastCaseSwitchStatement(
|
||||
SwitchStatement *node, int min_index, int range, int default_index);
|
||||
virtual void GenerateFastCaseSwitchJumpTable(
|
||||
SwitchStatement* node, int min_index, int range, Label *fail_label,
|
||||
SmartPointer<Label*> &case_targets, SmartPointer<Label> &case_labels);
|
||||
|
||||
void RecordStatementPosition(Node* node);
|
||||
|
||||
@ -1962,29 +1950,35 @@ void Ia32CodeGenerator::VisitWithExitStatement(WithExitStatement* node) {
|
||||
__ mov(Operand(ebp, StandardFrameConstants::kContextOffset), esi);
|
||||
}
|
||||
|
||||
int Ia32CodeGenerator::FastCaseSwitchMaxOverheadFactor() {
|
||||
return kFastSwitchMaxOverheadFactor;
|
||||
}
|
||||
|
||||
// Generate a computed jump with an empty jump table.
|
||||
// Returns a label pointing to the start of the jump table. This must
|
||||
// be populated later when the adresses of the targets are known
|
||||
int Ia32CodeGenerator::FastCaseSwitchMinCaseCount() {
|
||||
return kFastSwitchMinCaseCount;
|
||||
}
|
||||
|
||||
// Generate a computed jump to a switch case.
|
||||
void Ia32CodeGenerator::GenerateFastCaseSwitchJumpTable(
|
||||
int min_index, int range, Label *fail_label, Label &table_start) {
|
||||
SwitchStatement* node, int min_index, int range, Label *fail_label,
|
||||
SmartPointer<Label*> &case_targets, SmartPointer<Label> &case_labels) {
|
||||
// Notice: Internal references, used by both the jmp instruction and
|
||||
// the table entries, need to be relocated if the buffer grows. This
|
||||
// prevents the forward use of Labels, since a displacement cannot
|
||||
// survive relocation, and it also cannot safely be distinguished
|
||||
// from a real address. Instead we put in zero-values as
|
||||
// placeholders, and fill in the adresses after the labels have been
|
||||
// placeholders, and fill in the addresses after the labels have been
|
||||
// bound.
|
||||
|
||||
__ pop(eax); // supposed smi
|
||||
// check range of value, if outside [0..length-1] jump to default/end label.
|
||||
ASSERT(kSmiTagSize == 1 && kSmiTag == 0);
|
||||
if (min_index != 0) {
|
||||
__ sub(Operand(eax), Immediate(min_index * 2)); // smi subtraction
|
||||
__ sub(Operand(eax), Immediate(min_index << kSmiTagSize));
|
||||
}
|
||||
__ test(eax, Immediate(0x80000000 | kSmiTagMask)); // negative or not smi
|
||||
__ test(eax, Immediate(0x80000000 | kSmiTagMask)); // negative or not Smi
|
||||
__ j(not_equal, fail_label, not_taken);
|
||||
__ cmp(eax, range * 2);
|
||||
__ cmp(eax, range << kSmiTagSize);
|
||||
__ j(greater_equal, fail_label, not_taken);
|
||||
|
||||
// 0 is placeholder.
|
||||
@ -1993,120 +1987,21 @@ void Ia32CodeGenerator::GenerateFastCaseSwitchJumpTable(
|
||||
int32_t jump_table_ref = __ pc_offset() - sizeof(int32_t);
|
||||
|
||||
__ Align(4);
|
||||
Label table_start;
|
||||
__ bind(&table_start);
|
||||
__ WriteInternalReference(jump_table_ref, table_start);
|
||||
|
||||
for (int i = 0; i < range; i++) {
|
||||
__ dd(0x0, RelocInfo::INTERNAL_REFERENCE); // table entry, 0 is placeholder
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Populate an empty jump table with the adresses of bound labels.
|
||||
void Ia32CodeGenerator::PopulateFastCaseSwitchJumpTable(
|
||||
Label &table_start, SmartPointer<Label*> &case_targets, int table_size) {
|
||||
for (int i = 0; i < table_size; i++) {
|
||||
int table_entry_pos = table_start.pos() + i * sizeof(uint32_t);
|
||||
__ WriteInternalReference(table_entry_pos, *case_targets[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Generates a fast-case switch statement for a switch with all-Smi labels
|
||||
// in a limited range.
|
||||
void Ia32CodeGenerator::GenerateFastCaseSwitchStatement(
|
||||
SwitchStatement *node, int min_index, int range, int default_index) {
|
||||
ZoneList<CaseClause*>* cases = node->cases();
|
||||
int length = cases->length();
|
||||
|
||||
SmartPointer<Label*> case_targets(NewArray<Label*>(range));
|
||||
SmartPointer<Label> case_labels(NewArray<Label>(length));
|
||||
|
||||
Label* fail_label = (default_index >= 0 ? &(case_labels[default_index])
|
||||
: node->break_target());
|
||||
|
||||
// Create array of labels to jump to by index and set default jump
|
||||
// targets everywhere.
|
||||
for (int i = 0; i < range; i++) {
|
||||
// length => end label
|
||||
case_targets[i] = fail_label;
|
||||
// table entry, 0 is placeholder for case address
|
||||
__ dd(0x0, RelocInfo::INTERNAL_REFERENCE);
|
||||
}
|
||||
|
||||
// Overwrite for values of cases:
|
||||
// (reverse order, so that if same label twice, the first one wins).
|
||||
for (int i = length-1; i >= 0 ; i--) {
|
||||
CaseClause* clause = cases->at(i);
|
||||
if (!clause->is_default()) {
|
||||
Object* label_value = *(clause->label()->AsLiteral()->handle());
|
||||
int case_value = Smi::cast(label_value)->value();
|
||||
case_targets[case_value - min_index] = &(case_labels[i]);
|
||||
}
|
||||
GenerateFastCaseSwitchCases(node, case_labels);
|
||||
|
||||
for (int i = 0, entry_pos = table_start.pos();
|
||||
i < range; i++, entry_pos += sizeof(uint32_t)) {
|
||||
__ WriteInternalReference(entry_pos, *case_targets[i]);
|
||||
}
|
||||
|
||||
// Generate the jump table and code for all cases.
|
||||
Label table_start;
|
||||
|
||||
GenerateFastCaseSwitchJumpTable(min_index, range, fail_label, table_start);
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
Comment cmnt(masm_, "[ case clause");
|
||||
__ bind(&(case_labels[i]));
|
||||
VisitStatements(cases->at(i)->statements());
|
||||
}
|
||||
|
||||
__ bind(node->break_target());
|
||||
|
||||
// All labels bound now, so we can populate the table with the
|
||||
// correct addresses.
|
||||
PopulateFastCaseSwitchJumpTable(table_start, case_targets, range);
|
||||
}
|
||||
|
||||
|
||||
bool Ia32CodeGenerator::TryFastCaseSwitchStatement(SwitchStatement *node) {
|
||||
ZoneList<CaseClause*>* cases = node->cases();
|
||||
int length = cases->length();
|
||||
|
||||
if (length < kFastSwitchMinCaseCount) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Test whether fast-case should be used.
|
||||
int default_index = -1;
|
||||
int min_index = Smi::kMaxValue;
|
||||
int max_index = Smi::kMinValue;
|
||||
for (int i = 0; i < length; i++) {
|
||||
CaseClause* clause = cases->at(i);
|
||||
if (clause->is_default()) {
|
||||
if (default_index >= 0) {
|
||||
return false; // More than one default label:
|
||||
// Defer to normal case for error.
|
||||
}
|
||||
default_index = i;
|
||||
} else {
|
||||
Expression* label = clause->label();
|
||||
Literal* literal = label->AsLiteral();
|
||||
if (literal == NULL) {
|
||||
return false; // fail fast case
|
||||
}
|
||||
Object* value = *(literal->handle());
|
||||
if (!value->IsSmi()) {
|
||||
return false;
|
||||
}
|
||||
int smi = Smi::cast(value)->value();
|
||||
if (smi < min_index) { min_index = smi; }
|
||||
if (smi > max_index) { max_index = smi; }
|
||||
}
|
||||
}
|
||||
|
||||
// After this all labels are smis.
|
||||
int range = max_index - min_index + 1; // |min..max| inclusive
|
||||
if (range / kFastSwitchMaxOverheadFactor > length) {
|
||||
return false; // range of labels is too sparse
|
||||
}
|
||||
|
||||
// Optimization accepted, generate code.
|
||||
GenerateFastCaseSwitchStatement(node, min_index, range, default_index);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@ -2117,7 +2012,7 @@ void Ia32CodeGenerator::VisitSwitchStatement(SwitchStatement* node) {
|
||||
|
||||
Load(node->tag());
|
||||
|
||||
if (TryFastCaseSwitchStatement(node)) {
|
||||
if (TryGenerateFastCaseSwitchStatement(node)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
101
src/codegen.cc
101
src/codegen.cc
@ -251,6 +251,107 @@ bool CodeGenerator::CheckForInlineRuntimeCall(CallRuntime* node) {
|
||||
}
|
||||
|
||||
|
||||
void CodeGenerator::GenerateFastCaseSwitchStatement(
|
||||
SwitchStatement *node, int min_index, int range, int default_index) {
|
||||
|
||||
ZoneList<CaseClause*>* cases = node->cases();
|
||||
int length = cases->length();
|
||||
|
||||
// Label pointer per number in range
|
||||
SmartPointer<Label*> case_targets(NewArray<Label*>(range));
|
||||
|
||||
// Label per switch case
|
||||
SmartPointer<Label> case_labels(NewArray<Label>(length));
|
||||
|
||||
Label* fail_label = (default_index >= 0 ? &(case_labels[default_index])
|
||||
: node->break_target());
|
||||
|
||||
// Populate array of label pointers for each number in the range.
|
||||
// Initally put the failure label everywhere.
|
||||
for (int i = 0; i < range; i++) {
|
||||
case_targets[i] = fail_label;
|
||||
}
|
||||
|
||||
// Overwrite with label of a case for the number value of that case.
|
||||
// (In reverse order, so that if the same label occurs twice, the
|
||||
// first one wins).
|
||||
for (int i = length-1; i >= 0 ; i--) {
|
||||
CaseClause* clause = cases->at(i);
|
||||
if (!clause->is_default()) {
|
||||
Object* label_value = *(clause->label()->AsLiteral()->handle());
|
||||
int case_value = Smi::cast(label_value)->value();
|
||||
case_targets[case_value - min_index] = &(case_labels[i]);
|
||||
}
|
||||
}
|
||||
|
||||
GenerateFastCaseSwitchJumpTable(node, min_index, range, fail_label,
|
||||
case_targets, case_labels);
|
||||
}
|
||||
|
||||
void CodeGenerator::GenerateFastCaseSwitchCases(
|
||||
SwitchStatement* node, SmartPointer<Label> &case_labels) {
|
||||
|
||||
ZoneList<CaseClause*>* cases = node->cases();
|
||||
int length = cases->length();
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
Comment cmnt(masm(), "[ case clause");
|
||||
masm()->bind(&(case_labels[i]));
|
||||
VisitStatements(cases->at(i)->statements());
|
||||
}
|
||||
|
||||
masm()->bind(node->break_target());
|
||||
}
|
||||
|
||||
|
||||
bool CodeGenerator::TryGenerateFastCaseSwitchStatement(SwitchStatement* node) {
|
||||
ZoneList<CaseClause*>* cases = node->cases();
|
||||
int length = cases->length();
|
||||
|
||||
if (length < FastCaseSwitchMinCaseCount()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Test whether fast-case should be used.
|
||||
int default_index = -1;
|
||||
int min_index = Smi::kMaxValue;
|
||||
int max_index = Smi::kMinValue;
|
||||
for (int i = 0; i < length; i++) {
|
||||
CaseClause* clause = cases->at(i);
|
||||
if (clause->is_default()) {
|
||||
if (default_index >= 0) {
|
||||
return false; // More than one default label:
|
||||
// Defer to normal case for error.
|
||||
}
|
||||
default_index = i;
|
||||
} else {
|
||||
Expression* label = clause->label();
|
||||
Literal* literal = label->AsLiteral();
|
||||
if (literal == NULL) {
|
||||
return false; // fail fast case
|
||||
}
|
||||
Object* value = *(literal->handle());
|
||||
if (!value->IsSmi()) {
|
||||
return false;
|
||||
}
|
||||
int smi = Smi::cast(value)->value();
|
||||
if (smi < min_index) { min_index = smi; }
|
||||
if (smi > max_index) { max_index = smi; }
|
||||
}
|
||||
}
|
||||
|
||||
// All labels are known to be Smis.
|
||||
int range = max_index - min_index + 1; // |min..max| inclusive
|
||||
if (range / FastCaseSwitchMaxOverheadFactor() > length) {
|
||||
return false; // range of labels is too sparse
|
||||
}
|
||||
|
||||
// Optimization accepted, generate code.
|
||||
GenerateFastCaseSwitchStatement(node, min_index, range, default_index);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
const char* RuntimeStub::GetName() {
|
||||
return Runtime::FunctionForId(id_)->stub_name;
|
||||
}
|
||||
|
@ -167,6 +167,43 @@ class CodeGenerator: public Visitor {
|
||||
// Fast support for object equality testing.
|
||||
virtual void GenerateObjectEquals(ZoneList<Expression*>* args) = 0;
|
||||
|
||||
|
||||
// Multiple methods for fast case switch statement support.
|
||||
|
||||
// The limit of the range of a fast-case switch, as a factor of the number
|
||||
// of cases of the switch. Each platform should return a value that
|
||||
// is optimal compared to the default code generated for a switch statement
|
||||
// on that platform.
|
||||
virtual int FastCaseSwitchMaxOverheadFactor() = 0;
|
||||
|
||||
// The minimal number of cases in a switch before the fast-case switch
|
||||
// optimization is enabled. Each platform should return a value that
|
||||
// is optimal compared to the default code generated for a switch statement
|
||||
// on that platform.
|
||||
virtual int FastCaseSwitchMinCaseCount() = 0;
|
||||
|
||||
// Allocate a jump table and create code to jump through it.
|
||||
// Should call GenerateFastCaseSwitchCases to generate the code for
|
||||
// all the cases at the appropriate point.
|
||||
virtual void GenerateFastCaseSwitchJumpTable(
|
||||
SwitchStatement* node, int min_index, int range, Label *fail_label,
|
||||
SmartPointer<Label*> &case_targets, SmartPointer<Label>& case_labels) = 0;
|
||||
|
||||
// Generate the code for cases for the fast case switch.
|
||||
// Called by GenerateFastCaseSwitchJumpTable.
|
||||
virtual void GenerateFastCaseSwitchCases(
|
||||
SwitchStatement* node, SmartPointer<Label> &case_labels);
|
||||
|
||||
// Fast support for constant-Smi switches.
|
||||
virtual void GenerateFastCaseSwitchStatement(
|
||||
SwitchStatement *node, int min_index, int range, int default_index);
|
||||
|
||||
// Fast support for constant-Smi switches. Tests whether switch statement
|
||||
// permits optimization and calls GenerateFastCaseSwitch if it does.
|
||||
// Returns true if the fast-case switch was generated, and false if not.
|
||||
virtual bool TryGenerateFastCaseSwitchStatement(SwitchStatement *node);
|
||||
|
||||
|
||||
private:
|
||||
bool is_eval_; // Tells whether code is generated for eval.
|
||||
Handle<Script> script_;
|
||||
@ -174,7 +211,7 @@ class CodeGenerator: public Visitor {
|
||||
};
|
||||
|
||||
|
||||
// RuntimeStub models code stubs calling entrypoints in the Runtime class.
|
||||
// RuntimeStub models code stubs calling entry points in the Runtime class.
|
||||
class RuntimeStub : public CodeStub {
|
||||
public:
|
||||
explicit RuntimeStub(Runtime::FunctionId id, int num_arguments)
|
||||
|
@ -88,6 +88,7 @@ class MacroAssembler: public Assembler {
|
||||
void Call(Handle<Code> code, RelocInfo::Mode rmode, Condition cond = al);
|
||||
void Ret();
|
||||
|
||||
|
||||
// Sets the remembered set bit for [address+offset], where address is the
|
||||
// address of the heap object 'object'. The address must be in the first 8K
|
||||
// of an allocated page. The 'scratch' register is used in the
|
||||
|
@ -54,7 +54,7 @@ function f2(x) {
|
||||
break;
|
||||
case 2:
|
||||
r = "two";
|
||||
break
|
||||
break;
|
||||
case 3:
|
||||
r = "three";
|
||||
break;
|
||||
@ -69,6 +69,13 @@ assertEquals("one", f2(1), "0-1-switch.1");
|
||||
assertEquals("default", f2(7), "0-1-switch.2");
|
||||
assertEquals("default", f2(-1), "0-1-switch.-1");
|
||||
assertEquals("default", f2(NaN), "0-1-switch.NaN");
|
||||
assertEquals("default", f2(Math.pow(2,34)), "0-1-switch.largeNum");
|
||||
assertEquals("default", f2("0"), "0-1-switch.string");
|
||||
assertEquals("default", f2(false), "0-1-switch.bool");
|
||||
assertEquals("default", f2(null), "0-1-switch.null");
|
||||
assertEquals("default", f2(undefined), "0-1-switch.undef");
|
||||
assertEquals("default", f2(new Number(2)), "0-1-switch.undef");
|
||||
assertEquals("default", f2({valueOf: function(){return 2; }}), "0-1-switch.obj");
|
||||
|
||||
|
||||
function f3(x, c) {
|
||||
@ -114,10 +121,10 @@ function f4(x) {
|
||||
}
|
||||
|
||||
|
||||
assertEquals(3, f4(0), "fallthrough-switch.0")
|
||||
assertEquals(3, f4(1), "fallthrough-switch.1")
|
||||
assertEquals(3, f4(2), "fallthrough-switch.2")
|
||||
assertEquals(5, f4(3), "fallthrough-switch.3")
|
||||
assertEquals(3, f4(0), "fallthrough-switch.0");
|
||||
assertEquals(3, f4(1), "fallthrough-switch.1");
|
||||
assertEquals(3, f4(2), "fallthrough-switch.2");
|
||||
assertEquals(5, f4(3), "fallthrough-switch.3");
|
||||
|
||||
|
||||
function f5(x) {
|
||||
@ -130,14 +137,14 @@ function f5(x) {
|
||||
}
|
||||
}
|
||||
|
||||
assertTrue(f5(-2), "negcase.-2")
|
||||
assertFalse(f5(-1), "negcase.-1")
|
||||
assertTrue(f5(0), "negcase.-0")
|
||||
assertEquals(42, f5(1), "negcase.1")
|
||||
assertFalse(f5(2), "negcase.2")
|
||||
assertTrue(f5(-2), "negcase.-2");
|
||||
assertFalse(f5(-1), "negcase.-1");
|
||||
assertTrue(f5(0), "negcase.-0");
|
||||
assertEquals(42, f5(1), "negcase.1");
|
||||
assertFalse(f5(2), "negcase.2");
|
||||
|
||||
function f6(N) {
|
||||
// long enough case that code buffer grows while it is code-generated.
|
||||
// long enough case that code buffer grows during code-generation
|
||||
var res = 0;
|
||||
for(var i = 0; i < N; i++) {
|
||||
switch(i & 0x3f) {
|
||||
@ -217,4 +224,4 @@ assertEquals(2016, f6(64), "largeSwitch.64");
|
||||
assertEquals(4032, f6(128), "largeSwitch.128");
|
||||
assertEquals(4222, f6(148), "largeSwitch.148");
|
||||
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user