}
-int CodeGenerator::FastCaseSwitchMaxOverheadFactor() {
- return kFastSwitchMaxOverheadFactor;
-}
-
-int CodeGenerator::FastCaseSwitchMinCaseCount() {
- return kFastSwitchMinCaseCount;
-}
-
-
-void CodeGenerator::GenerateFastCaseSwitchJumpTable(
- SwitchStatement* node,
- int min_index,
- int range,
- Label* default_label,
- Vector<Label*> case_targets,
- Vector<Label> case_labels) {
- VirtualFrame::SpilledScope spilled_scope;
- JumpTarget setup_default;
- JumpTarget is_smi;
-
- // A non-null default label pointer indicates a default case among
- // the case labels. Otherwise we use the break target as a
- // "default" for failure to hit the jump table.
- JumpTarget* default_target =
- (default_label == NULL) ? node->break_target() : &setup_default;
-
- ASSERT(kSmiTag == 0 && kSmiTagSize <= 2);
- frame_->EmitPop(r0);
-
- // Test for a Smi value in a HeapNumber.
- __ tst(r0, Operand(kSmiTagMask));
- is_smi.Branch(eq);
- __ CompareObjectType(r0, r1, r1, HEAP_NUMBER_TYPE);
- default_target->Branch(ne);
- frame_->EmitPush(r0);
- frame_->CallRuntime(Runtime::kNumberToSmi, 1);
- is_smi.Bind();
-
- if (min_index != 0) {
- // Small positive numbers can be immediate operands.
- if (min_index < 0) {
- // If min_index is Smi::kMinValue, -min_index is not a Smi.
- if (Smi::IsValid(-min_index)) {
- __ add(r0, r0, Operand(Smi::FromInt(-min_index)));
- } else {
- __ add(r0, r0, Operand(Smi::FromInt(-min_index - 1)));
- __ add(r0, r0, Operand(Smi::FromInt(1)));
- }
- } else {
- __ sub(r0, r0, Operand(Smi::FromInt(min_index)));
- }
- }
- __ tst(r0, Operand(0x80000000 | kSmiTagMask));
- default_target->Branch(ne);
- __ cmp(r0, Operand(Smi::FromInt(range)));
- default_target->Branch(ge);
- VirtualFrame* start_frame = new VirtualFrame(frame_);
- __ SmiJumpTable(r0, case_targets);
-
- GenerateFastCaseSwitchCases(node, case_labels, start_frame);
-
- // If there was a default case among the case labels, we need to
- // emit code to jump to it from the default target used for failure
- // to hit the jump table.
- if (default_label != NULL) {
- if (has_valid_frame()) {
- node->break_target()->Jump();
- }
- setup_default.Bind();
- frame_->MergeTo(start_frame);
- __ b(default_label);
- DeleteFrame();
- }
- if (node->break_target()->is_linked()) {
- node->break_target()->Bind();
- }
-}
-
-
void CodeGenerator::VisitSwitchStatement(SwitchStatement* node) {
#ifdef DEBUG
int original_height = frame_->height();
node->break_target()->set_direction(JumpTarget::FORWARD_ONLY);
LoadAndSpill(node->tag());
- if (TryGenerateFastCaseSwitchStatement(node)) {
- ASSERT(!has_valid_frame() || frame_->height() == original_height);
- return;
- }
JumpTarget next_test;
JumpTarget fall_through;
inline void GenerateMathSin(ZoneList<Expression*>* args);
inline void GenerateMathCos(ZoneList<Expression*>* args);
- // Methods and constants for fast case switch statement support.
- //
- // Only allow fast-case switch if the range of labels is at most
- // this factor times the number of case labels.
- // Value is derived from comparing the size of code generated by the normal
- // switch code for Smi-labels to the size of a single pointer. If code
- // quality increases this number should be decreased to match.
- static const int kFastSwitchMaxOverheadFactor = 10;
-
- // Minimal number of switch cases required before we allow jump-table
- // optimization.
- static const int kFastSwitchMinCaseCount = 5;
-
- // 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.
- int FastCaseSwitchMaxOverheadFactor();
-
- // 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.
- int FastCaseSwitchMinCaseCount();
-
- // 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.
- void GenerateFastCaseSwitchJumpTable(SwitchStatement* node,
- int min_index,
- int range,
- Label* default_label,
- Vector<Label*> case_targets,
- Vector<Label> case_labels);
-
- // Generate the code for cases for the fast case switch.
- // Called by GenerateFastCaseSwitchJumpTable.
- void GenerateFastCaseSwitchCases(SwitchStatement* node,
- Vector<Label> case_labels,
- VirtualFrame* start_frame);
-
- // Fast support for constant-Smi switches.
- 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.
- bool TryGenerateFastCaseSwitchStatement(SwitchStatement* node);
-
-
// Methods used to indicate which source code is generated for. Source
// positions are collected by the assembler and emitted with the relocation
// information.
}
-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]) : NULL;
-
- // 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,
- Vector<Label*>(*case_targets, range),
- Vector<Label>(*case_labels, length));
-}
-
-
-void CodeGenerator::GenerateFastCaseSwitchCases(
- SwitchStatement* node,
- Vector<Label> case_labels,
- VirtualFrame* start_frame) {
- ZoneList<CaseClause*>* cases = node->cases();
- int length = cases->length();
-
- for (int i = 0; i < length; i++) {
- Comment cmnt(masm(), "[ Case clause");
-
- // We may not have a virtual frame if control flow did not fall
- // off the end of the previous case. In that case, use the start
- // frame. Otherwise, we have to merge the existing one to the
- // start frame as part of the previous case.
- if (!has_valid_frame()) {
- RegisterFile empty;
- SetFrame(new VirtualFrame(start_frame), &empty);
- } else {
- frame_->MergeTo(start_frame);
- }
- masm()->bind(&case_labels[i]);
- VisitStatements(cases->at(i)->statements());
- }
-}
-
-
-bool CodeGenerator::TryGenerateFastCaseSwitchStatement(SwitchStatement* node) {
- // TODO(238): Due to issue 238, fast case switches can crash on ARM
- // and possibly IA32. They are disabled for now.
- // See http://code.google.com/p/v8/issues/detail?id=238
- return false;
-
- 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) {
- // There is more than one default label. Defer to the normal case
- // for error.
- return false;
- }
- 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 int_value = Smi::cast(value)->value();
- min_index = Min(int_value, min_index);
- max_index = Max(int_value, max_index);
- }
- }
-
- // 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;
-}
-
-
void CodeGenerator::CodeForFunctionPosition(FunctionLiteral* fun) {
if (FLAG_debug_info) {
int pos = fun->start_position();
// FindInlineRuntimeLUT
// CheckForInlineRuntimeCall
// PatchInlineRuntimeEntry
-// GenerateFastCaseSwitchStatement
-// GenerateFastCaseSwitchCases
-// TryGenerateFastCaseSwitchStatement
-// GenerateFastCaseSwitchJumpTable
-// FastCaseSwitchMinCaseCount
-// FastCaseSwitchMaxOverheadFactor
// CodeForFunctionPosition
// CodeForReturnPosition
// CodeForStatementPosition
}
-void Assembler::WriteInternalReference(int position, const Label& bound_label) {
- ASSERT(bound_label.is_bound());
- ASSERT(0 <= position);
- ASSERT(position + static_cast<int>(sizeof(uint32_t)) <= pc_offset());
- ASSERT(long_at(position) == 0); // only initialize once!
-
- uint32_t label_loc = reinterpret_cast<uint32_t>(addr_at(bound_label.pos()));
- long_at_put(position, label_loc);
-}
-
-
#ifdef GENERATED_CODE_COVERAGE
static FILE* coverage_log = NULL;
// Used for inline tables, e.g., jump-tables.
void dd(uint32_t data, RelocInfo::Mode reloc_info);
- // Writes the absolute address of a bound label at the given position in
- // the generated code. That positions should have the relocation mode
- // internal_reference!
- void WriteInternalReference(int position, const Label& bound_label);
-
int pc_offset() const { return pc_ - buffer_; }
int current_statement_position() const { return current_statement_position_; }
int current_position() const { return current_position_; }
}
-int CodeGenerator::FastCaseSwitchMaxOverheadFactor() {
- return kFastSwitchMaxOverheadFactor;
-}
-
-
-int CodeGenerator::FastCaseSwitchMinCaseCount() {
- return kFastSwitchMinCaseCount;
-}
-
-
-// Generate a computed jump to a switch case.
-void CodeGenerator::GenerateFastCaseSwitchJumpTable(
- SwitchStatement* node,
- int min_index,
- int range,
- Label* default_label,
- Vector<Label*> case_targets,
- Vector<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 addresses after the labels have been
- // bound.
-
- JumpTarget setup_default;
- JumpTarget is_smi;
-
- // A non-null default label pointer indicates a default case among
- // the case labels. Otherwise we use the break target as a
- // "default".
- JumpTarget* default_target =
- (default_label == NULL) ? node->break_target() : &setup_default;
-
- // Test whether input is a smi.
- ASSERT(kSmiTagSize == 1 && kSmiTag == 0);
- Result switch_value = frame_->Pop();
- switch_value.ToRegister();
- __ test(switch_value.reg(), Immediate(kSmiTagMask));
- is_smi.Branch(equal, &switch_value, taken);
-
- // It's a heap object, not a smi or a failure. Check if it is a
- // heap number.
- Result temp = allocator()->Allocate();
- ASSERT(temp.is_valid());
- __ CmpObjectType(switch_value.reg(), HEAP_NUMBER_TYPE, temp.reg());
- temp.Unuse();
- default_target->Branch(not_equal);
-
- // The switch value is a heap number. Convert it to a smi.
- frame_->Push(&switch_value);
- Result smi_value = frame_->CallRuntime(Runtime::kNumberToSmi, 1);
-
- is_smi.Bind(&smi_value);
- smi_value.ToRegister();
- // Convert the switch value to a 0-based table index.
- if (min_index != 0) {
- frame_->Spill(smi_value.reg());
- __ sub(Operand(smi_value.reg()), Immediate(min_index << kSmiTagSize));
- }
- // Go to the default case if the table index is negative or not a smi.
- __ test(smi_value.reg(), Immediate(0x80000000 | kSmiTagMask));
- default_target->Branch(not_equal, not_taken);
- __ cmp(smi_value.reg(), range << kSmiTagSize);
- default_target->Branch(greater_equal, not_taken);
-
- // The expected frame at all the case labels is a version of the
- // current one (the bidirectional entry frame, which an arbitrary
- // frame of the correct height can be merged to). Keep a copy to
- // restore at the start of every label. Create a jump target and
- // bind it to set its entry frame properly.
- JumpTarget entry_target(JumpTarget::BIDIRECTIONAL);
- entry_target.Bind(&smi_value);
- VirtualFrame* start_frame = new VirtualFrame(frame_);
-
- // 0 is placeholder.
- // Jump to the address at table_address + 2 * smi_value.reg().
- // The target of the jump is read from table_address + 4 * switch_value.
- // The Smi encoding of smi_value.reg() is 2 * switch_value.
- smi_value.ToRegister();
- __ jmp(Operand(smi_value.reg(), smi_value.reg(),
- times_1, 0x0, RelocInfo::INTERNAL_REFERENCE));
- smi_value.Unuse();
- // Calculate address to overwrite later with actual address of table.
- int32_t jump_table_ref = masm_->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++) {
- // These are the table entries. 0x0 is the placeholder for case address.
- __ dd(0x0, RelocInfo::INTERNAL_REFERENCE);
- }
-
- GenerateFastCaseSwitchCases(node, case_labels, start_frame);
-
- // If there was a default case, we need to emit the code to match it.
- if (default_label != NULL) {
- if (has_valid_frame()) {
- node->break_target()->Jump();
- }
- setup_default.Bind();
- frame_->MergeTo(start_frame);
- __ jmp(default_label);
- DeleteFrame();
- }
- if (node->break_target()->is_linked()) {
- node->break_target()->Bind();
- }
-
- for (int i = 0, entry_pos = table_start.pos();
- i < range;
- i++, entry_pos += sizeof(uint32_t)) {
- if (case_targets[i] == NULL) {
- __ WriteInternalReference(entry_pos,
- *node->break_target()->entry_label());
- } else {
- __ WriteInternalReference(entry_pos, *case_targets[i]);
- }
- }
-}
-
-
void CodeGenerator::VisitSwitchStatement(SwitchStatement* node) {
ASSERT(!in_spilled_code());
Comment cmnt(masm_, "[ SwitchStatement");
// Compile the switch value.
Load(node->tag());
- if (TryGenerateFastCaseSwitchStatement(node)) {
- return;
- }
-
ZoneList<CaseClause*>* cases = node->cases();
int length = cases->length();
CaseClause* default_clause = NULL;
inline void GenerateMathSin(ZoneList<Expression*>* args);
inline void GenerateMathCos(ZoneList<Expression*>* args);
- // Methods and constants for fast case switch statement support.
- //
- // Only allow fast-case switch if the range of labels is at most
- // this factor times the number of case labels.
- // Value is derived from comparing the size of code generated by the normal
- // switch code for Smi-labels to the size of a single pointer. If code
- // quality increases this number should be decreased to match.
- static const int kFastSwitchMaxOverheadFactor = 5;
-
- // Minimal number of switch cases required before we allow jump-table
- // optimization.
- static const int kFastSwitchMinCaseCount = 5;
-
- // 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.
- int FastCaseSwitchMaxOverheadFactor();
-
- // 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.
- int FastCaseSwitchMinCaseCount();
-
- // 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.
- void GenerateFastCaseSwitchJumpTable(SwitchStatement* node,
- int min_index,
- int range,
- Label* fail_label,
- Vector<Label*> case_targets,
- Vector<Label> case_labels);
-
- // Generate the code for cases for the fast case switch.
- // Called by GenerateFastCaseSwitchJumpTable.
- void GenerateFastCaseSwitchCases(SwitchStatement* node,
- Vector<Label> case_labels,
- VirtualFrame* start_frame);
-
- // Fast support for constant-Smi switches.
- 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.
- bool TryGenerateFastCaseSwitchStatement(SwitchStatement* node);
-
// Methods used to indicate which source code is generated for. Source
// positions are collected by the assembler and emitted with the relocation
// information.
// Used for inline tables, e.g., jump-tables.
// void dd(uint64_t data, RelocInfo::Mode reloc_info);
- // Writes the absolute address of a bound label at the given position in
- // the generated code. That positions should have the relocation mode
- // internal_reference!
- void WriteInternalReference(int position, const Label& bound_label);
-
int pc_offset() const { return pc_ - buffer_; }
int current_statement_position() const { return current_statement_position_; }
int current_position() const { return current_position_; }