Ignore:
Timestamp:
Jul 23, 2008, 5:49:46 PM (17 years ago)
Author:
[email protected]
Message:

Improve switch performance.

Reviewed by Geoff Garen and Sam Weinig.

Improve switch performance by converting to a hashmap based jump
table to avoid the sequence of dispatches that would otherwise be
needed. This results in a 9-19x performance win for string switches
based on ad hoc testing, and a 6x improvement for integer switch
statements. SunSpider reports a 1.2% progression.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/JavaScriptCore/VM/CodeGenerator.cpp

    r35245 r35309  
    3333#include "JSFunction.h"
    3434#include "Machine.h"
     35#include "ustring.h"
    3536
    3637using namespace std;
     
    11451146}
    11461147
     1148void CodeGenerator::beginSwitch(RegisterID* scrutineeRegister, SwitchInfo::SwitchType type)
     1149{
     1150    SwitchInfo info = { instructions().size(), type };
     1151    switch (type) {
     1152        case SwitchInfo::SwitchImmediate:
     1153            emitOpcode(op_switch_imm);
     1154            break;
     1155        case SwitchInfo::SwitchCharacter:
     1156            emitOpcode(op_switch_char);
     1157            break;
     1158        case SwitchInfo::SwitchString:
     1159            emitOpcode(op_switch_string);
     1160            break;
     1161        default:
     1162            ASSERT_NOT_REACHED();
     1163    }
     1164
     1165    instructions().append(0); // place holder for table index
     1166    instructions().append(0); // place holder for default target   
     1167    instructions().append(scrutineeRegister->index());
     1168    m_switchContextStack.append(info);
     1169}
     1170
     1171static int32_t keyForImmediateSwitch(ExpressionNode* node, int32_t min, int32_t max)
     1172{
     1173    UNUSED_PARAM(max);
     1174    ASSERT(node->isNumber());
     1175    double value = static_cast<NumberNode*>(node)->value();
     1176    ASSERT(JSImmediate::from(value));
     1177    int32_t key = static_cast<int32_t>(value);
     1178    ASSERT(key == value);
     1179    ASSERT(key >= min);
     1180    ASSERT(key <= max);
     1181    return key - min;
     1182}
     1183
     1184static void prepareJumpTableForImmediateSwitch(SimpleJumpTable& jumpTable, int32_t switchAddress, uint32_t clauseCount, RefPtr<LabelID>* labels, ExpressionNode** nodes, int32_t min, int32_t max)
     1185{
     1186    jumpTable.min = min;
     1187    jumpTable.branchOffsets.resize(max - min + 1);
     1188    jumpTable.branchOffsets.fill(0);
     1189    for (uint32_t i = 0; i < clauseCount; ++i) {
     1190        // We're emitting this after the clause labels should have been fixed, so
     1191        // the labels should not be "forward" references
     1192        ASSERT(!labels[i]->isForwardLabel());
     1193        jumpTable.add(keyForImmediateSwitch(nodes[i], min, max), labels[i]->offsetFrom(switchAddress));
     1194    }
     1195}
     1196
     1197static int32_t keyForCharacterSwitch(ExpressionNode* node, int32_t min, int32_t max)
     1198{
     1199    UNUSED_PARAM(max);
     1200    ASSERT(node->isString());
     1201    UString::Rep* clause = static_cast<StringNode*>(node)->value().rep();
     1202    ASSERT(clause->size() == 1);
     1203   
     1204    int32_t key = clause->data()[0];
     1205    ASSERT(key >= min);
     1206    ASSERT(key <= max);
     1207    return key - min;
     1208}
     1209
     1210static void prepareJumpTableForCharacterSwitch(SimpleJumpTable& jumpTable, int32_t switchAddress, uint32_t clauseCount, RefPtr<LabelID>* labels, ExpressionNode** nodes, int32_t min, int32_t max)
     1211{
     1212    jumpTable.min = min;
     1213    jumpTable.branchOffsets.resize(max - min + 1);
     1214    jumpTable.branchOffsets.fill(0);
     1215    for (uint32_t i = 0; i < clauseCount; ++i) {
     1216        // We're emitting this after the clause labels should have been fixed, so
     1217        // the labels should not be "forward" references
     1218        ASSERT(!labels[i]->isForwardLabel());
     1219        jumpTable.add(keyForCharacterSwitch(nodes[i], min, max), labels[i]->offsetFrom(switchAddress));
     1220    }
     1221}
     1222
     1223static void prepareJumpTableForStringSwitch(StringJumpTable& jumpTable, int32_t switchAddress, uint32_t clauseCount, RefPtr<LabelID>* labels, ExpressionNode** nodes)
     1224{
     1225    for (uint32_t i = 0; i < clauseCount; ++i) {
     1226        // We're emitting this after the clause labels should have been fixed, so
     1227        // the labels should not be "forward" references
     1228        ASSERT(!labels[i]->isForwardLabel());
     1229       
     1230        ASSERT(nodes[i]->isString());
     1231        UString::Rep* clause = static_cast<StringNode*>(nodes[i])->value().rep();
     1232        jumpTable.add(clause, labels[i]->offsetFrom(switchAddress));
     1233    }
     1234}
     1235
     1236void CodeGenerator::endSwitch(uint32_t clauseCount, RefPtr<LabelID>* labels, ExpressionNode** nodes, LabelID* defaultLabel, int32_t min, int32_t max)
     1237{
     1238    SwitchInfo switchInfo = m_switchContextStack.last();
     1239    m_switchContextStack.removeLast();
     1240    if (switchInfo.switchType == SwitchInfo::SwitchImmediate) {
     1241        instructions()[switchInfo.opcodeOffset + 1] = m_codeBlock->immediateSwitchJumpTables.size();
     1242        instructions()[switchInfo.opcodeOffset + 2] = defaultLabel->offsetFrom(switchInfo.opcodeOffset + 3);
     1243
     1244        m_codeBlock->immediateSwitchJumpTables.append(SimpleJumpTable());
     1245        SimpleJumpTable& jumpTable = m_codeBlock->immediateSwitchJumpTables.last();
     1246
     1247        prepareJumpTableForImmediateSwitch(jumpTable, switchInfo.opcodeOffset + 3, clauseCount, labels, nodes, min, max);
     1248    } else if (switchInfo.switchType == SwitchInfo::SwitchCharacter) {
     1249        instructions()[switchInfo.opcodeOffset + 1] = m_codeBlock->characterSwitchJumpTables.size();
     1250        instructions()[switchInfo.opcodeOffset + 2] = defaultLabel->offsetFrom(switchInfo.opcodeOffset + 3);
     1251       
     1252        m_codeBlock->characterSwitchJumpTables.append(SimpleJumpTable());
     1253        SimpleJumpTable& jumpTable = m_codeBlock->characterSwitchJumpTables.last();
     1254
     1255        prepareJumpTableForCharacterSwitch(jumpTable, switchInfo.opcodeOffset + 3, clauseCount, labels, nodes, min, max);
     1256    } else {
     1257        ASSERT(switchInfo.switchType == SwitchInfo::SwitchString);
     1258        instructions()[switchInfo.opcodeOffset + 1] = m_codeBlock->stringSwitchJumpTables.size();
     1259        instructions()[switchInfo.opcodeOffset + 2] = defaultLabel->offsetFrom(switchInfo.opcodeOffset + 3);
     1260
     1261        m_codeBlock->stringSwitchJumpTables.append(StringJumpTable());
     1262        StringJumpTable& jumpTable = m_codeBlock->stringSwitchJumpTables.last();
     1263
     1264        prepareJumpTableForStringSwitch(jumpTable, switchInfo.opcodeOffset + 3, clauseCount, labels, nodes);
     1265    }
     1266}
     1267
    11471268} // namespace KJS
Note: See TracChangeset for help on using the changeset viewer.