Shorten generated code for binary-search switches

On some branches of the search tree for a binary-search switch, the
input value is sufficiently constrained that we could unconditionally
jump to the last possible case rather than checking for value equality.
This shortens some builtins by a few instructions and might speed things
up, though I expect the effect to be small.

Change-Id: I2313f26976e6d3c182f03bd927b338c8175b3af3
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3335437
Reviewed-by: Nico Hartmann <nicohartmann@chromium.org>
Commit-Queue: Seth Brenith <seth.brenith@microsoft.com>
Cr-Commit-Position: refs/heads/main@{#78376}
This commit is contained in:
Seth Brenith 2021-12-13 12:30:27 -08:00 committed by V8 LUCI CQ
parent 86363753cf
commit 00a757fabd
12 changed files with 44 additions and 28 deletions

View File

@ -3625,9 +3625,9 @@ void CodeGenerator::AssembleArchBoolean(Instruction* instr,
void CodeGenerator::AssembleArchBinarySearchSwitch(Instruction* instr) {
ArmOperandConverter i(this, instr);
Register input = i.InputRegister(0);
std::vector<std::pair<int32_t, Label*>> cases;
std::vector<std::pair<int32_t, RpoNumber>> cases;
for (size_t index = 2; index < instr->InputCount(); index += 2) {
cases.push_back({i.InputInt32(index + 0), GetLabel(i.InputRpo(index + 1))});
cases.push_back({i.InputInt32(index + 0), i.InputRpo(index + 1)});
}
AssembleArchBinarySearchSwitchRange(input, i.InputRpo(1), cases.data(),
cases.data() + cases.size());

View File

@ -2959,9 +2959,9 @@ void CodeGenerator::AssembleArchSelect(Instruction* instr,
void CodeGenerator::AssembleArchBinarySearchSwitch(Instruction* instr) {
Arm64OperandConverter i(this, instr);
Register input = i.InputRegister32(0);
std::vector<std::pair<int32_t, Label*>> cases;
std::vector<std::pair<int32_t, RpoNumber>> cases;
for (size_t index = 2; index < instr->InputCount(); index += 2) {
cases.push_back({i.InputInt32(index + 0), GetLabel(i.InputRpo(index + 1))});
cases.push_back({i.InputInt32(index + 0), i.InputRpo(index + 1)});
}
AssembleArchBinarySearchSwitchRange(input, i.InputRpo(1), cases.data(),
cases.data() + cases.size());

View File

@ -491,11 +491,23 @@ void CodeGenerator::AssembleCode() {
}
void CodeGenerator::AssembleArchBinarySearchSwitchRange(
Register input, RpoNumber def_block, std::pair<int32_t, Label*>* begin,
std::pair<int32_t, Label*>* end) {
Register input, RpoNumber def_block, std::pair<int32_t, RpoNumber>* begin,
std::pair<int32_t, RpoNumber>* end, int32_t min_possible_value,
int32_t max_possible_value) {
if (end - begin < kBinarySearchSwitchMinimalCases) {
while (begin != end) {
tasm()->JumpIfEqual(input, begin->first, begin->second);
if (min_possible_value == begin->first) {
if (max_possible_value == begin->first) {
// Only one value is possible, so we can jump to it unconditionally.
DCHECK_EQ(begin + 1, end);
def_block = begin->second;
break;
}
// After emitting the branch for this case, the minimum possible value
// is increased.
++min_possible_value;
}
tasm()->JumpIfEqual(input, begin->first, GetLabel(begin->second));
++begin;
}
AssembleArchJumpRegardlessOfAssemblyOrder(def_block);
@ -504,9 +516,11 @@ void CodeGenerator::AssembleArchBinarySearchSwitchRange(
auto middle = begin + (end - begin) / 2;
Label less_label;
tasm()->JumpIfLessThan(input, middle->first, &less_label);
AssembleArchBinarySearchSwitchRange(input, def_block, middle, end);
AssembleArchBinarySearchSwitchRange(input, def_block, middle, end,
middle->first, max_possible_value);
tasm()->bind(&less_label);
AssembleArchBinarySearchSwitchRange(input, def_block, begin, middle);
AssembleArchBinarySearchSwitchRange(input, def_block, begin, middle,
min_possible_value, middle->first - 1);
}
void CodeGenerator::AssembleArchJump(RpoNumber target) {

View File

@ -257,9 +257,11 @@ class V8_EXPORT_PRIVATE CodeGenerator final : public GapResolver::Assembler {
#if V8_ENABLE_WEBASSEMBLY
void AssembleArchTrap(Instruction* instr, FlagsCondition condition);
#endif // V8_ENABLE_WEBASSEMBLY
void AssembleArchBinarySearchSwitchRange(Register input, RpoNumber def_block,
std::pair<int32_t, Label*>* begin,
std::pair<int32_t, Label*>* end);
void AssembleArchBinarySearchSwitchRange(
Register input, RpoNumber def_block, std::pair<int32_t, RpoNumber>* begin,
std::pair<int32_t, RpoNumber>* end,
int32_t min_possible_value = std::numeric_limits<int32_t>::min(),
int32_t max_possible_value = std::numeric_limits<int32_t>::max());
void AssembleArchBinarySearchSwitch(Instruction* instr);
void AssembleArchTableSwitch(Instruction* instr);

View File

@ -3841,9 +3841,9 @@ void CodeGenerator::AssembleArchBoolean(Instruction* instr,
void CodeGenerator::AssembleArchBinarySearchSwitch(Instruction* instr) {
IA32OperandConverter i(this, instr);
Register input = i.InputRegister(0);
std::vector<std::pair<int32_t, Label*>> cases;
std::vector<std::pair<int32_t, RpoNumber>> cases;
for (size_t index = 2; index < instr->InputCount(); index += 2) {
cases.push_back({i.InputInt32(index + 0), GetLabel(i.InputRpo(index + 1))});
cases.push_back({i.InputInt32(index + 0), i.InputRpo(index + 1)});
}
AssembleArchBinarySearchSwitchRange(input, i.InputRpo(1), cases.data(),
cases.data() + cases.size());

View File

@ -2152,9 +2152,9 @@ void CodeGenerator::AssembleArchBoolean(Instruction* instr,
void CodeGenerator::AssembleArchBinarySearchSwitch(Instruction* instr) {
Loong64OperandConverter i(this, instr);
Register input = i.InputRegister(0);
std::vector<std::pair<int32_t, Label*>> cases;
std::vector<std::pair<int32_t, RpoNumber>> cases;
for (size_t index = 2; index < instr->InputCount(); index += 2) {
cases.push_back({i.InputInt32(index + 0), GetLabel(i.InputRpo(index + 1))});
cases.push_back({i.InputInt32(index + 0), i.InputRpo(index + 1)});
}
AssembleArchBinarySearchSwitchRange(input, i.InputRpo(1), cases.data(),
cases.data() + cases.size());

View File

@ -3906,9 +3906,9 @@ void CodeGenerator::AssembleArchBoolean(Instruction* instr,
void CodeGenerator::AssembleArchBinarySearchSwitch(Instruction* instr) {
MipsOperandConverter i(this, instr);
Register input = i.InputRegister(0);
std::vector<std::pair<int32_t, Label*>> cases;
std::vector<std::pair<int32_t, RpoNumber>> cases;
for (size_t index = 2; index < instr->InputCount(); index += 2) {
cases.push_back({i.InputInt32(index + 0), GetLabel(i.InputRpo(index + 1))});
cases.push_back({i.InputInt32(index + 0), i.InputRpo(index + 1)});
}
AssembleArchBinarySearchSwitchRange(input, i.InputRpo(1), cases.data(),
cases.data() + cases.size());

View File

@ -4110,9 +4110,9 @@ void CodeGenerator::AssembleArchBoolean(Instruction* instr,
void CodeGenerator::AssembleArchBinarySearchSwitch(Instruction* instr) {
MipsOperandConverter i(this, instr);
Register input = i.InputRegister(0);
std::vector<std::pair<int32_t, Label*>> cases;
std::vector<std::pair<int32_t, RpoNumber>> cases;
for (size_t index = 2; index < instr->InputCount(); index += 2) {
cases.push_back({i.InputInt32(index + 0), GetLabel(i.InputRpo(index + 1))});
cases.push_back({i.InputInt32(index + 0), i.InputRpo(index + 1)});
}
AssembleArchBinarySearchSwitchRange(input, i.InputRpo(1), cases.data(),
cases.data() + cases.size());

View File

@ -3926,9 +3926,9 @@ void CodeGenerator::AssembleArchBoolean(Instruction* instr,
void CodeGenerator::AssembleArchBinarySearchSwitch(Instruction* instr) {
PPCOperandConverter i(this, instr);
Register input = i.InputRegister(0);
std::vector<std::pair<int32_t, Label*>> cases;
std::vector<std::pair<int32_t, RpoNumber>> cases;
for (size_t index = 2; index < instr->InputCount(); index += 2) {
cases.push_back({i.InputInt32(index + 0), GetLabel(i.InputRpo(index + 1))});
cases.push_back({i.InputInt32(index + 0), i.InputRpo(index + 1)});
}
AssembleArchBinarySearchSwitchRange(input, i.InputRpo(1), cases.data(),
cases.data() + cases.size());

View File

@ -3764,9 +3764,9 @@ void CodeGenerator::AssembleArchBoolean(Instruction* instr,
void CodeGenerator::AssembleArchBinarySearchSwitch(Instruction* instr) {
RiscvOperandConverter i(this, instr);
Register input = i.InputRegister(0);
std::vector<std::pair<int32_t, Label*>> cases;
std::vector<std::pair<int32_t, RpoNumber>> cases;
for (size_t index = 2; index < instr->InputCount(); index += 2) {
cases.push_back({i.InputInt32(index + 0), GetLabel(i.InputRpo(index + 1))});
cases.push_back({i.InputInt32(index + 0), i.InputRpo(index + 1)});
}
AssembleArchBinarySearchSwitchRange(input, i.InputRpo(1), cases.data(),
cases.data() + cases.size());

View File

@ -3793,9 +3793,9 @@ void CodeGenerator::AssembleArchBoolean(Instruction* instr,
void CodeGenerator::AssembleArchBinarySearchSwitch(Instruction* instr) {
S390OperandConverter i(this, instr);
Register input = i.InputRegister(0);
std::vector<std::pair<int32_t, Label*>> cases;
std::vector<std::pair<int32_t, RpoNumber>> cases;
for (size_t index = 2; index < instr->InputCount(); index += 2) {
cases.push_back({i.InputInt32(index + 0), GetLabel(i.InputRpo(index + 1))});
cases.push_back({i.InputInt32(index + 0), i.InputRpo(index + 1)});
}
AssembleArchBinarySearchSwitchRange(input, i.InputRpo(1), cases.data(),
cases.data() + cases.size());

View File

@ -4478,9 +4478,9 @@ void CodeGenerator::AssembleArchBoolean(Instruction* instr,
void CodeGenerator::AssembleArchBinarySearchSwitch(Instruction* instr) {
X64OperandConverter i(this, instr);
Register input = i.InputRegister(0);
std::vector<std::pair<int32_t, Label*>> cases;
std::vector<std::pair<int32_t, RpoNumber>> cases;
for (size_t index = 2; index < instr->InputCount(); index += 2) {
cases.push_back({i.InputInt32(index + 0), GetLabel(i.InputRpo(index + 1))});
cases.push_back({i.InputInt32(index + 0), i.InputRpo(index + 1)});
}
AssembleArchBinarySearchSwitchRange(input, i.InputRpo(1), cases.data(),
cases.data() + cases.size());