[mips][codegen] Correct safepoint record and CheckTrampolinePool.

On mips platform, call operations may be followed by trampolines, which
leading to wrong information of the call instr's location in safepoint.
This CL fix it by adding a last_call_pc_ to record the location.

Besides, this CL also fix a bind operation in CheckTrampolinePool, which
may try to use trampoline before it's emission.

Change-Id: Ic0cbdb93afffa60a7389ee8177c381087fcaf52e
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2095645
Commit-Queue: Zhao Jiazhong <zhaojiazhong-hf@loongson.cn>
Reviewed-by: Georg Neis <neis@chromium.org>
Cr-Commit-Position: refs/heads/master@{#68600}
This commit is contained in:
Zhao Jiazhong 2020-06-28 22:16:47 -04:00 committed by Commit Bot
parent f0693ac6a0
commit d469c731f0
11 changed files with 221 additions and 5 deletions

View File

@ -257,6 +257,15 @@ class V8_EXPORT_PRIVATE AssemblerBase : public Malloced {
int pc_offset() const { return static_cast<int>(pc_ - buffer_start_); }
int pc_offset_for_safepoint() {
#if defined(V8_TARGET_ARCH_MIPS) || defined(V8_TARGET_ARCH_MIPS64)
// Mips needs it's own implementation to avoid trampoline's influence.
UNREACHABLE();
#else
return pc_offset();
#endif
}
byte* buffer_start() const { return buffer_->start(); }
int buffer_size() const { return buffer_->size(); }
int instruction_size() const { return pc_offset(); }

View File

@ -3655,8 +3655,12 @@ void Assembler::CheckTrampolinePool() {
}
}
}
bind(&after_pool);
// If unbound_labels_count_ is big enough, label after_pool will
// need a trampoline too, so we must create the trampoline before
// the bind operation to make sure function 'bind' can get this
// information.
trampoline_ = Trampoline(pool_start, unbound_labels_count_);
bind(&after_pool);
trampoline_emitted_ = true;
// As we are only going to emit trampoline once, we need to prevent any
@ -3797,6 +3801,7 @@ void Assembler::GenPCRelativeJumpAndLink(Register t, int32_t imm32,
addu(t, ra, t);
jalr(t);
if (bdslot == PROTECT) nop();
set_last_call_pc_(pc_);
}
UseScratchRegisterScope::UseScratchRegisterScope(Assembler* assembler)

View File

@ -170,6 +170,35 @@ class V8_EXPORT_PRIVATE Assembler : public AssemblerBase {
// Unused on this architecture.
void MaybeEmitOutOfLineConstantPool() {}
// Mips uses BlockTrampolinePool to prevent generating trampoline inside a
// continuous instruction block. For Call instrution, it prevents generating
// trampoline between jalr and delay slot instruction. In the destructor of
// BlockTrampolinePool, it must check if it needs to generate trampoline
// immediately, if it does not do this, the branch range will go beyond the
// max branch offset, that means the pc_offset after call CheckTrampolinePool
// may be not the Call instruction's location. So we use last_call_pc here for
// safepoint record.
int pc_offset_for_safepoint() {
#ifdef DEBUG
Instr instr1 =
instr_at(static_cast<int>(last_call_pc_ - buffer_start_ - kInstrSize));
Instr instr2 = instr_at(
static_cast<int>(last_call_pc_ - buffer_start_ - kInstrSize * 2));
if (GetOpcodeField(instr1) != SPECIAL) { // instr1 == jialc.
DCHECK(IsMipsArchVariant(kMips32r6) && GetOpcodeField(instr1) == POP76 &&
GetRs(instr1) == 0);
} else {
if (GetFunctionField(instr1) == SLL) { // instr1 == nop, instr2 == jalr.
DCHECK(GetOpcodeField(instr2) == SPECIAL &&
GetFunctionField(instr2) == JALR);
} else { // instr1 == jalr.
DCHECK(GetFunctionField(instr1) == JALR);
}
}
#endif
return static_cast<int>(last_call_pc_ - buffer_start_);
}
// Label operations & relative jumps (PPUM Appendix D).
//
// Takes a branch opcode (cc) and a label (L) and generates
@ -1593,6 +1622,8 @@ class V8_EXPORT_PRIVATE Assembler : public AssemblerBase {
void GenPCRelativeJumpAndLink(Register t, int32_t imm32,
RelocInfo::Mode rmode, BranchDelaySlot bdslot);
void set_last_call_pc_(byte* pc) { last_call_pc_ = pc; }
private:
// Avoid overflows for displacements etc.
static const int kMaximalBufferSize = 512 * MB;
@ -1856,6 +1887,11 @@ class V8_EXPORT_PRIVATE Assembler : public AssemblerBase {
Trampoline trampoline_;
bool internal_trampoline_exception_;
// Keep track of the last Call's position to ensure that safepoint can get the
// correct information even if there is a trampoline immediately after the
// Call.
byte* last_call_pc_;
private:
void AllocateAndInstallRequestedHeapObjects(Isolate* isolate);

View File

@ -3906,6 +3906,7 @@ void TurboAssembler::Call(Register target, int16_t offset, Condition cond,
// Emit a nop in the branch delay slot if required.
if (bd == PROTECT) nop();
}
set_last_call_pc_(pc_);
}
// Note: To call gcc-compiled C code on mips, you must call through t9.
@ -3938,6 +3939,7 @@ void TurboAssembler::Call(Register target, Register base, int16_t offset,
// Emit a nop in the branch delay slot if required.
if (bd == PROTECT) nop();
}
set_last_call_pc_(pc_);
}
void TurboAssembler::Call(Address target, RelocInfo::Mode rmode, Condition cond,

View File

@ -3859,8 +3859,12 @@ void Assembler::CheckTrampolinePool() {
}
}
nop();
bind(&after_pool);
// If unbound_labels_count_ is big enough, label after_pool will
// need a trampoline too, so we must create the trampoline before
// the bind operation to make sure function 'bind' can get this
// information.
trampoline_ = Trampoline(pool_start, unbound_labels_count_);
bind(&after_pool);
trampoline_emitted_ = true;
// As we are only going to emit trampoline once, we need to prevent any

View File

@ -168,6 +168,35 @@ class V8_EXPORT_PRIVATE Assembler : public AssemblerBase {
// Unused on this architecture.
void MaybeEmitOutOfLineConstantPool() {}
// Mips uses BlockTrampolinePool to prevent generating trampoline inside a
// continuous instruction block. For Call instruction, it prevents generating
// trampoline between jalr and delay slot instruction. In the destructor of
// BlockTrampolinePool, it must check if it needs to generate trampoline
// immediately, if it does not do this, the branch range will go beyond the
// max branch offset, that means the pc_offset after call CheckTrampolinePool
// may be not the Call instruction's location. So we use last_call_pc here for
// safepoint record.
int pc_offset_for_safepoint() {
#ifdef DEBUG
Instr instr1 =
instr_at(static_cast<int>(last_call_pc_ - buffer_start_ - kInstrSize));
Instr instr2 = instr_at(
static_cast<int>(last_call_pc_ - buffer_start_ - kInstrSize * 2));
if (GetOpcodeField(instr1) != SPECIAL) { // instr1 == jialc.
DCHECK((kArchVariant == kMips64r6) && GetOpcodeField(instr1) == POP76 &&
GetRs(instr1) == 0);
} else {
if (GetFunctionField(instr1) == SLL) { // instr1 == nop, instr2 == jalr.
DCHECK(GetOpcodeField(instr2) == SPECIAL &&
GetFunctionField(instr2) == JALR);
} else { // instr1 == jalr.
DCHECK(GetFunctionField(instr1) == JALR);
}
}
#endif
return static_cast<int>(last_call_pc_ - buffer_start_);
}
// Label operations & relative jumps (PPUM Appendix D).
//
// Takes a branch opcode (cc) and a label (L) and generates
@ -1629,6 +1658,8 @@ class V8_EXPORT_PRIVATE Assembler : public AssemblerBase {
}
}
void set_last_call_pc_(byte* pc) { last_call_pc_ = pc; }
private:
// Avoid overflows for displacements etc.
static const int kMaximalBufferSize = 512 * MB;
@ -1882,6 +1913,11 @@ class V8_EXPORT_PRIVATE Assembler : public AssemblerBase {
Trampoline trampoline_;
bool internal_trampoline_exception_;
// Keep track of the last Call's position to ensure that safepoint can get the
// correct information even if there is a trampoline immediately after the
// Call.
byte* last_call_pc_;
RegList scratch_register_list_;
private:

View File

@ -4235,6 +4235,7 @@ void TurboAssembler::Call(Register target, Condition cond, Register rs,
// Emit a nop in the branch delay slot if required.
if (bd == PROTECT) nop();
}
set_last_call_pc_(pc_);
}
void MacroAssembler::JumpIfIsInRange(Register value, unsigned lower_limit,

View File

@ -90,7 +90,7 @@ void SafepointTable::PrintBits(std::ostream& os, // NOLINT
Safepoint SafepointTableBuilder::DefineSafepoint(
Assembler* assembler, Safepoint::DeoptMode deopt_mode) {
deoptimization_info_.push_back(
DeoptimizationInfo(zone_, assembler->pc_offset()));
DeoptimizationInfo(zone_, assembler->pc_offset_for_safepoint()));
DeoptimizationInfo& new_info = deoptimization_info_.back();
return Safepoint(new_info.indexes);
}

View File

@ -981,7 +981,8 @@ void CodeGenerator::RecordCallPosition(Instruction* instr) {
InstructionOperandConverter i(this, instr);
RpoNumber handler_rpo = i.InputRpo(instr->InputCount() - 1);
DCHECK(instructions()->InstructionBlockAt(handler_rpo)->IsHandler());
handlers_.push_back({GetLabel(handler_rpo), tasm()->pc_offset()});
handlers_.push_back(
{GetLabel(handler_rpo), tasm()->pc_offset_for_safepoint()});
}
if (needs_frame_state) {
@ -991,7 +992,7 @@ void CodeGenerator::RecordCallPosition(Instruction* instr) {
size_t frame_state_offset = 2;
FrameStateDescriptor* descriptor =
GetDeoptimizationEntry(instr, frame_state_offset).descriptor();
int pc_offset = tasm()->pc_offset();
int pc_offset = tasm()->pc_offset_for_safepoint();
BuildTranslation(instr, pc_offset, frame_state_offset,
descriptor->state_combine());
}

View File

@ -5437,6 +5437,67 @@ TEST(Trampoline) {
CHECK_EQ(0, res);
}
TEST(Trampoline_with_massive_unbound_labels) {
CcTest::InitializeVM();
Isolate* isolate = CcTest::i_isolate();
HandleScope scope(isolate);
MacroAssembler assm(isolate, v8::internal::CodeObjectRequired::kYes);
const int kNumSlots =
TurboAssembler::kMaxBranchOffset / TurboAssembler::kTrampolineSlotsSize;
Label labels[kNumSlots];
{
TurboAssembler::BlockTrampolinePoolScope block_trampoline_pool(&assm);
for (int i = 0; i < kNumSlots; i++) {
__ Branch(&labels[i]);
}
}
__ bind(&labels[0]);
}
static void DummyFunction(Object result) {}
TEST(Call_with_trampoline) {
CcTest::InitializeVM();
Isolate* isolate = CcTest::i_isolate();
HandleScope scope(isolate);
MacroAssembler assm(isolate, v8::internal::CodeObjectRequired::kYes);
int next_buffer_check_ = FLAG_force_long_branches
? kMaxInt
: TurboAssembler::kMaxBranchOffset -
TurboAssembler::kTrampolineSlotsSize * 16;
Label done;
__ Branch(&done);
next_buffer_check_ -= TurboAssembler::kTrampolineSlotsSize;
int num_nops = (next_buffer_check_ - __ pc_offset()) / kInstrSize - 1;
for (int i = 0; i < num_nops; i++) {
__ nop();
}
int pc_offset_before = __ pc_offset();
{
// There should be a trampoline after this Call
__ Call(FUNCTION_ADDR(DummyFunction), RelocInfo::RUNTIME_ENTRY);
}
int pc_offset_after = __ pc_offset();
int last_call_pc = __ pc_offset_for_safepoint();
// Without trampoline, the Call emits no more than 6 instructions, otherwise
// more than 6 instructions will be generated.
int num_instrs = 6;
// pc_offset_after records the offset after trampoline.
CHECK_GT(pc_offset_after - pc_offset_before, num_instrs * kInstrSize);
// last_call_pc records the offset before trampoline.
CHECK_LE(last_call_pc - pc_offset_before, num_instrs * kInstrSize);
__ bind(&done);
}
template <class T>
struct TestCaseMaddMsub {
T fr, fs, ft, fd_add, fd_sub;

View File

@ -6120,6 +6120,67 @@ TEST(Trampoline) {
CHECK_EQ(0, res);
}
TEST(Trampoline_with_massive_unbound_labels) {
CcTest::InitializeVM();
Isolate* isolate = CcTest::i_isolate();
HandleScope scope(isolate);
MacroAssembler assm(isolate, v8::internal::CodeObjectRequired::kYes);
const int kNumSlots =
TurboAssembler::kMaxBranchOffset / TurboAssembler::kTrampolineSlotsSize;
Label labels[kNumSlots];
{
TurboAssembler::BlockTrampolinePoolScope block_trampoline_pool(&assm);
for (int i = 0; i < kNumSlots; i++) {
__ Branch(&labels[i]);
}
}
__ bind(&labels[0]);
}
static void DummyFunction(Object result) {}
TEST(Call_with_trampoline) {
CcTest::InitializeVM();
Isolate* isolate = CcTest::i_isolate();
HandleScope scope(isolate);
MacroAssembler assm(isolate, v8::internal::CodeObjectRequired::kYes);
int next_buffer_check_ = FLAG_force_long_branches
? kMaxInt
: TurboAssembler::kMaxBranchOffset -
TurboAssembler::kTrampolineSlotsSize * 16;
Label done;
__ Branch(&done);
next_buffer_check_ -= TurboAssembler::kTrampolineSlotsSize;
int num_nops = (next_buffer_check_ - __ pc_offset()) / kInstrSize - 1;
for (int i = 0; i < num_nops; i++) {
__ nop();
}
int pc_offset_before = __ pc_offset();
{
// There should be a trampoline after this Call
__ Call(FUNCTION_ADDR(DummyFunction), RelocInfo::RUNTIME_ENTRY);
}
int pc_offset_after = __ pc_offset();
int last_call_pc = __ pc_offset_for_safepoint();
// Without trampoline, the Call emits no more than 8 instructions, otherwise
// more than 8 instructions will be generated.
int num_instrs = 8;
// pc_offset_after records the offset after trampoline.
CHECK_GT(pc_offset_after - pc_offset_before, num_instrs * kInstrSize);
// last_call_pc records the offset before trampoline.
CHECK_LE(last_call_pc - pc_offset_before, num_instrs * kInstrSize);
__ bind(&done);
}
template <class T>
struct TestCaseMaddMsub {
T fr, fs, ft, fd_add, fd_sub;