Commit d469c731 authored by Zhao Jiazhong's avatar Zhao Jiazhong Committed by Commit Bot

[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: 's avatarGeorg Neis <neis@chromium.org>
Cr-Commit-Position: refs/heads/master@{#68600}
parent f0693ac6
......@@ -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(); }
......
......@@ -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)
......
......@@ -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);
......
......@@ -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,
......
......@@ -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
......
......@@ -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:
......
......@@ -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,
......
......@@ -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);
}
......
......@@ -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());
}
......
......@@ -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;
......
......@@ -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;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment