MIPS: Fix trampoline emission after switch table generation

Trampolines are generated when the value of pc_offset is greater than
next_buffer_check_ (attribute from Assembler class). This value
shouldn't be incremented in bind_to() method when internal reference
label is bound, because it is not decremented when the switch table is
generated (dd() method from Assemler class).

This patch fixes this problem. Regression test are also included for
mips and mips64 arch.

BUG=

Review-Url: https://codereview.chromium.org/2530143002
Cr-Commit-Position: refs/heads/master@{#41423}
This commit is contained in:
dusan.simicic 2016-12-01 05:03:00 -08:00 committed by Commit bot
parent 39d289f59e
commit d735f3ab12
6 changed files with 186 additions and 12 deletions

View File

@ -895,8 +895,7 @@ void Assembler::print(Label* L) {
} else {
PrintF("%d\n", instr);
}
next(&l, internal_reference_positions_.find(l.pos()) !=
internal_reference_positions_.end());
next(&l, is_internal_reference(&l));
}
} else {
PrintF("label in inconsistent state (pos = %d)\n", L->pos_);
@ -910,14 +909,15 @@ void Assembler::bind_to(Label* L, int pos) {
bool is_internal = false;
if (L->is_linked() && !trampoline_emitted_) {
unbound_labels_count_--;
next_buffer_check_ += kTrampolineSlotsSize;
if (!is_internal_reference(L)) {
next_buffer_check_ += kTrampolineSlotsSize;
}
}
while (L->is_linked()) {
int32_t fixup_pos = L->pos();
int32_t dist = pos - fixup_pos;
is_internal = internal_reference_positions_.find(fixup_pos) !=
internal_reference_positions_.end();
is_internal = is_internal_reference(L);
next(L, is_internal); // Call next before overwriting link with target at
// fixup_pos.
Instr instr = instr_at(fixup_pos);
@ -934,7 +934,6 @@ void Assembler::bind_to(Label* L, int pos) {
CHECK((trampoline_pos - fixup_pos) <= branch_offset);
target_at_put(fixup_pos, trampoline_pos, false);
fixup_pos = trampoline_pos;
dist = pos - fixup_pos;
}
target_at_put(fixup_pos, pos, false);
} else {

View File

@ -1452,6 +1452,10 @@ class Assembler : public AssemblerBase {
// Internal reference positions, required for unbounded internal reference
// labels.
std::set<int> internal_reference_positions_;
bool is_internal_reference(Label* L) {
return internal_reference_positions_.find(L->pos()) !=
internal_reference_positions_.end();
}
void EmittedCompactBranchInstruction() { prev_instr_compact_branch_ = true; }
void ClearCompactBranchState() { prev_instr_compact_branch_ = false; }

View File

@ -859,8 +859,7 @@ void Assembler::print(Label* L) {
} else {
PrintF("%d\n", instr);
}
next(&l, internal_reference_positions_.find(l.pos()) !=
internal_reference_positions_.end());
next(&l, is_internal_reference(&l));
}
} else {
PrintF("label in inconsistent state (pos = %d)\n", L->pos_);
@ -874,14 +873,15 @@ void Assembler::bind_to(Label* L, int pos) {
bool is_internal = false;
if (L->is_linked() && !trampoline_emitted_) {
unbound_labels_count_--;
next_buffer_check_ += kTrampolineSlotsSize;
if (!is_internal_reference(L)) {
next_buffer_check_ += kTrampolineSlotsSize;
}
}
while (L->is_linked()) {
int fixup_pos = L->pos();
int dist = pos - fixup_pos;
is_internal = internal_reference_positions_.find(fixup_pos) !=
internal_reference_positions_.end();
is_internal = is_internal_reference(L);
next(L, is_internal); // Call next before overwriting link with target at
// fixup_pos.
Instr instr = instr_at(fixup_pos);
@ -898,7 +898,6 @@ void Assembler::bind_to(Label* L, int pos) {
CHECK((trampoline_pos - fixup_pos) <= branch_offset);
target_at_put(fixup_pos, trampoline_pos, false);
fixup_pos = trampoline_pos;
dist = pos - fixup_pos;
}
target_at_put(fixup_pos, pos, false);
} else {

View File

@ -1503,6 +1503,10 @@ class Assembler : public AssemblerBase {
// Internal reference positions, required for unbounded internal reference
// labels.
std::set<int64_t> internal_reference_positions_;
bool is_internal_reference(Label* L) {
return internal_reference_positions_.find(L->pos()) !=
internal_reference_positions_.end();
}
void EmittedCompactBranchInstruction() { prev_instr_compact_branch_ = true; }
void ClearCompactBranchState() { prev_instr_compact_branch_ = false; }

View File

@ -282,6 +282,90 @@ TEST(jump_tables5) {
}
}
TEST(jump_tables6) {
// Similar to test-assembler-mips jump_tables1, with extra test for branch
// trampoline required after emission of the dd table (where trampolines are
// blocked). This test checks if number of really generated instructions is
// greater than number of counted instructions from code, as we are expecting
// generation of trampoline in this case (when number of kFillInstr
// instructions is close to 32K)
CcTest::InitializeVM();
Isolate* isolate = CcTest::i_isolate();
HandleScope scope(isolate);
MacroAssembler assembler(isolate, nullptr, 0,
v8::internal::CodeObjectRequired::kYes);
MacroAssembler* masm = &assembler;
const int kNumCases = 40;
const int kFillInstr = 32551;
const int kMaxBranchOffset = (1 << (18 - 1)) - 1;
const int kTrampolineSlotsSize = 4 * Instruction::kInstrSize;
const int kMaxOffsetForTrampolineStart =
kMaxBranchOffset - 16 * kTrampolineSlotsSize;
int values[kNumCases];
isolate->random_number_generator()->NextBytes(values, sizeof(values));
Label labels[kNumCases];
Label near_start, end, done;
__ Push(ra);
__ mov(v0, zero_reg);
int offs1 = masm->pc_offset();
int gen_insn = 0;
__ Branch(&end);
gen_insn += 2;
__ bind(&near_start);
// Generate slightly less than 32K instructions, which will soon require
// trampoline for branch distance fixup.
for (int i = 0; i < kFillInstr; ++i) {
__ addiu(v0, v0, 1);
}
gen_insn += kFillInstr;
__ GenerateSwitchTable(a0, kNumCases,
[&labels](size_t i) { return labels + i; });
gen_insn += (10 + kNumCases);
for (int i = 0; i < kNumCases; ++i) {
__ bind(&labels[i]);
__ li(v0, values[i]);
__ Branch(&done);
}
gen_insn += (4 * kNumCases);
// If offset from here to first branch instr is greater than max allowed
// offset for trampoline ...
CHECK_LT(kMaxOffsetForTrampolineStart, masm->pc_offset() - offs1);
// ... number of generated instructions must be greater then "gen_insn",
// as we are expecting trampoline generation
CHECK_LT(gen_insn, (masm->pc_offset() - offs1) / Instruction::kInstrSize);
__ bind(&done);
__ Pop(ra);
__ jr(ra);
__ nop();
__ bind(&end);
__ Branch(&near_start);
CodeDesc desc;
masm->GetCode(&desc);
Handle<Code> code = isolate->factory()->NewCode(
desc, Code::ComputeFlags(Code::STUB), Handle<Code>());
#ifdef OBJECT_PRINT
code->Print(std::cout);
#endif
F1 f = FUNCTION_CAST<F1>(code->entry());
for (int i = 0; i < kNumCases; ++i) {
int res =
reinterpret_cast<int>(CALL_GENERATED_CODE(isolate, f, i, 0, 0, 0, 0));
::printf("f(%d) = %d\n", i, res);
CHECK_EQ(values[i], res);
}
}
static uint32_t run_lsa(uint32_t rt, uint32_t rs, int8_t sa) {
Isolate* isolate = CcTest::i_isolate();

View File

@ -350,6 +350,90 @@ TEST(jump_tables5) {
}
}
TEST(jump_tables6) {
// Similar to test-assembler-mips jump_tables1, with extra test for branch
// trampoline required after emission of the dd table (where trampolines are
// blocked). This test checks if number of really generated instructions is
// greater than number of counted instructions from code, as we are expecting
// generation of trampoline in this case (when number of kFillInstr
// instructions is close to 32K)
CcTest::InitializeVM();
Isolate* isolate = CcTest::i_isolate();
HandleScope scope(isolate);
MacroAssembler assembler(isolate, nullptr, 0,
v8::internal::CodeObjectRequired::kYes);
MacroAssembler* masm = &assembler;
const int kNumCases = 40;
const int kFillInstr = 32551;
const int kMaxBranchOffset = (1 << (18 - 1)) - 1;
const int kTrampolineSlotsSize = 2 * Instruction::kInstrSize;
const int kMaxOffsetForTrampolineStart =
kMaxBranchOffset - 16 * kTrampolineSlotsSize;
int values[kNumCases];
isolate->random_number_generator()->NextBytes(values, sizeof(values));
Label labels[kNumCases];
Label near_start, end, done;
__ Push(ra);
__ mov(v0, zero_reg);
int offs1 = masm->pc_offset();
int gen_insn = 0;
__ Branch(&end);
gen_insn += 2;
__ bind(&near_start);
// Generate slightly less than 32K instructions, which will soon require
// trampoline for branch distance fixup.
for (int i = 0; i < kFillInstr; ++i) {
__ addiu(v0, v0, 1);
}
gen_insn += kFillInstr;
__ GenerateSwitchTable(a0, kNumCases,
[&labels](size_t i) { return labels + i; });
gen_insn += (11 + 2 * kNumCases);
for (int i = 0; i < kNumCases; ++i) {
__ bind(&labels[i]);
__ li(v0, values[i]);
__ Branch(&done);
}
gen_insn += (4 * kNumCases);
// If offset from here to first branch instr is greater than max allowed
// offset for trampoline ...
CHECK_LT(kMaxOffsetForTrampolineStart, masm->pc_offset() - offs1);
// ... number of generated instructions must be greater then "gen_insn",
// as we are expecting trampoline generation
CHECK_LT(gen_insn, (masm->pc_offset() - offs1) / Instruction::kInstrSize);
__ bind(&done);
__ Pop(ra);
__ jr(ra);
__ nop();
__ bind(&end);
__ Branch(&near_start);
CodeDesc desc;
masm->GetCode(&desc);
Handle<Code> code = isolate->factory()->NewCode(
desc, Code::ComputeFlags(Code::STUB), Handle<Code>());
#ifdef OBJECT_PRINT
code->Print(std::cout);
#endif
F1 f = FUNCTION_CAST<F1>(code->entry());
for (int i = 0; i < kNumCases; ++i) {
int64_t res = reinterpret_cast<int64_t>(
CALL_GENERATED_CODE(isolate, f, i, 0, 0, 0, 0));
::printf("f(%d) = %" PRId64 "\n", i, res);
CHECK_EQ(values[i], res);
}
}
static uint64_t run_lsa(uint32_t rt, uint32_t rs, int8_t sa) {
Isolate* isolate = CcTest::i_isolate();