Commit 3573d3cb authored by balazs.kilvady's avatar balazs.kilvady Committed by Commit bot

MIPS: r6 compact branch optimization.

BUG=

Review URL: https://codereview.chromium.org/1396133002

Cr-Commit-Position: refs/heads/master@{#31761}
parent 05947816
...@@ -879,8 +879,6 @@ void PatchInlinedSmiCode(Address address, InlinedSmiCheck check) { ...@@ -879,8 +879,6 @@ void PatchInlinedSmiCode(Address address, InlinedSmiCheck check) {
Address patch_address = Address patch_address =
andi_instruction_address - delta * Instruction::kInstrSize; andi_instruction_address - delta * Instruction::kInstrSize;
Instr instr_at_patch = Assembler::instr_at(patch_address); Instr instr_at_patch = Assembler::instr_at(patch_address);
Instr branch_instr =
Assembler::instr_at(patch_address + Instruction::kInstrSize);
// This is patching a conditional "jump if not smi/jump if smi" site. // This is patching a conditional "jump if not smi/jump if smi" site.
// Enabling by changing from // Enabling by changing from
// andi at, rx, 0 // andi at, rx, 0
...@@ -900,13 +898,44 @@ void PatchInlinedSmiCode(Address address, InlinedSmiCheck check) { ...@@ -900,13 +898,44 @@ void PatchInlinedSmiCode(Address address, InlinedSmiCheck check) {
DCHECK(Assembler::IsAndImmediate(instr_at_patch)); DCHECK(Assembler::IsAndImmediate(instr_at_patch));
patcher.masm()->andi(at, reg, 0); patcher.masm()->andi(at, reg, 0);
} }
Instr branch_instr =
Assembler::instr_at(patch_address + Instruction::kInstrSize);
DCHECK(Assembler::IsBranch(branch_instr)); DCHECK(Assembler::IsBranch(branch_instr));
if (Assembler::IsBeq(branch_instr)) {
patcher.ChangeBranchCondition(ne); uint32_t opcode = Assembler::GetOpcodeField(branch_instr);
} else { // Currently only the 'eq' and 'ne' cond values are supported and the simple
DCHECK(Assembler::IsBne(branch_instr)); // branch instructions and their r6 variants (with opcode being the branch
patcher.ChangeBranchCondition(eq); // type). There are some special cases (see Assembler::IsBranch()) so
// extending this would be tricky.
DCHECK(opcode == BEQ || // BEQ
opcode == BNE || // BNE
opcode == POP10 || // BEQC
opcode == POP30 || // BNEC
opcode == POP66 || // BEQZC
opcode == POP76); // BNEZC
switch (opcode) {
case BEQ:
opcode = BNE; // change BEQ to BNE.
break;
case POP10:
opcode = POP30; // change BEQC to BNEC.
break;
case POP66:
opcode = POP76; // change BEQZC to BNEZC.
break;
case BNE:
opcode = BEQ; // change BNE to BEQ.
break;
case POP30:
opcode = POP10; // change BNEC to BEQC.
break;
case POP76:
opcode = POP66; // change BNEZC to BEQZC.
break;
default:
UNIMPLEMENTED();
} }
patcher.ChangeBranchCondition(branch_instr, opcode);
} }
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
......
...@@ -445,12 +445,24 @@ void Assembler::CheckTrampolinePoolQuick(int extra_instructions) { ...@@ -445,12 +445,24 @@ void Assembler::CheckTrampolinePoolQuick(int extra_instructions) {
} }
void Assembler::emit(Instr x) { void Assembler::emit(Instr x, CompactBranchType is_compact_branch) {
if (!is_buffer_growth_blocked()) { if (!is_buffer_growth_blocked()) {
CheckBuffer(); CheckBuffer();
} }
if (IsPrevInstrCompactBranch()) {
if (Instruction::IsForbiddenAfterBranchInstr(x)) {
// Nop instruction to preceed a CTI in forbidden slot:
Instr nop = SPECIAL | SLL;
*reinterpret_cast<Instr*>(pc_) = nop;
pc_ += kInstrSize;
}
ClearCompactBranchState();
}
*reinterpret_cast<Instr*>(pc_) = x; *reinterpret_cast<Instr*>(pc_) = x;
pc_ += kInstrSize; pc_ += kInstrSize;
if (is_compact_branch == CompactBranchType::COMPACT_BRANCH) {
EmittedCompactBranchInstruction();
}
CheckTrampolinePoolQuick(); CheckTrampolinePoolQuick();
} }
......
...@@ -285,6 +285,10 @@ Assembler::Assembler(Isolate* isolate, void* buffer, int buffer_size) ...@@ -285,6 +285,10 @@ Assembler::Assembler(Isolate* isolate, void* buffer, int buffer_size)
void Assembler::GetCode(CodeDesc* desc) { void Assembler::GetCode(CodeDesc* desc) {
if (IsPrevInstrCompactBranch()) {
nop();
ClearCompactBranchState();
}
DCHECK(pc_ <= reloc_info_writer.pos()); // No overlap. DCHECK(pc_ <= reloc_info_writer.pos()); // No overlap.
// Set up code descriptor. // Set up code descriptor.
desc->buffer = buffer_; desc->buffer = buffer_;
...@@ -297,6 +301,10 @@ void Assembler::GetCode(CodeDesc* desc) { ...@@ -297,6 +301,10 @@ void Assembler::GetCode(CodeDesc* desc) {
void Assembler::Align(int m) { void Assembler::Align(int m) {
DCHECK(m >= 4 && base::bits::IsPowerOfTwo32(m)); DCHECK(m >= 4 && base::bits::IsPowerOfTwo32(m));
if (IsPrevInstrCompactBranch()) {
nop();
ClearCompactBranchState();
}
while ((pc_offset() & (m - 1)) != 0) { while ((pc_offset() & (m - 1)) != 0) {
nop(); nop();
} }
...@@ -453,19 +461,38 @@ bool Assembler::IsBranch(Instr instr) { ...@@ -453,19 +461,38 @@ bool Assembler::IsBranch(Instr instr) {
uint32_t rt_field = GetRtField(instr); uint32_t rt_field = GetRtField(instr);
uint32_t rs_field = GetRsField(instr); uint32_t rs_field = GetRsField(instr);
// Checks if the instruction is a branch. // Checks if the instruction is a branch.
return opcode == BEQ || bool isBranch =
opcode == BNE || opcode == BEQ || opcode == BNE || opcode == BLEZ || opcode == BGTZ ||
opcode == BLEZ || opcode == BEQL || opcode == BNEL || opcode == BLEZL || opcode == BGTZL ||
opcode == BGTZ ||
opcode == BEQL ||
opcode == BNEL ||
opcode == BLEZL ||
opcode == BGTZL ||
(opcode == REGIMM && (rt_field == BLTZ || rt_field == BGEZ || (opcode == REGIMM && (rt_field == BLTZ || rt_field == BGEZ ||
rt_field == BLTZAL || rt_field == BGEZAL)) || rt_field == BLTZAL || rt_field == BGEZAL)) ||
(opcode == COP1 && rs_field == BC1) || // Coprocessor branch. (opcode == COP1 && rs_field == BC1) || // Coprocessor branch.
(opcode == COP1 && rs_field == BC1EQZ) || (opcode == COP1 && rs_field == BC1EQZ) ||
(opcode == COP1 && rs_field == BC1NEZ); (opcode == COP1 && rs_field == BC1NEZ);
if (!isBranch && IsMipsArchVariant(kMips32r6)) {
// All the 3 variants of POP10 (BOVC, BEQC, BEQZALC) and
// POP30 (BNVC, BNEC, BNEZALC) are branch ops.
isBranch |= opcode == POP10 || opcode == POP30 || opcode == BC ||
opcode == BALC ||
(opcode == POP66 && rs_field != 0) || // BEQZC
(opcode == POP76 && rs_field != 0); // BNEZC
}
return isBranch;
}
bool Assembler::IsBc(Instr instr) {
uint32_t opcode = GetOpcodeField(instr);
// Checks if the instruction is a BC or BALC.
return opcode == BC || opcode == BALC;
}
bool Assembler::IsBzc(Instr instr) {
uint32_t opcode = GetOpcodeField(instr);
// Checks if the instruction is BEQZC or BNEZC.
return (opcode == POP66 && GetRsField(instr) != 0) ||
(opcode == POP76 && GetRsField(instr) != 0);
} }
...@@ -485,6 +512,34 @@ bool Assembler::IsBne(Instr instr) { ...@@ -485,6 +512,34 @@ bool Assembler::IsBne(Instr instr) {
} }
bool Assembler::IsBeqzc(Instr instr) {
uint32_t opcode = GetOpcodeField(instr);
return opcode == POP66 && GetRsField(instr) != 0;
}
bool Assembler::IsBnezc(Instr instr) {
uint32_t opcode = GetOpcodeField(instr);
return opcode == POP76 && GetRsField(instr) != 0;
}
bool Assembler::IsBeqc(Instr instr) {
uint32_t opcode = GetOpcodeField(instr);
uint32_t rs = GetRsField(instr);
uint32_t rt = GetRtField(instr);
return opcode == POP10 && rs != 0 && rs < rt; // && rt != 0
}
bool Assembler::IsBnec(Instr instr) {
uint32_t opcode = GetOpcodeField(instr);
uint32_t rs = GetRsField(instr);
uint32_t rt = GetRtField(instr);
return opcode == POP30 && rs != 0 && rs < rt; // && rt != 0
}
bool Assembler::IsJump(Instr instr) { bool Assembler::IsJump(Instr instr) {
uint32_t opcode = GetOpcodeField(instr); uint32_t opcode = GetOpcodeField(instr);
uint32_t rt_field = GetRtField(instr); uint32_t rt_field = GetRtField(instr);
...@@ -570,7 +625,7 @@ int32_t Assembler::GetBranchOffset(Instr instr) { ...@@ -570,7 +625,7 @@ int32_t Assembler::GetBranchOffset(Instr instr) {
bool Assembler::IsLw(Instr instr) { bool Assembler::IsLw(Instr instr) {
return ((instr & kOpcodeMask) == LW); return (static_cast<uint32_t>(instr & kOpcodeMask) == LW);
} }
...@@ -592,7 +647,7 @@ Instr Assembler::SetLwOffset(Instr instr, int16_t offset) { ...@@ -592,7 +647,7 @@ Instr Assembler::SetLwOffset(Instr instr, int16_t offset) {
bool Assembler::IsSw(Instr instr) { bool Assembler::IsSw(Instr instr) {
return ((instr & kOpcodeMask) == SW); return (static_cast<uint32_t>(instr & kOpcodeMask) == SW);
} }
...@@ -618,6 +673,36 @@ bool Assembler::IsAndImmediate(Instr instr) { ...@@ -618,6 +673,36 @@ bool Assembler::IsAndImmediate(Instr instr) {
} }
static Assembler::OffsetSize OffsetSizeInBits(Instr instr) {
if (IsMipsArchVariant(kMips32r6)) {
if (Assembler::IsBc(instr)) {
return Assembler::OffsetSize::kOffset26;
} else if (Assembler::IsBzc(instr)) {
return Assembler::OffsetSize::kOffset21;
}
}
return Assembler::OffsetSize::kOffset16;
}
static inline int32_t AddBranchOffset(int pos, Instr instr) {
int bits = OffsetSizeInBits(instr);
const int32_t mask = (1 << bits) - 1;
bits = 32 - bits;
// Do NOT change this to <<2. We rely on arithmetic shifts here, assuming
// the compiler uses arithmetic shifts for signed integers.
int32_t imm = ((instr & mask) << bits) >> (bits - 2);
if (imm == kEndOfChain) {
// EndOfChain sentinel is returned directly, not relative to pc or pos.
return kEndOfChain;
} else {
return pos + Assembler::kBranchPCOffset + imm;
}
}
int Assembler::target_at(int pos, bool is_internal) { int Assembler::target_at(int pos, bool is_internal) {
Instr instr = instr_at(pos); Instr instr = instr_at(pos);
if (is_internal) { if (is_internal) {
...@@ -641,18 +726,9 @@ int Assembler::target_at(int pos, bool is_internal) { ...@@ -641,18 +726,9 @@ int Assembler::target_at(int pos, bool is_internal) {
} }
// Check we have a branch or jump instruction. // Check we have a branch or jump instruction.
DCHECK(IsBranch(instr) || IsLui(instr)); DCHECK(IsBranch(instr) || IsLui(instr));
// Do NOT change this to <<2. We rely on arithmetic shifts here, assuming
// the compiler uses arithmetic shifts for signed integers.
if (IsBranch(instr)) { if (IsBranch(instr)) {
int32_t imm18 = ((instr & static_cast<int32_t>(kImm16Mask)) << 16) >> 14; return AddBranchOffset(pos, instr);
} else {
if (imm18 == kEndOfChain) {
// EndOfChain sentinel is returned directly, not relative to pc or pos.
return kEndOfChain;
} else {
return pos + kBranchPCOffset + imm18;
}
} else if (IsLui(instr)) {
Instr instr_lui = instr_at(pos + 0 * Assembler::kInstrSize); Instr instr_lui = instr_at(pos + 0 * Assembler::kInstrSize);
Instr instr_ori = instr_at(pos + 1 * Assembler::kInstrSize); Instr instr_ori = instr_at(pos + 1 * Assembler::kInstrSize);
DCHECK(IsOri(instr_ori)); DCHECK(IsOri(instr_ori));
...@@ -668,10 +744,23 @@ int Assembler::target_at(int pos, bool is_internal) { ...@@ -668,10 +744,23 @@ int Assembler::target_at(int pos, bool is_internal) {
DCHECK(pos > delta); DCHECK(pos > delta);
return pos - delta; return pos - delta;
} }
} else {
UNREACHABLE();
return 0;
} }
return 0;
}
static inline Instr SetBranchOffset(int32_t pos, int32_t target_pos,
Instr instr) {
int32_t bits = OffsetSizeInBits(instr);
int32_t imm = target_pos - (pos + Assembler::kBranchPCOffset);
DCHECK((imm & 3) == 0);
imm >>= 2;
const int32_t mask = (1 << bits) - 1;
instr &= ~mask;
DCHECK(is_intn(imm, bits));
return instr | (imm & mask);
} }
...@@ -694,15 +783,9 @@ void Assembler::target_at_put(int32_t pos, int32_t target_pos, ...@@ -694,15 +783,9 @@ void Assembler::target_at_put(int32_t pos, int32_t target_pos,
DCHECK(IsBranch(instr) || IsLui(instr)); DCHECK(IsBranch(instr) || IsLui(instr));
if (IsBranch(instr)) { if (IsBranch(instr)) {
int32_t imm18 = target_pos - (pos + kBranchPCOffset); instr = SetBranchOffset(pos, target_pos, instr);
DCHECK((imm18 & 3) == 0); instr_at_put(pos, instr);
} else {
instr &= ~kImm16Mask;
int32_t imm16 = imm18 >> 2;
DCHECK(is_int16(imm16));
instr_at_put(pos, instr | (imm16 & kImm16Mask));
} else if (IsLui(instr)) {
Instr instr_lui = instr_at(pos + 0 * Assembler::kInstrSize); Instr instr_lui = instr_at(pos + 0 * Assembler::kInstrSize);
Instr instr_ori = instr_at(pos + 1 * Assembler::kInstrSize); Instr instr_ori = instr_at(pos + 1 * Assembler::kInstrSize);
DCHECK(IsOri(instr_ori)); DCHECK(IsOri(instr_ori));
...@@ -716,8 +799,6 @@ void Assembler::target_at_put(int32_t pos, int32_t target_pos, ...@@ -716,8 +799,6 @@ void Assembler::target_at_put(int32_t pos, int32_t target_pos,
instr_lui | ((imm & kHiMask) >> kLuiShift)); instr_lui | ((imm & kHiMask) >> kLuiShift));
instr_at_put(pos + 1 * Assembler::kInstrSize, instr_at_put(pos + 1 * Assembler::kInstrSize,
instr_ori | (imm & kImm16Mask)); instr_ori | (imm & kImm16Mask));
} else {
UNREACHABLE();
} }
} }
...@@ -766,20 +847,22 @@ void Assembler::bind_to(Label* L, int pos) { ...@@ -766,20 +847,22 @@ void Assembler::bind_to(Label* L, int pos) {
Instr instr = instr_at(fixup_pos); Instr instr = instr_at(fixup_pos);
if (is_internal) { if (is_internal) {
target_at_put(fixup_pos, pos, is_internal); target_at_put(fixup_pos, pos, is_internal);
} else if (!is_internal && IsBranch(instr)) { } else {
if (dist > kMaxBranchOffset) { if (IsBranch(instr)) {
if (trampoline_pos == kInvalidSlotPos) { if (dist > kMaxBranchOffset) {
trampoline_pos = get_trampoline_entry(fixup_pos); if (trampoline_pos == kInvalidSlotPos) {
CHECK(trampoline_pos != kInvalidSlotPos); trampoline_pos = get_trampoline_entry(fixup_pos);
CHECK(trampoline_pos != kInvalidSlotPos);
}
CHECK((trampoline_pos - fixup_pos) <= kMaxBranchOffset);
target_at_put(fixup_pos, trampoline_pos, false);
fixup_pos = trampoline_pos;
dist = pos - fixup_pos;
} }
CHECK((trampoline_pos - fixup_pos) <= kMaxBranchOffset); target_at_put(fixup_pos, pos, false);
target_at_put(fixup_pos, trampoline_pos, false); } else {
fixup_pos = trampoline_pos; target_at_put(fixup_pos, pos, false);
dist = pos - fixup_pos;
} }
target_at_put(fixup_pos, pos, false);
} else {
target_at_put(fixup_pos, pos, false);
} }
} }
L->bind_to(pos); L->bind_to(pos);
...@@ -810,10 +893,23 @@ void Assembler::next(Label* L, bool is_internal) { ...@@ -810,10 +893,23 @@ void Assembler::next(Label* L, bool is_internal) {
bool Assembler::is_near(Label* L) { bool Assembler::is_near(Label* L) {
if (L->is_bound()) { DCHECK(L->is_bound());
return ((pc_offset() - L->pos()) < kMaxBranchOffset - 4 * kInstrSize); return ((pc_offset() - L->pos()) < kMaxBranchOffset - 4 * kInstrSize);
} }
return false;
bool Assembler::is_near(Label* L, OffsetSize bits) {
if (L == nullptr || !L->is_bound()) return true;
return ((pc_offset() - L->pos()) <
(1 << (bits + 2 - 1)) - 1 - 5 * kInstrSize);
}
bool Assembler::is_near_branch(Label* L) {
DCHECK(L->is_bound());
int max_offset =
IsMipsArchVariant(kMips32r6) ? kMaxCompactBranchOffset : kMaxBranchOffset;
return pc_offset() - L->pos() < max_offset - 4 * kInstrSize;
} }
...@@ -904,49 +1000,56 @@ void Assembler::GenInstrRegister(Opcode opcode, ...@@ -904,49 +1000,56 @@ void Assembler::GenInstrRegister(Opcode opcode,
// Instructions with immediate value. // Instructions with immediate value.
// Registers are in the order of the instruction encoding, from left to right. // Registers are in the order of the instruction encoding, from left to right.
void Assembler::GenInstrImmediate(Opcode opcode, void Assembler::GenInstrImmediate(Opcode opcode, Register rs, Register rt,
Register rs, int32_t j,
Register rt, CompactBranchType is_compact_branch) {
int32_t j) {
DCHECK(rs.is_valid() && rt.is_valid() && (is_int16(j) || is_uint16(j))); DCHECK(rs.is_valid() && rt.is_valid() && (is_int16(j) || is_uint16(j)));
Instr instr = opcode | (rs.code() << kRsShift) | (rt.code() << kRtShift) Instr instr = opcode | (rs.code() << kRsShift) | (rt.code() << kRtShift)
| (j & kImm16Mask); | (j & kImm16Mask);
emit(instr); emit(instr, is_compact_branch);
} }
void Assembler::GenInstrImmediate(Opcode opcode, void Assembler::GenInstrImmediate(Opcode opcode, Register rs, SecondaryField SF,
Register rs, int32_t j,
SecondaryField SF, CompactBranchType is_compact_branch) {
int32_t j) {
DCHECK(rs.is_valid() && (is_int16(j) || is_uint16(j))); DCHECK(rs.is_valid() && (is_int16(j) || is_uint16(j)));
Instr instr = opcode | (rs.code() << kRsShift) | SF | (j & kImm16Mask); Instr instr = opcode | (rs.code() << kRsShift) | SF | (j & kImm16Mask);
emit(instr); emit(instr, is_compact_branch);
} }
void Assembler::GenInstrImmediate(Opcode opcode, void Assembler::GenInstrImmediate(Opcode opcode, Register rs, FPURegister ft,
Register rs, int32_t j,
FPURegister ft, CompactBranchType is_compact_branch) {
int32_t j) {
DCHECK(rs.is_valid() && ft.is_valid() && (is_int16(j) || is_uint16(j))); DCHECK(rs.is_valid() && ft.is_valid() && (is_int16(j) || is_uint16(j)));
Instr instr = opcode | (rs.code() << kRsShift) | (ft.code() << kFtShift) Instr instr = opcode | (rs.code() << kRsShift) | (ft.code() << kFtShift)
| (j & kImm16Mask); | (j & kImm16Mask);
emit(instr); emit(instr, is_compact_branch);
} }
void Assembler::GenInstrImmediate(Opcode opcode, Register rs, int32_t j) { void Assembler::GenInstrImmediate(Opcode opcode, Register rs, int32_t offset21,
DCHECK(rs.is_valid() && (is_uint21(j))); CompactBranchType is_compact_branch) {
Instr instr = opcode | (rs.code() << kRsShift) | (j & kImm21Mask); DCHECK(rs.is_valid() && (is_int21(offset21)));
Instr instr = opcode | (rs.code() << kRsShift) | (offset21 & kImm21Mask);
emit(instr, is_compact_branch);
}
void Assembler::GenInstrImmediate(Opcode opcode, Register rs,
uint32_t offset21) {
DCHECK(rs.is_valid() && (is_uint21(offset21)));
Instr instr = opcode | (rs.code() << kRsShift) | (offset21 & kImm21Mask);
emit(instr); emit(instr);
} }
void Assembler::GenInstrImmediate(Opcode opcode, int32_t offset26) { void Assembler::GenInstrImmediate(Opcode opcode, int32_t offset26,
CompactBranchType is_compact_branch) {
DCHECK(is_int26(offset26)); DCHECK(is_int26(offset26));
Instr instr = opcode | (offset26 & kImm26Mask); Instr instr = opcode | (offset26 & kImm26Mask);
emit(instr); emit(instr, is_compact_branch);
} }
...@@ -999,17 +1102,18 @@ uint32_t Assembler::jump_address(Label* L) { ...@@ -999,17 +1102,18 @@ uint32_t Assembler::jump_address(Label* L) {
} }
int32_t Assembler::branch_offset(Label* L, bool jump_elimination_allowed) { int32_t Assembler::branch_offset_helper(Label* L, OffsetSize bits) {
int32_t target_pos; int32_t target_pos;
int32_t pad = IsPrevInstrCompactBranch() ? kInstrSize : 0;
if (L->is_bound()) { if (L->is_bound()) {
target_pos = L->pos(); target_pos = L->pos();
} else { } else {
if (L->is_linked()) { if (L->is_linked()) {
target_pos = L->pos(); target_pos = L->pos();
L->link_to(pc_offset()); L->link_to(pc_offset() + pad);
} else { } else {
L->link_to(pc_offset()); L->link_to(pc_offset() + pad);
if (!trampoline_emitted_) { if (!trampoline_emitted_) {
unbound_labels_count_++; unbound_labels_count_++;
next_buffer_check_ -= kTrampolineSlotsSize; next_buffer_check_ -= kTrampolineSlotsSize;
...@@ -1018,91 +1122,9 @@ int32_t Assembler::branch_offset(Label* L, bool jump_elimination_allowed) { ...@@ -1018,91 +1122,9 @@ int32_t Assembler::branch_offset(Label* L, bool jump_elimination_allowed) {
} }
} }
int32_t offset = target_pos - (pc_offset() + kBranchPCOffset); int32_t offset = target_pos - (pc_offset() + kBranchPCOffset + pad);
DCHECK(is_intn(offset, bits + 2));
DCHECK((offset & 3) == 0); DCHECK((offset & 3) == 0);
DCHECK(is_int16(offset >> 2));
return offset;
}
int32_t Assembler::branch_offset_compact(Label* L,
bool jump_elimination_allowed) {
int32_t target_pos;
if (L->is_bound()) {
target_pos = L->pos();
} else {
if (L->is_linked()) {
target_pos = L->pos();
L->link_to(pc_offset());
} else {
L->link_to(pc_offset());
if (!trampoline_emitted_) {
unbound_labels_count_++;
next_buffer_check_ -= kTrampolineSlotsSize;
}
return kEndOfChain;
}
}
int32_t offset = target_pos - pc_offset();
DCHECK((offset & 3) == 0);
DCHECK(is_int16(offset >> 2));
return offset;
}
int32_t Assembler::branch_offset21(Label* L, bool jump_elimination_allowed) {
int32_t target_pos;
if (L->is_bound()) {
target_pos = L->pos();
} else {
if (L->is_linked()) {
target_pos = L->pos();
L->link_to(pc_offset());
} else {
L->link_to(pc_offset());
if (!trampoline_emitted_) {
unbound_labels_count_++;
next_buffer_check_ -= kTrampolineSlotsSize;
}
return kEndOfChain;
}
}
int32_t offset = target_pos - (pc_offset() + kBranchPCOffset);
DCHECK((offset & 3) == 0);
DCHECK(((offset >> 2) & 0xFFE00000) == 0); // Offset is 21bit width.
return offset;
}
int32_t Assembler::branch_offset21_compact(Label* L,
bool jump_elimination_allowed) {
int32_t target_pos;
if (L->is_bound()) {
target_pos = L->pos();
} else {
if (L->is_linked()) {
target_pos = L->pos();
L->link_to(pc_offset());
} else {
L->link_to(pc_offset());
if (!trampoline_emitted_) {
unbound_labels_count_++;
next_buffer_check_ -= kTrampolineSlotsSize;
}
return kEndOfChain;
}
}
int32_t offset = target_pos - pc_offset();
DCHECK((offset & 3) == 0);
DCHECK(((offset >> 2) & 0xFFe00000) == 0); // Offset is 21bit width.
return offset; return offset;
} }
...@@ -1149,14 +1171,14 @@ void Assembler::bal(int16_t offset) { ...@@ -1149,14 +1171,14 @@ void Assembler::bal(int16_t offset) {
void Assembler::bc(int32_t offset) { void Assembler::bc(int32_t offset) {
DCHECK(IsMipsArchVariant(kMips32r6)); DCHECK(IsMipsArchVariant(kMips32r6));
GenInstrImmediate(BC, offset); GenInstrImmediate(BC, offset, CompactBranchType::COMPACT_BRANCH);
} }
void Assembler::balc(int32_t offset) { void Assembler::balc(int32_t offset) {
DCHECK(IsMipsArchVariant(kMips32r6)); DCHECK(IsMipsArchVariant(kMips32r6));
positions_recorder()->WriteRecordedPositions(); positions_recorder()->WriteRecordedPositions();
GenInstrImmediate(BALC, offset); GenInstrImmediate(BALC, offset, CompactBranchType::COMPACT_BRANCH);
} }
...@@ -1177,7 +1199,7 @@ void Assembler::bgez(Register rs, int16_t offset) { ...@@ -1177,7 +1199,7 @@ void Assembler::bgez(Register rs, int16_t offset) {
void Assembler::bgezc(Register rt, int16_t offset) { void Assembler::bgezc(Register rt, int16_t offset) {
DCHECK(IsMipsArchVariant(kMips32r6)); DCHECK(IsMipsArchVariant(kMips32r6));
DCHECK(!(rt.is(zero_reg))); DCHECK(!(rt.is(zero_reg)));
GenInstrImmediate(BLEZL, rt, rt, offset); GenInstrImmediate(BLEZL, rt, rt, offset, CompactBranchType::COMPACT_BRANCH);
} }
...@@ -1186,7 +1208,7 @@ void Assembler::bgeuc(Register rs, Register rt, int16_t offset) { ...@@ -1186,7 +1208,7 @@ void Assembler::bgeuc(Register rs, Register rt, int16_t offset) {
DCHECK(!(rs.is(zero_reg))); DCHECK(!(rs.is(zero_reg)));
DCHECK(!(rt.is(zero_reg))); DCHECK(!(rt.is(zero_reg)));
DCHECK(rs.code() != rt.code()); DCHECK(rs.code() != rt.code());
GenInstrImmediate(BLEZ, rs, rt, offset); GenInstrImmediate(BLEZ, rs, rt, offset, CompactBranchType::COMPACT_BRANCH);
} }
...@@ -1195,7 +1217,7 @@ void Assembler::bgec(Register rs, Register rt, int16_t offset) { ...@@ -1195,7 +1217,7 @@ void Assembler::bgec(Register rs, Register rt, int16_t offset) {
DCHECK(!(rs.is(zero_reg))); DCHECK(!(rs.is(zero_reg)));
DCHECK(!(rt.is(zero_reg))); DCHECK(!(rt.is(zero_reg)));
DCHECK(rs.code() != rt.code()); DCHECK(rs.code() != rt.code());
GenInstrImmediate(BLEZL, rs, rt, offset); GenInstrImmediate(BLEZL, rs, rt, offset, CompactBranchType::COMPACT_BRANCH);
} }
...@@ -1218,7 +1240,8 @@ void Assembler::bgtz(Register rs, int16_t offset) { ...@@ -1218,7 +1240,8 @@ void Assembler::bgtz(Register rs, int16_t offset) {
void Assembler::bgtzc(Register rt, int16_t offset) { void Assembler::bgtzc(Register rt, int16_t offset) {
DCHECK(IsMipsArchVariant(kMips32r6)); DCHECK(IsMipsArchVariant(kMips32r6));
DCHECK(!(rt.is(zero_reg))); DCHECK(!(rt.is(zero_reg)));
GenInstrImmediate(BGTZL, zero_reg, rt, offset); GenInstrImmediate(BGTZL, zero_reg, rt, offset,
CompactBranchType::COMPACT_BRANCH);
} }
...@@ -1232,14 +1255,15 @@ void Assembler::blez(Register rs, int16_t offset) { ...@@ -1232,14 +1255,15 @@ void Assembler::blez(Register rs, int16_t offset) {
void Assembler::blezc(Register rt, int16_t offset) { void Assembler::blezc(Register rt, int16_t offset) {
DCHECK(IsMipsArchVariant(kMips32r6)); DCHECK(IsMipsArchVariant(kMips32r6));
DCHECK(!(rt.is(zero_reg))); DCHECK(!(rt.is(zero_reg)));
GenInstrImmediate(BLEZL, zero_reg, rt, offset); GenInstrImmediate(BLEZL, zero_reg, rt, offset,
CompactBranchType::COMPACT_BRANCH);
} }
void Assembler::bltzc(Register rt, int16_t offset) { void Assembler::bltzc(Register rt, int16_t offset) {
DCHECK(IsMipsArchVariant(kMips32r6)); DCHECK(IsMipsArchVariant(kMips32r6));
DCHECK(!(rt.is(zero_reg))); DCHECK(!rt.is(zero_reg));
GenInstrImmediate(BGTZL, rt, rt, offset); GenInstrImmediate(BGTZL, rt, rt, offset, CompactBranchType::COMPACT_BRANCH);
} }
...@@ -1248,16 +1272,16 @@ void Assembler::bltuc(Register rs, Register rt, int16_t offset) { ...@@ -1248,16 +1272,16 @@ void Assembler::bltuc(Register rs, Register rt, int16_t offset) {
DCHECK(!(rs.is(zero_reg))); DCHECK(!(rs.is(zero_reg)));
DCHECK(!(rt.is(zero_reg))); DCHECK(!(rt.is(zero_reg)));
DCHECK(rs.code() != rt.code()); DCHECK(rs.code() != rt.code());
GenInstrImmediate(BGTZ, rs, rt, offset); GenInstrImmediate(BGTZ, rs, rt, offset, CompactBranchType::COMPACT_BRANCH);
} }
void Assembler::bltc(Register rs, Register rt, int16_t offset) { void Assembler::bltc(Register rs, Register rt, int16_t offset) {
DCHECK(IsMipsArchVariant(kMips32r6)); DCHECK(IsMipsArchVariant(kMips32r6));
DCHECK(!(rs.is(zero_reg))); DCHECK(!rs.is(zero_reg));
DCHECK(!(rt.is(zero_reg))); DCHECK(!rt.is(zero_reg));
DCHECK(rs.code() != rt.code()); DCHECK(rs.code() != rt.code());
GenInstrImmediate(BGTZL, rs, rt, offset); GenInstrImmediate(BGTZL, rs, rt, offset, CompactBranchType::COMPACT_BRANCH);
} }
...@@ -1288,7 +1312,7 @@ void Assembler::bovc(Register rs, Register rt, int16_t offset) { ...@@ -1288,7 +1312,7 @@ void Assembler::bovc(Register rs, Register rt, int16_t offset) {
DCHECK(IsMipsArchVariant(kMips32r6)); DCHECK(IsMipsArchVariant(kMips32r6));
DCHECK(!(rs.is(zero_reg))); DCHECK(!(rs.is(zero_reg)));
DCHECK(rs.code() >= rt.code()); DCHECK(rs.code() >= rt.code());
GenInstrImmediate(ADDI, rs, rt, offset); GenInstrImmediate(ADDI, rs, rt, offset, CompactBranchType::COMPACT_BRANCH);
} }
...@@ -1296,27 +1320,31 @@ void Assembler::bnvc(Register rs, Register rt, int16_t offset) { ...@@ -1296,27 +1320,31 @@ void Assembler::bnvc(Register rs, Register rt, int16_t offset) {
DCHECK(IsMipsArchVariant(kMips32r6)); DCHECK(IsMipsArchVariant(kMips32r6));
DCHECK(!(rs.is(zero_reg))); DCHECK(!(rs.is(zero_reg)));
DCHECK(rs.code() >= rt.code()); DCHECK(rs.code() >= rt.code());
GenInstrImmediate(DADDI, rs, rt, offset); GenInstrImmediate(DADDI, rs, rt, offset, CompactBranchType::COMPACT_BRANCH);
} }
void Assembler::blezalc(Register rt, int16_t offset) { void Assembler::blezalc(Register rt, int16_t offset) {
DCHECK(IsMipsArchVariant(kMips32r6)); DCHECK(IsMipsArchVariant(kMips32r6));
DCHECK(!(rt.is(zero_reg))); DCHECK(!(rt.is(zero_reg)));
GenInstrImmediate(BLEZ, zero_reg, rt, offset); positions_recorder()->WriteRecordedPositions();
GenInstrImmediate(BLEZ, zero_reg, rt, offset,
CompactBranchType::COMPACT_BRANCH);
} }
void Assembler::bgezalc(Register rt, int16_t offset) { void Assembler::bgezalc(Register rt, int16_t offset) {
DCHECK(IsMipsArchVariant(kMips32r6)); DCHECK(IsMipsArchVariant(kMips32r6));
DCHECK(!(rt.is(zero_reg))); DCHECK(!(rt.is(zero_reg)));
GenInstrImmediate(BLEZ, rt, rt, offset); positions_recorder()->WriteRecordedPositions();
GenInstrImmediate(BLEZ, rt, rt, offset, CompactBranchType::COMPACT_BRANCH);
} }
void Assembler::bgezall(Register rs, int16_t offset) { void Assembler::bgezall(Register rs, int16_t offset) {
DCHECK(IsMipsArchVariant(kMips32r6)); DCHECK(IsMipsArchVariant(kMips32r6));
DCHECK(!(rs.is(zero_reg))); DCHECK(!(rs.is(zero_reg)));
positions_recorder()->WriteRecordedPositions();
GenInstrImmediate(REGIMM, rs, BGEZALL, offset); GenInstrImmediate(REGIMM, rs, BGEZALL, offset);
} }
...@@ -1324,58 +1352,71 @@ void Assembler::bgezall(Register rs, int16_t offset) { ...@@ -1324,58 +1352,71 @@ void Assembler::bgezall(Register rs, int16_t offset) {
void Assembler::bltzalc(Register rt, int16_t offset) { void Assembler::bltzalc(Register rt, int16_t offset) {
DCHECK(IsMipsArchVariant(kMips32r6)); DCHECK(IsMipsArchVariant(kMips32r6));
DCHECK(!(rt.is(zero_reg))); DCHECK(!(rt.is(zero_reg)));
GenInstrImmediate(BGTZ, rt, rt, offset); positions_recorder()->WriteRecordedPositions();
GenInstrImmediate(BGTZ, rt, rt, offset, CompactBranchType::COMPACT_BRANCH);
} }
void Assembler::bgtzalc(Register rt, int16_t offset) { void Assembler::bgtzalc(Register rt, int16_t offset) {
DCHECK(IsMipsArchVariant(kMips32r6)); DCHECK(IsMipsArchVariant(kMips32r6));
DCHECK(!(rt.is(zero_reg))); DCHECK(!(rt.is(zero_reg)));
GenInstrImmediate(BGTZ, zero_reg, rt, offset); positions_recorder()->WriteRecordedPositions();
GenInstrImmediate(BGTZ, zero_reg, rt, offset,
CompactBranchType::COMPACT_BRANCH);
} }
void Assembler::beqzalc(Register rt, int16_t offset) { void Assembler::beqzalc(Register rt, int16_t offset) {
DCHECK(IsMipsArchVariant(kMips32r6)); DCHECK(IsMipsArchVariant(kMips32r6));
DCHECK(!(rt.is(zero_reg))); DCHECK(!(rt.is(zero_reg)));
GenInstrImmediate(ADDI, zero_reg, rt, offset); positions_recorder()->WriteRecordedPositions();
GenInstrImmediate(ADDI, zero_reg, rt, offset,
CompactBranchType::COMPACT_BRANCH);
} }
void Assembler::bnezalc(Register rt, int16_t offset) { void Assembler::bnezalc(Register rt, int16_t offset) {
DCHECK(IsMipsArchVariant(kMips32r6)); DCHECK(IsMipsArchVariant(kMips32r6));
DCHECK(!(rt.is(zero_reg))); DCHECK(!(rt.is(zero_reg)));
GenInstrImmediate(DADDI, zero_reg, rt, offset); positions_recorder()->WriteRecordedPositions();
GenInstrImmediate(DADDI, zero_reg, rt, offset,
CompactBranchType::COMPACT_BRANCH);
} }
void Assembler::beqc(Register rs, Register rt, int16_t offset) { void Assembler::beqc(Register rs, Register rt, int16_t offset) {
DCHECK(IsMipsArchVariant(kMips32r6)); DCHECK(IsMipsArchVariant(kMips32r6));
DCHECK(rs.code() < rt.code()); DCHECK(rs.code() != rt.code() && rs.code() != 0 && rt.code() != 0);
GenInstrImmediate(ADDI, rs, rt, offset); if (rs.code() < rt.code()) {
GenInstrImmediate(ADDI, rs, rt, offset, CompactBranchType::COMPACT_BRANCH);
} else {
GenInstrImmediate(ADDI, rt, rs, offset, CompactBranchType::COMPACT_BRANCH);
}
} }
void Assembler::beqzc(Register rs, int32_t offset) { void Assembler::beqzc(Register rs, int32_t offset) {
DCHECK(IsMipsArchVariant(kMips32r6)); DCHECK(IsMipsArchVariant(kMips32r6));
DCHECK(!(rs.is(zero_reg))); DCHECK(!(rs.is(zero_reg)));
Instr instr = POP66 | (rs.code() << kRsShift) | (offset & kImm21Mask); GenInstrImmediate(POP66, rs, offset, CompactBranchType::COMPACT_BRANCH);
emit(instr);
} }
void Assembler::bnec(Register rs, Register rt, int16_t offset) { void Assembler::bnec(Register rs, Register rt, int16_t offset) {
DCHECK(IsMipsArchVariant(kMips32r6)); DCHECK(IsMipsArchVariant(kMips32r6));
DCHECK(rs.code() < rt.code()); DCHECK(rs.code() != rt.code() && rs.code() != 0 && rt.code() != 0);
GenInstrImmediate(DADDI, rs, rt, offset); if (rs.code() < rt.code()) {
GenInstrImmediate(DADDI, rs, rt, offset, CompactBranchType::COMPACT_BRANCH);
} else {
GenInstrImmediate(DADDI, rt, rs, offset, CompactBranchType::COMPACT_BRANCH);
}
} }
void Assembler::bnezc(Register rs, int32_t offset) { void Assembler::bnezc(Register rs, int32_t offset) {
DCHECK(IsMipsArchVariant(kMips32r6)); DCHECK(IsMipsArchVariant(kMips32r6));
DCHECK(!(rs.is(zero_reg))); DCHECK(!(rs.is(zero_reg)));
Instr instr = POP76 | (rs.code() << kRsShift) | offset; GenInstrImmediate(POP76, rs, offset, CompactBranchType::COMPACT_BRANCH);
emit(instr);
} }
...@@ -1429,16 +1470,16 @@ void Assembler::jalr(Register rs, Register rd) { ...@@ -1429,16 +1470,16 @@ void Assembler::jalr(Register rs, Register rd) {
void Assembler::jic(Register rt, int16_t offset) { void Assembler::jic(Register rt, int16_t offset) {
DCHECK(IsMipsArchVariant(kMips32r6)); DCHECK(IsMipsArchVariant(kMips32r6));
Instr instr = POP66 | (JIC << kRsShift) | (rt.code() << kRtShift) | GenInstrImmediate(POP66, zero_reg, rt, offset,
(offset & kImm16Mask); CompactBranchType::COMPACT_BRANCH);
emit(instr);
} }
void Assembler::jialc(Register rt, int16_t offset) { void Assembler::jialc(Register rt, int16_t offset) {
DCHECK(IsMipsArchVariant(kMips32r6)); DCHECK(IsMipsArchVariant(kMips32r6));
positions_recorder()->WriteRecordedPositions(); positions_recorder()->WriteRecordedPositions();
GenInstrImmediate(POP76, zero_reg, rt, offset); GenInstrImmediate(POP76, zero_reg, rt, offset,
CompactBranchType::COMPACT_BRANCH);
} }
...@@ -1762,7 +1803,7 @@ void Assembler::aui(Register rs, Register rt, int32_t j) { ...@@ -1762,7 +1803,7 @@ void Assembler::aui(Register rs, Register rt, int32_t j) {
void Assembler::addiupc(Register rs, int32_t imm19) { void Assembler::addiupc(Register rs, int32_t imm19) {
DCHECK(IsMipsArchVariant(kMips32r6)); DCHECK(IsMipsArchVariant(kMips32r6));
DCHECK(rs.is_valid() && is_int19(imm19)); DCHECK(rs.is_valid() && is_int19(imm19));
int32_t imm21 = ADDIUPC << kImm19Bits | (imm19 & kImm19Mask); uint32_t imm21 = ADDIUPC << kImm19Bits | (imm19 & kImm19Mask);
GenInstrImmediate(PCREL, rs, imm21); GenInstrImmediate(PCREL, rs, imm21);
} }
...@@ -1770,7 +1811,7 @@ void Assembler::addiupc(Register rs, int32_t imm19) { ...@@ -1770,7 +1811,7 @@ void Assembler::addiupc(Register rs, int32_t imm19) {
void Assembler::lwpc(Register rs, int32_t offset19) { void Assembler::lwpc(Register rs, int32_t offset19) {
DCHECK(IsMipsArchVariant(kMips32r6)); DCHECK(IsMipsArchVariant(kMips32r6));
DCHECK(rs.is_valid() && is_int19(offset19)); DCHECK(rs.is_valid() && is_int19(offset19));
int32_t imm21 = LWPC << kImm19Bits | (offset19 & kImm19Mask); uint32_t imm21 = LWPC << kImm19Bits | (offset19 & kImm19Mask);
GenInstrImmediate(PCREL, rs, imm21); GenInstrImmediate(PCREL, rs, imm21);
} }
...@@ -1778,7 +1819,7 @@ void Assembler::lwpc(Register rs, int32_t offset19) { ...@@ -1778,7 +1819,7 @@ void Assembler::lwpc(Register rs, int32_t offset19) {
void Assembler::auipc(Register rs, int16_t imm16) { void Assembler::auipc(Register rs, int16_t imm16) {
DCHECK(IsMipsArchVariant(kMips32r6)); DCHECK(IsMipsArchVariant(kMips32r6));
DCHECK(rs.is_valid() && is_int16(imm16)); DCHECK(rs.is_valid() && is_int16(imm16));
int32_t imm21 = AUIPC << kImm16Bits | (imm16 & kImm16Mask); uint32_t imm21 = AUIPC << kImm16Bits | (imm16 & kImm16Mask);
GenInstrImmediate(PCREL, rs, imm21); GenInstrImmediate(PCREL, rs, imm21);
} }
...@@ -1786,7 +1827,7 @@ void Assembler::auipc(Register rs, int16_t imm16) { ...@@ -1786,7 +1827,7 @@ void Assembler::auipc(Register rs, int16_t imm16) {
void Assembler::aluipc(Register rs, int16_t imm16) { void Assembler::aluipc(Register rs, int16_t imm16) {
DCHECK(IsMipsArchVariant(kMips32r6)); DCHECK(IsMipsArchVariant(kMips32r6));
DCHECK(rs.is_valid() && is_int16(imm16)); DCHECK(rs.is_valid() && is_int16(imm16));
int32_t imm21 = ALUIPC << kImm16Bits | (imm16 & kImm16Mask); uint32_t imm21 = ALUIPC << kImm16Bits | (imm16 & kImm16Mask);
GenInstrImmediate(PCREL, rs, imm21); GenInstrImmediate(PCREL, rs, imm21);
} }
...@@ -2671,7 +2712,6 @@ void Assembler::bc1t(int16_t offset, uint16_t cc) { ...@@ -2671,7 +2712,6 @@ void Assembler::bc1t(int16_t offset, uint16_t cc) {
} }
// Debugging.
int Assembler::RelocateInternalReference(RelocInfo::Mode rmode, byte* pc, int Assembler::RelocateInternalReference(RelocInfo::Mode rmode, byte* pc,
intptr_t pc_delta) { intptr_t pc_delta) {
Instr instr = instr_at(pc); Instr instr = instr_at(pc);
...@@ -2865,8 +2905,12 @@ void Assembler::CheckTrampolinePool() { ...@@ -2865,8 +2905,12 @@ void Assembler::CheckTrampolinePool() {
// First we emit jump (2 instructions), then we emit trampoline pool. // First we emit jump (2 instructions), then we emit trampoline pool.
{ BlockTrampolinePoolScope block_trampoline_pool(this); { BlockTrampolinePoolScope block_trampoline_pool(this);
Label after_pool; Label after_pool;
b(&after_pool); if (IsMipsArchVariant(kMips32r6)) {
nop(); bc(&after_pool);
} else {
b(&after_pool);
nop();
}
int pool_start = pc_offset(); int pool_start = pc_offset();
for (int i = 0; i < unbound_labels_count_; i++) { for (int i = 0; i < unbound_labels_count_; i++) {
......
...@@ -408,27 +408,36 @@ class Assembler : public AssemblerBase { ...@@ -408,27 +408,36 @@ class Assembler : public AssemblerBase {
// Note: The same Label can be used for forward and backward branches // Note: The same Label can be used for forward and backward branches
// but it may be bound only once. // but it may be bound only once.
void bind(Label* L); // Binds an unbound label L to current code position. void bind(Label* L); // Binds an unbound label L to current code position.
enum OffsetSize : int { kOffset26 = 26, kOffset21 = 21, kOffset16 = 16 };
// Determines if Label is bound and near enough so that branch instruction // Determines if Label is bound and near enough so that branch instruction
// can be used to reach it, instead of jump instruction. // can be used to reach it, instead of jump instruction.
bool is_near(Label* L); bool is_near(Label* L);
bool is_near(Label* L, OffsetSize bits);
bool is_near_branch(Label* L);
// Returns the branch offset to the given label from the current code // Returns the branch offset to the given label from the current code
// position. Links the label to the current position if it is still unbound. // position. Links the label to the current position if it is still unbound.
// Manages the jump elimination optimization if the second parameter is true. // Manages the jump elimination optimization if the second parameter is true.
int32_t branch_offset(Label* L, bool jump_elimination_allowed); int32_t branch_offset_helper(Label* L, OffsetSize bits);
int32_t branch_offset_compact(Label* L, bool jump_elimination_allowed); inline int32_t branch_offset(Label* L) {
int32_t branch_offset21(Label* L, bool jump_elimination_allowed); return branch_offset_helper(L, OffsetSize::kOffset16);
int32_t branch_offset21_compact(Label* L, bool jump_elimination_allowed); }
int32_t shifted_branch_offset(Label* L, bool jump_elimination_allowed) { inline int32_t branch_offset21(Label* L) {
int32_t o = branch_offset(L, jump_elimination_allowed); return branch_offset_helper(L, OffsetSize::kOffset21);
DCHECK((o & 3) == 0); // Assert the offset is aligned. }
return o >> 2; inline int32_t branch_offset26(Label* L) {
} return branch_offset_helper(L, OffsetSize::kOffset26);
int32_t shifted_branch_offset_compact(Label* L, }
bool jump_elimination_allowed) { inline int32_t shifted_branch_offset(Label* L) {
int32_t o = branch_offset_compact(L, jump_elimination_allowed); return branch_offset(L) >> 2;
DCHECK((o & 3) == 0); // Assert the offset is aligned. }
return o >> 2; inline int32_t shifted_branch_offset21(Label* L) {
return branch_offset21(L) >> 2;
}
inline int32_t shifted_branch_offset26(Label* L) {
return branch_offset26(L) >> 2;
} }
uint32_t jump_address(Label* L); uint32_t jump_address(Label* L);
...@@ -571,111 +580,111 @@ class Assembler : public AssemblerBase { ...@@ -571,111 +580,111 @@ class Assembler : public AssemblerBase {
// --------Branch-and-jump-instructions---------- // --------Branch-and-jump-instructions----------
// We don't use likely variant of instructions. // We don't use likely variant of instructions.
void b(int16_t offset); void b(int16_t offset);
void b(Label* L) { b(branch_offset(L, false)>>2); } inline void b(Label* L) { b(shifted_branch_offset(L)); }
void bal(int16_t offset); void bal(int16_t offset);
void bal(Label* L) { bal(branch_offset(L, false)>>2); } inline void bal(Label* L) { bal(shifted_branch_offset(L)); }
void bc(int32_t offset); void bc(int32_t offset);
void bc(Label* L) { bc(branch_offset(L, false) >> 2); } inline void bc(Label* L) { bc(shifted_branch_offset26(L)); }
void balc(int32_t offset); void balc(int32_t offset);
void balc(Label* L) { balc(branch_offset(L, false) >> 2); } inline void balc(Label* L) { balc(shifted_branch_offset26(L)); }
void beq(Register rs, Register rt, int16_t offset); void beq(Register rs, Register rt, int16_t offset);
void beq(Register rs, Register rt, Label* L) { inline void beq(Register rs, Register rt, Label* L) {
beq(rs, rt, branch_offset(L, false) >> 2); beq(rs, rt, shifted_branch_offset(L));
} }
void bgez(Register rs, int16_t offset); void bgez(Register rs, int16_t offset);
void bgezc(Register rt, int16_t offset); void bgezc(Register rt, int16_t offset);
void bgezc(Register rt, Label* L) { inline void bgezc(Register rt, Label* L) {
bgezc(rt, branch_offset_compact(L, false)>>2); bgezc(rt, shifted_branch_offset(L));
} }
void bgeuc(Register rs, Register rt, int16_t offset); void bgeuc(Register rs, Register rt, int16_t offset);
void bgeuc(Register rs, Register rt, Label* L) { inline void bgeuc(Register rs, Register rt, Label* L) {
bgeuc(rs, rt, branch_offset_compact(L, false)>>2); bgeuc(rs, rt, shifted_branch_offset(L));
} }
void bgec(Register rs, Register rt, int16_t offset); void bgec(Register rs, Register rt, int16_t offset);
void bgec(Register rs, Register rt, Label* L) { inline void bgec(Register rs, Register rt, Label* L) {
bgec(rs, rt, branch_offset_compact(L, false)>>2); bgec(rs, rt, shifted_branch_offset(L));
} }
void bgezal(Register rs, int16_t offset); void bgezal(Register rs, int16_t offset);
void bgezalc(Register rt, int16_t offset); void bgezalc(Register rt, int16_t offset);
void bgezalc(Register rt, Label* L) { inline void bgezalc(Register rt, Label* L) {
bgezalc(rt, branch_offset_compact(L, false)>>2); bgezalc(rt, shifted_branch_offset(L));
} }
void bgezall(Register rs, int16_t offset); void bgezall(Register rs, int16_t offset);
void bgezall(Register rs, Label* L) { inline void bgezall(Register rs, Label* L) {
bgezall(rs, branch_offset(L, false)>>2); bgezall(rs, branch_offset(L) >> 2);
} }
void bgtz(Register rs, int16_t offset); void bgtz(Register rs, int16_t offset);
void bgtzc(Register rt, int16_t offset); void bgtzc(Register rt, int16_t offset);
void bgtzc(Register rt, Label* L) { inline void bgtzc(Register rt, Label* L) {
bgtzc(rt, branch_offset_compact(L, false)>>2); bgtzc(rt, shifted_branch_offset(L));
} }
void blez(Register rs, int16_t offset); void blez(Register rs, int16_t offset);
void blezc(Register rt, int16_t offset); void blezc(Register rt, int16_t offset);
void blezc(Register rt, Label* L) { inline void blezc(Register rt, Label* L) {
blezc(rt, branch_offset_compact(L, false)>>2); blezc(rt, shifted_branch_offset(L));
} }
void bltz(Register rs, int16_t offset); void bltz(Register rs, int16_t offset);
void bltzc(Register rt, int16_t offset); void bltzc(Register rt, int16_t offset);
void bltzc(Register rt, Label* L) { inline void bltzc(Register rt, Label* L) {
bltzc(rt, branch_offset_compact(L, false)>>2); bltzc(rt, shifted_branch_offset(L));
} }
void bltuc(Register rs, Register rt, int16_t offset); void bltuc(Register rs, Register rt, int16_t offset);
void bltuc(Register rs, Register rt, Label* L) { inline void bltuc(Register rs, Register rt, Label* L) {
bltuc(rs, rt, branch_offset_compact(L, false)>>2); bltuc(rs, rt, shifted_branch_offset(L));
} }
void bltc(Register rs, Register rt, int16_t offset); void bltc(Register rs, Register rt, int16_t offset);
void bltc(Register rs, Register rt, Label* L) { inline void bltc(Register rs, Register rt, Label* L) {
bltc(rs, rt, branch_offset_compact(L, false)>>2); bltc(rs, rt, shifted_branch_offset(L));
} }
void bltzal(Register rs, int16_t offset); void bltzal(Register rs, int16_t offset);
void blezalc(Register rt, int16_t offset); void blezalc(Register rt, int16_t offset);
void blezalc(Register rt, Label* L) { inline void blezalc(Register rt, Label* L) {
blezalc(rt, branch_offset_compact(L, false)>>2); blezalc(rt, shifted_branch_offset(L));
} }
void bltzalc(Register rt, int16_t offset); void bltzalc(Register rt, int16_t offset);
void bltzalc(Register rt, Label* L) { inline void bltzalc(Register rt, Label* L) {
bltzalc(rt, branch_offset_compact(L, false)>>2); bltzalc(rt, shifted_branch_offset(L));
} }
void bgtzalc(Register rt, int16_t offset); void bgtzalc(Register rt, int16_t offset);
void bgtzalc(Register rt, Label* L) { inline void bgtzalc(Register rt, Label* L) {
bgtzalc(rt, branch_offset_compact(L, false)>>2); bgtzalc(rt, shifted_branch_offset(L));
} }
void beqzalc(Register rt, int16_t offset); void beqzalc(Register rt, int16_t offset);
void beqzalc(Register rt, Label* L) { inline void beqzalc(Register rt, Label* L) {
beqzalc(rt, branch_offset_compact(L, false)>>2); beqzalc(rt, shifted_branch_offset(L));
} }
void beqc(Register rs, Register rt, int16_t offset); void beqc(Register rs, Register rt, int16_t offset);
void beqc(Register rs, Register rt, Label* L) { inline void beqc(Register rs, Register rt, Label* L) {
beqc(rs, rt, branch_offset_compact(L, false)>>2); beqc(rs, rt, shifted_branch_offset(L));
} }
void beqzc(Register rs, int32_t offset); void beqzc(Register rs, int32_t offset);
void beqzc(Register rs, Label* L) { inline void beqzc(Register rs, Label* L) {
beqzc(rs, branch_offset21_compact(L, false)>>2); beqzc(rs, shifted_branch_offset21(L));
} }
void bnezalc(Register rt, int16_t offset); void bnezalc(Register rt, int16_t offset);
void bnezalc(Register rt, Label* L) { inline void bnezalc(Register rt, Label* L) {
bnezalc(rt, branch_offset_compact(L, false)>>2); bnezalc(rt, shifted_branch_offset(L));
} }
void bnec(Register rs, Register rt, int16_t offset); void bnec(Register rs, Register rt, int16_t offset);
void bnec(Register rs, Register rt, Label* L) { inline void bnec(Register rs, Register rt, Label* L) {
bnec(rs, rt, branch_offset_compact(L, false)>>2); bnec(rs, rt, shifted_branch_offset(L));
} }
void bnezc(Register rt, int32_t offset); void bnezc(Register rt, int32_t offset);
void bnezc(Register rt, Label* L) { inline void bnezc(Register rt, Label* L) {
bnezc(rt, branch_offset21_compact(L, false)>>2); bnezc(rt, shifted_branch_offset21(L));
} }
void bne(Register rs, Register rt, int16_t offset); void bne(Register rs, Register rt, int16_t offset);
void bne(Register rs, Register rt, Label* L) { inline void bne(Register rs, Register rt, Label* L) {
bne(rs, rt, branch_offset(L, false)>>2); bne(rs, rt, shifted_branch_offset(L));
} }
void bovc(Register rs, Register rt, int16_t offset); void bovc(Register rs, Register rt, int16_t offset);
void bovc(Register rs, Register rt, Label* L) { inline void bovc(Register rs, Register rt, Label* L) {
bovc(rs, rt, branch_offset_compact(L, false)>>2); bovc(rs, rt, shifted_branch_offset(L));
} }
void bnvc(Register rs, Register rt, int16_t offset); void bnvc(Register rs, Register rt, int16_t offset);
void bnvc(Register rs, Register rt, Label* L) { inline void bnvc(Register rs, Register rt, Label* L) {
bnvc(rs, rt, branch_offset_compact(L, false)>>2); bnvc(rs, rt, shifted_branch_offset(L));
} }
// Never use the int16_t b(l)cond version with a branch offset // Never use the int16_t b(l)cond version with a branch offset
...@@ -920,12 +929,12 @@ class Assembler : public AssemblerBase { ...@@ -920,12 +929,12 @@ class Assembler : public AssemblerBase {
void cmp_d(FPUCondition cond, FPURegister fd, FPURegister fs, FPURegister ft); void cmp_d(FPUCondition cond, FPURegister fd, FPURegister fs, FPURegister ft);
void bc1eqz(int16_t offset, FPURegister ft); void bc1eqz(int16_t offset, FPURegister ft);
void bc1eqz(Label* L, FPURegister ft) { inline void bc1eqz(Label* L, FPURegister ft) {
bc1eqz(branch_offset(L, false)>>2, ft); bc1eqz(shifted_branch_offset(L), ft);
} }
void bc1nez(int16_t offset, FPURegister ft); void bc1nez(int16_t offset, FPURegister ft);
void bc1nez(Label* L, FPURegister ft) { inline void bc1nez(Label* L, FPURegister ft) {
bc1nez(branch_offset(L, false)>>2, ft); bc1nez(shifted_branch_offset(L), ft);
} }
// Conditions and branches for non MIPSr6. // Conditions and branches for non MIPSr6.
...@@ -935,9 +944,13 @@ class Assembler : public AssemblerBase { ...@@ -935,9 +944,13 @@ class Assembler : public AssemblerBase {
void c_d(FPUCondition cond, FPURegister ft, FPURegister fs, uint16_t cc = 0); void c_d(FPUCondition cond, FPURegister ft, FPURegister fs, uint16_t cc = 0);
void bc1f(int16_t offset, uint16_t cc = 0); void bc1f(int16_t offset, uint16_t cc = 0);
void bc1f(Label* L, uint16_t cc = 0) { bc1f(branch_offset(L, false)>>2, cc); } inline void bc1f(Label* L, uint16_t cc = 0) {
bc1f(shifted_branch_offset(L), cc);
}
void bc1t(int16_t offset, uint16_t cc = 0); void bc1t(int16_t offset, uint16_t cc = 0);
void bc1t(Label* L, uint16_t cc = 0) { bc1t(branch_offset(L, false)>>2, cc); } inline void bc1t(Label* L, uint16_t cc = 0) {
bc1t(shifted_branch_offset(L), cc);
}
void fcmp(FPURegister src1, const double src2, FPUCondition cond); void fcmp(FPURegister src1, const double src2, FPUCondition cond);
// Check the code size generated from label to here. // Check the code size generated from label to here.
...@@ -1056,8 +1069,14 @@ class Assembler : public AssemblerBase { ...@@ -1056,8 +1069,14 @@ class Assembler : public AssemblerBase {
// Check if an instruction is a branch of some kind. // Check if an instruction is a branch of some kind.
static bool IsBranch(Instr instr); static bool IsBranch(Instr instr);
static bool IsBc(Instr instr);
static bool IsBzc(Instr instr);
static bool IsBeq(Instr instr); static bool IsBeq(Instr instr);
static bool IsBne(Instr instr); static bool IsBne(Instr instr);
static bool IsBeqzc(Instr instr);
static bool IsBnezc(Instr instr);
static bool IsBeqc(Instr instr);
static bool IsBnec(Instr instr);
static bool IsJump(Instr instr); static bool IsJump(Instr instr);
static bool IsJ(Instr instr); static bool IsJ(Instr instr);
...@@ -1179,6 +1198,8 @@ class Assembler : public AssemblerBase { ...@@ -1179,6 +1198,8 @@ class Assembler : public AssemblerBase {
return block_buffer_growth_; return block_buffer_growth_;
} }
bool IsPrevInstrCompactBranch() { return prev_instr_compact_branch_; }
private: private:
inline static void set_target_internal_reference_encoded_at(Address pc, inline static void set_target_internal_reference_encoded_at(Address pc,
Address target); Address target);
...@@ -1221,10 +1242,14 @@ class Assembler : public AssemblerBase { ...@@ -1221,10 +1242,14 @@ class Assembler : public AssemblerBase {
// The bound position, before this we cannot do instruction elimination. // The bound position, before this we cannot do instruction elimination.
int last_bound_pos_; int last_bound_pos_;
// Readable constants for compact branch handling in emit()
enum class CompactBranchType : bool { NO = false, COMPACT_BRANCH = true };
// Code emission. // Code emission.
inline void CheckBuffer(); inline void CheckBuffer();
void GrowBuffer(); void GrowBuffer();
inline void emit(Instr x); inline void emit(Instr x,
CompactBranchType is_compact_branch = CompactBranchType::NO);
inline void CheckTrampolinePoolQuick(int extra_instructions = 0); inline void CheckTrampolinePoolQuick(int extra_instructions = 0);
// Instruction generation. // Instruction generation.
...@@ -1276,21 +1301,22 @@ class Assembler : public AssemblerBase { ...@@ -1276,21 +1301,22 @@ class Assembler : public AssemblerBase {
FPUControlRegister fs, FPUControlRegister fs,
SecondaryField func = NULLSF); SecondaryField func = NULLSF);
void GenInstrImmediate(
void GenInstrImmediate(Opcode opcode, Opcode opcode, Register rs, Register rt, int32_t j,
Register rs, CompactBranchType is_compact_branch = CompactBranchType::NO);
Register rt, void GenInstrImmediate(
int32_t j); Opcode opcode, Register rs, SecondaryField SF, int32_t j,
void GenInstrImmediate(Opcode opcode, CompactBranchType is_compact_branch = CompactBranchType::NO);
Register rs, void GenInstrImmediate(
SecondaryField SF, Opcode opcode, Register r1, FPURegister r2, int32_t j,
int32_t j); CompactBranchType is_compact_branch = CompactBranchType::NO);
void GenInstrImmediate(Opcode opcode, void GenInstrImmediate(
Register r1, Opcode opcode, Register rs, int32_t offset21,
FPURegister r2, CompactBranchType is_compact_branch = CompactBranchType::NO);
int32_t j); void GenInstrImmediate(Opcode opcode, Register rs, uint32_t offset21);
void GenInstrImmediate(Opcode opcode, Register rs, int32_t j); void GenInstrImmediate(
void GenInstrImmediate(Opcode opcode, int32_t offset26); Opcode opcode, int32_t offset26,
CompactBranchType is_compact_branch = CompactBranchType::NO);
void GenInstrJump(Opcode opcode, void GenInstrJump(Opcode opcode,
...@@ -1365,12 +1391,17 @@ class Assembler : public AssemblerBase { ...@@ -1365,12 +1391,17 @@ class Assembler : public AssemblerBase {
bool trampoline_emitted_; bool trampoline_emitted_;
static const int kTrampolineSlotsSize = 4 * kInstrSize; static const int kTrampolineSlotsSize = 4 * kInstrSize;
static const int kMaxBranchOffset = (1 << (18 - 1)) - 1; static const int kMaxBranchOffset = (1 << (18 - 1)) - 1;
static const int kMaxCompactBranchOffset = (1 << (28 - 1)) - 1;
static const int kInvalidSlotPos = -1; static const int kInvalidSlotPos = -1;
// Internal reference positions, required for unbounded internal reference // Internal reference positions, required for unbounded internal reference
// labels. // labels.
std::set<int> internal_reference_positions_; std::set<int> internal_reference_positions_;
void EmittedCompactBranchInstruction() { prev_instr_compact_branch_ = true; }
void ClearCompactBranchState() { prev_instr_compact_branch_ = false; }
bool prev_instr_compact_branch_ = false;
Trampoline trampoline_; Trampoline trampoline_;
bool internal_trampoline_exception_; bool internal_trampoline_exception_;
......
...@@ -126,24 +126,28 @@ int FPURegisters::Number(const char* name) { ...@@ -126,24 +126,28 @@ int FPURegisters::Number(const char* name) {
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Instructions. // Instructions.
bool Instruction::IsForbiddenInBranchDelay() const { bool Instruction::IsForbiddenAfterBranchInstr(Instr instr) {
const int op = OpcodeFieldRaw(); Opcode opcode = static_cast<Opcode>(instr & kOpcodeMask);
switch (op) { switch (opcode) {
case J: case J:
case JAL: case JAL:
case BEQ: case BEQ:
case BNE: case BNE:
case BLEZ: case BLEZ: // POP06 bgeuc/bleuc, blezalc, bgezalc
case BGTZ: case BGTZ: // POP07 bltuc/bgtuc, bgtzalc, bltzalc
case BEQL: case BEQL:
case BNEL: case BNEL:
case BLEZL: case BLEZL: // POP26 bgezc, blezc, bgec/blec
case BGTZL: case BGTZL: // POP27 bgtzc, bltzc, bltc/bgtc
case BC: case BC:
case BALC: case BALC:
case POP10: // beqzalc, bovc, beqc
case POP30: // bnezalc, bvnc, bnec
case POP66: // beqzc, jic
case POP76: // bnezc, jialc
return true; return true;
case REGIMM: case REGIMM:
switch (RtFieldRaw()) { switch (instr & kRtFieldMask) {
case BLTZ: case BLTZ:
case BGEZ: case BGEZ:
case BLTZAL: case BLTZAL:
...@@ -154,7 +158,7 @@ bool Instruction::IsForbiddenInBranchDelay() const { ...@@ -154,7 +158,7 @@ bool Instruction::IsForbiddenInBranchDelay() const {
} }
break; break;
case SPECIAL: case SPECIAL:
switch (FunctionFieldRaw()) { switch (instr & kFunctionFieldMask) {
case JR: case JR:
case JALR: case JALR:
return true; return true;
...@@ -169,8 +173,7 @@ bool Instruction::IsForbiddenInBranchDelay() const { ...@@ -169,8 +173,7 @@ bool Instruction::IsForbiddenInBranchDelay() const {
bool Instruction::IsLinkingInstruction() const { bool Instruction::IsLinkingInstruction() const {
const int op = OpcodeFieldRaw(); switch (OpcodeFieldRaw()) {
switch (op) {
case JAL: case JAL:
return true; return true;
case POP76: case POP76:
......
...@@ -298,311 +298,319 @@ const int kFBtrueBits = 1; ...@@ -298,311 +298,319 @@ const int kFBtrueBits = 1;
// ----- Miscellaneous useful masks. // ----- Miscellaneous useful masks.
// Instruction bit masks. // Instruction bit masks.
const int kOpcodeMask = ((1 << kOpcodeBits) - 1) << kOpcodeShift; const int kOpcodeMask = ((1 << kOpcodeBits) - 1) << kOpcodeShift;
const int kImm16Mask = ((1 << kImm16Bits) - 1) << kImm16Shift; const int kImm16Mask = ((1 << kImm16Bits) - 1) << kImm16Shift;
const int kImm18Mask = ((1 << kImm18Bits) - 1) << kImm18Shift; const int kImm18Mask = ((1 << kImm18Bits) - 1) << kImm18Shift;
const int kImm19Mask = ((1 << kImm19Bits) - 1) << kImm19Shift; const int kImm19Mask = ((1 << kImm19Bits) - 1) << kImm19Shift;
const int kImm21Mask = ((1 << kImm21Bits) - 1) << kImm21Shift; const int kImm21Mask = ((1 << kImm21Bits) - 1) << kImm21Shift;
const int kImm26Mask = ((1 << kImm26Bits) - 1) << kImm26Shift; const int kImm26Mask = ((1 << kImm26Bits) - 1) << kImm26Shift;
const int kImm28Mask = ((1 << kImm28Bits) - 1) << kImm28Shift; const int kImm28Mask = ((1 << kImm28Bits) - 1) << kImm28Shift;
const int kRsFieldMask = ((1 << kRsBits) - 1) << kRsShift; const int kRsFieldMask = ((1 << kRsBits) - 1) << kRsShift;
const int kRtFieldMask = ((1 << kRtBits) - 1) << kRtShift; const int kRtFieldMask = ((1 << kRtBits) - 1) << kRtShift;
const int kRdFieldMask = ((1 << kRdBits) - 1) << kRdShift; const int kRdFieldMask = ((1 << kRdBits) - 1) << kRdShift;
const int kSaFieldMask = ((1 << kSaBits) - 1) << kSaShift; const int kSaFieldMask = ((1 << kSaBits) - 1) << kSaShift;
const int kFunctionFieldMask = ((1 << kFunctionBits) - 1) << kFunctionShift; const int kFunctionFieldMask = ((1 << kFunctionBits) - 1) << kFunctionShift;
// Misc masks. // Misc masks.
const int kHiMask = 0xffff << 16; const int kHiMask = 0xffff << 16;
const int kLoMask = 0xffff; const int kLoMask = 0xffff;
const int kSignMask = 0x80000000; const int kSignMask = 0x80000000;
const int kJumpAddrMask = (1 << (kImm26Bits + kImmFieldShift)) - 1; const int kJumpAddrMask = (1 << (kImm26Bits + kImmFieldShift)) - 1;
// ----- MIPS Opcodes and Function Fields. // ----- MIPS Opcodes and Function Fields.
// We use this presentation to stay close to the table representation in // We use this presentation to stay close to the table representation in
// MIPS32 Architecture For Programmers, Volume II: The MIPS32 Instruction Set. // MIPS32 Architecture For Programmers, Volume II: The MIPS32 Instruction Set.
enum Opcode { enum Opcode : uint32_t {
SPECIAL = 0 << kOpcodeShift, SPECIAL = 0U << kOpcodeShift,
REGIMM = 1 << kOpcodeShift, REGIMM = 1U << kOpcodeShift,
J = ((0 << 3) + 2) << kOpcodeShift, J = ((0U << 3) + 2) << kOpcodeShift,
JAL = ((0 << 3) + 3) << kOpcodeShift, JAL = ((0U << 3) + 3) << kOpcodeShift,
BEQ = ((0 << 3) + 4) << kOpcodeShift, BEQ = ((0U << 3) + 4) << kOpcodeShift,
BNE = ((0 << 3) + 5) << kOpcodeShift, BNE = ((0U << 3) + 5) << kOpcodeShift,
BLEZ = ((0 << 3) + 6) << kOpcodeShift, BLEZ = ((0U << 3) + 6) << kOpcodeShift,
BGTZ = ((0 << 3) + 7) << kOpcodeShift, BGTZ = ((0U << 3) + 7) << kOpcodeShift,
ADDI = ((1 << 3) + 0) << kOpcodeShift, ADDI = ((1U << 3) + 0) << kOpcodeShift,
ADDIU = ((1 << 3) + 1) << kOpcodeShift, ADDIU = ((1U << 3) + 1) << kOpcodeShift,
SLTI = ((1 << 3) + 2) << kOpcodeShift, SLTI = ((1U << 3) + 2) << kOpcodeShift,
SLTIU = ((1 << 3) + 3) << kOpcodeShift, SLTIU = ((1U << 3) + 3) << kOpcodeShift,
ANDI = ((1 << 3) + 4) << kOpcodeShift, ANDI = ((1U << 3) + 4) << kOpcodeShift,
ORI = ((1 << 3) + 5) << kOpcodeShift, ORI = ((1U << 3) + 5) << kOpcodeShift,
XORI = ((1 << 3) + 6) << kOpcodeShift, XORI = ((1U << 3) + 6) << kOpcodeShift,
LUI = ((1 << 3) + 7) << kOpcodeShift, // LUI/AUI family. LUI = ((1U << 3) + 7) << kOpcodeShift, // LUI/AUI family.
BEQC = ((2 << 3) + 0) << kOpcodeShift, BEQC = ((2U << 3) + 0) << kOpcodeShift,
COP1 = ((2 << 3) + 1) << kOpcodeShift, // Coprocessor 1 class. COP1 = ((2U << 3) + 1) << kOpcodeShift, // Coprocessor 1 class.
BEQL = ((2 << 3) + 4) << kOpcodeShift, BEQL = ((2U << 3) + 4) << kOpcodeShift,
BNEL = ((2 << 3) + 5) << kOpcodeShift, BNEL = ((2U << 3) + 5) << kOpcodeShift,
BLEZL = ((2 << 3) + 6) << kOpcodeShift, BLEZL = ((2U << 3) + 6) << kOpcodeShift,
BGTZL = ((2 << 3) + 7) << kOpcodeShift, BGTZL = ((2U << 3) + 7) << kOpcodeShift,
DADDI = ((3 << 3) + 0) << kOpcodeShift, // This is also BNEC. DADDI = ((3U << 3) + 0) << kOpcodeShift, // This is also BNEC.
SPECIAL2 = ((3 << 3) + 4) << kOpcodeShift, SPECIAL2 = ((3U << 3) + 4) << kOpcodeShift,
SPECIAL3 = ((3 << 3) + 7) << kOpcodeShift, SPECIAL3 = ((3U << 3) + 7) << kOpcodeShift,
LB = ((4 << 3) + 0) << kOpcodeShift, LB = ((4U << 3) + 0) << kOpcodeShift,
LH = ((4 << 3) + 1) << kOpcodeShift, LH = ((4U << 3) + 1) << kOpcodeShift,
LWL = ((4 << 3) + 2) << kOpcodeShift, LWL = ((4U << 3) + 2) << kOpcodeShift,
LW = ((4 << 3) + 3) << kOpcodeShift, LW = ((4U << 3) + 3) << kOpcodeShift,
LBU = ((4 << 3) + 4) << kOpcodeShift, LBU = ((4U << 3) + 4) << kOpcodeShift,
LHU = ((4 << 3) + 5) << kOpcodeShift, LHU = ((4U << 3) + 5) << kOpcodeShift,
LWR = ((4 << 3) + 6) << kOpcodeShift, LWR = ((4U << 3) + 6) << kOpcodeShift,
SB = ((5 << 3) + 0) << kOpcodeShift, SB = ((5U << 3) + 0) << kOpcodeShift,
SH = ((5 << 3) + 1) << kOpcodeShift, SH = ((5U << 3) + 1) << kOpcodeShift,
SWL = ((5 << 3) + 2) << kOpcodeShift, SWL = ((5U << 3) + 2) << kOpcodeShift,
SW = ((5 << 3) + 3) << kOpcodeShift, SW = ((5U << 3) + 3) << kOpcodeShift,
SWR = ((5 << 3) + 6) << kOpcodeShift, SWR = ((5U << 3) + 6) << kOpcodeShift,
LWC1 = ((6 << 3) + 1) << kOpcodeShift, LWC1 = ((6U << 3) + 1) << kOpcodeShift,
BC = ((6 << 3) + 2) << kOpcodeShift, BC = ((6U << 3) + 2) << kOpcodeShift,
LDC1 = ((6 << 3) + 5) << kOpcodeShift, LDC1 = ((6U << 3) + 5) << kOpcodeShift,
POP66 = ((6 << 3) + 6) << kOpcodeShift, POP66 = ((6U << 3) + 6) << kOpcodeShift, // beqzc, jic
PREF = ((6 << 3) + 3) << kOpcodeShift, PREF = ((6U << 3) + 3) << kOpcodeShift,
SWC1 = ((7 << 3) + 1) << kOpcodeShift, SWC1 = ((7U << 3) + 1) << kOpcodeShift,
BALC = ((7 << 3) + 2) << kOpcodeShift, BALC = ((7U << 3) + 2) << kOpcodeShift,
PCREL = ((7 << 3) + 3) << kOpcodeShift, PCREL = ((7U << 3) + 3) << kOpcodeShift,
SDC1 = ((7 << 3) + 5) << kOpcodeShift, SDC1 = ((7U << 3) + 5) << kOpcodeShift,
POP76 = ((7 << 3) + 6) << kOpcodeShift, POP76 = ((7U << 3) + 6) << kOpcodeShift, // bnezc, jialc
COP1X = ((1 << 4) + 3) << kOpcodeShift COP1X = ((1U << 4) + 3) << kOpcodeShift,
// New r6 instruction.
POP06 = BLEZ, // bgeuc/bleuc, blezalc, bgezalc
POP07 = BGTZ, // bltuc/bgtuc, bgtzalc, bltzalc
POP10 = ADDI, // beqzalc, bovc, beqc
POP26 = BLEZL, // bgezc, blezc, bgec/blec
POP27 = BGTZL, // bgtzc, bltzc, bltc/bgtc
POP30 = DADDI, // bnezalc, bvnc, bnec
}; };
enum SecondaryField { enum SecondaryField : uint32_t {
// SPECIAL Encoding of Function Field. // SPECIAL Encoding of Function Field.
SLL = ((0 << 3) + 0), SLL = ((0U << 3) + 0),
MOVCI = ((0 << 3) + 1), MOVCI = ((0U << 3) + 1),
SRL = ((0 << 3) + 2), SRL = ((0U << 3) + 2),
SRA = ((0 << 3) + 3), SRA = ((0U << 3) + 3),
SLLV = ((0 << 3) + 4), SLLV = ((0U << 3) + 4),
SRLV = ((0 << 3) + 6), SRLV = ((0U << 3) + 6),
SRAV = ((0 << 3) + 7), SRAV = ((0U << 3) + 7),
JR = ((1 << 3) + 0), JR = ((1U << 3) + 0),
JALR = ((1 << 3) + 1), JALR = ((1U << 3) + 1),
MOVZ = ((1 << 3) + 2), MOVZ = ((1U << 3) + 2),
MOVN = ((1 << 3) + 3), MOVN = ((1U << 3) + 3),
BREAK = ((1 << 3) + 5), BREAK = ((1U << 3) + 5),
MFHI = ((2 << 3) + 0), MFHI = ((2U << 3) + 0),
CLZ_R6 = ((2 << 3) + 0), CLZ_R6 = ((2U << 3) + 0),
CLO_R6 = ((2 << 3) + 1), CLO_R6 = ((2U << 3) + 1),
MFLO = ((2 << 3) + 2), MFLO = ((2U << 3) + 2),
MULT = ((3 << 3) + 0), MULT = ((3U << 3) + 0),
MULTU = ((3 << 3) + 1), MULTU = ((3U << 3) + 1),
DIV = ((3 << 3) + 2), DIV = ((3U << 3) + 2),
DIVU = ((3 << 3) + 3), DIVU = ((3U << 3) + 3),
ADD = ((4 << 3) + 0), ADD = ((4U << 3) + 0),
ADDU = ((4 << 3) + 1), ADDU = ((4U << 3) + 1),
SUB = ((4 << 3) + 2), SUB = ((4U << 3) + 2),
SUBU = ((4 << 3) + 3), SUBU = ((4U << 3) + 3),
AND = ((4 << 3) + 4), AND = ((4U << 3) + 4),
OR = ((4 << 3) + 5), OR = ((4U << 3) + 5),
XOR = ((4 << 3) + 6), XOR = ((4U << 3) + 6),
NOR = ((4 << 3) + 7), NOR = ((4U << 3) + 7),
SLT = ((5 << 3) + 2), SLT = ((5U << 3) + 2),
SLTU = ((5 << 3) + 3), SLTU = ((5U << 3) + 3),
TGE = ((6 << 3) + 0), TGE = ((6U << 3) + 0),
TGEU = ((6 << 3) + 1), TGEU = ((6U << 3) + 1),
TLT = ((6 << 3) + 2), TLT = ((6U << 3) + 2),
TLTU = ((6 << 3) + 3), TLTU = ((6U << 3) + 3),
TEQ = ((6 << 3) + 4), TEQ = ((6U << 3) + 4),
SELEQZ_S = ((6 << 3) + 5), SELEQZ_S = ((6U << 3) + 5),
TNE = ((6 << 3) + 6), TNE = ((6U << 3) + 6),
SELNEZ_S = ((6 << 3) + 7), SELNEZ_S = ((6U << 3) + 7),
// Multiply integers in r6. // Multiply integers in r6.
MUL_MUH = ((3 << 3) + 0), // MUL, MUH. MUL_MUH = ((3U << 3) + 0), // MUL, MUH.
MUL_MUH_U = ((3 << 3) + 1), // MUL_U, MUH_U. MUL_MUH_U = ((3U << 3) + 1), // MUL_U, MUH_U.
RINT = ((3 << 3) + 2), RINT = ((3U << 3) + 2),
MUL_OP = ((0 << 3) + 2), MUL_OP = ((0U << 3) + 2),
MUH_OP = ((0 << 3) + 3), MUH_OP = ((0U << 3) + 3),
DIV_OP = ((0 << 3) + 2), DIV_OP = ((0U << 3) + 2),
MOD_OP = ((0 << 3) + 3), MOD_OP = ((0U << 3) + 3),
DIV_MOD = ((3 << 3) + 2), DIV_MOD = ((3U << 3) + 2),
DIV_MOD_U = ((3 << 3) + 3), DIV_MOD_U = ((3U << 3) + 3),
// SPECIAL2 Encoding of Function Field. // SPECIAL2 Encoding of Function Field.
MUL = ((0 << 3) + 2), MUL = ((0U << 3) + 2),
CLZ = ((4 << 3) + 0), CLZ = ((4U << 3) + 0),
CLO = ((4 << 3) + 1), CLO = ((4U << 3) + 1),
// SPECIAL3 Encoding of Function Field. // SPECIAL3 Encoding of Function Field.
EXT = ((0 << 3) + 0), EXT = ((0U << 3) + 0),
INS = ((0 << 3) + 4), INS = ((0U << 3) + 4),
BSHFL = ((4 << 3) + 0), BSHFL = ((4U << 3) + 0),
// SPECIAL3 Encoding of sa Field. // SPECIAL3 Encoding of sa Field.
BITSWAP = ((0 << 3) + 0), BITSWAP = ((0U << 3) + 0),
ALIGN = ((0 << 3) + 2), ALIGN = ((0U << 3) + 2),
WSBH = ((0 << 3) + 2), WSBH = ((0U << 3) + 2),
SEB = ((2 << 3) + 0), SEB = ((2U << 3) + 0),
SEH = ((3 << 3) + 0), SEH = ((3U << 3) + 0),
// REGIMM encoding of rt Field. // REGIMM encoding of rt Field.
BLTZ = ((0 << 3) + 0) << 16, BLTZ = ((0U << 3) + 0) << 16,
BGEZ = ((0 << 3) + 1) << 16, BGEZ = ((0U << 3) + 1) << 16,
BLTZAL = ((2 << 3) + 0) << 16, BLTZAL = ((2U << 3) + 0) << 16,
BGEZAL = ((2 << 3) + 1) << 16, BGEZAL = ((2U << 3) + 1) << 16,
BGEZALL = ((2 << 3) + 3) << 16, BGEZALL = ((2U << 3) + 3) << 16,
// COP1 Encoding of rs Field. // COP1 Encoding of rs Field.
MFC1 = ((0 << 3) + 0) << 21, MFC1 = ((0U << 3) + 0) << 21,
CFC1 = ((0 << 3) + 2) << 21, CFC1 = ((0U << 3) + 2) << 21,
MFHC1 = ((0 << 3) + 3) << 21, MFHC1 = ((0U << 3) + 3) << 21,
MTC1 = ((0 << 3) + 4) << 21, MTC1 = ((0U << 3) + 4) << 21,
CTC1 = ((0 << 3) + 6) << 21, CTC1 = ((0U << 3) + 6) << 21,
MTHC1 = ((0 << 3) + 7) << 21, MTHC1 = ((0U << 3) + 7) << 21,
BC1 = ((1 << 3) + 0) << 21, BC1 = ((1U << 3) + 0) << 21,
S = ((2 << 3) + 0) << 21, S = ((2U << 3) + 0) << 21,
D = ((2 << 3) + 1) << 21, D = ((2U << 3) + 1) << 21,
W = ((2 << 3) + 4) << 21, W = ((2U << 3) + 4) << 21,
L = ((2 << 3) + 5) << 21, L = ((2U << 3) + 5) << 21,
PS = ((2 << 3) + 6) << 21, PS = ((2U << 3) + 6) << 21,
// COP1 Encoding of Function Field When rs=S. // COP1 Encoding of Function Field When rs=S.
ADD_S = ((0 << 3) + 0), ADD_S = ((0U << 3) + 0),
SUB_S = ((0 << 3) + 1), SUB_S = ((0U << 3) + 1),
MUL_S = ((0 << 3) + 2), MUL_S = ((0U << 3) + 2),
DIV_S = ((0 << 3) + 3), DIV_S = ((0U << 3) + 3),
ABS_S = ((0 << 3) + 5), ABS_S = ((0U << 3) + 5),
SQRT_S = ((0 << 3) + 4), SQRT_S = ((0U << 3) + 4),
MOV_S = ((0 << 3) + 6), MOV_S = ((0U << 3) + 6),
NEG_S = ((0 << 3) + 7), NEG_S = ((0U << 3) + 7),
ROUND_L_S = ((1 << 3) + 0), ROUND_L_S = ((1U << 3) + 0),
TRUNC_L_S = ((1 << 3) + 1), TRUNC_L_S = ((1U << 3) + 1),
CEIL_L_S = ((1 << 3) + 2), CEIL_L_S = ((1U << 3) + 2),
FLOOR_L_S = ((1 << 3) + 3), FLOOR_L_S = ((1U << 3) + 3),
ROUND_W_S = ((1 << 3) + 4), ROUND_W_S = ((1U << 3) + 4),
TRUNC_W_S = ((1 << 3) + 5), TRUNC_W_S = ((1U << 3) + 5),
CEIL_W_S = ((1 << 3) + 6), CEIL_W_S = ((1U << 3) + 6),
FLOOR_W_S = ((1 << 3) + 7), FLOOR_W_S = ((1U << 3) + 7),
RECIP_S = ((2 << 3) + 5), RECIP_S = ((2U << 3) + 5),
RSQRT_S = ((2 << 3) + 6), RSQRT_S = ((2U << 3) + 6),
CLASS_S = ((3 << 3) + 3), CLASS_S = ((3U << 3) + 3),
CVT_D_S = ((4 << 3) + 1), CVT_D_S = ((4U << 3) + 1),
CVT_W_S = ((4 << 3) + 4), CVT_W_S = ((4U << 3) + 4),
CVT_L_S = ((4 << 3) + 5), CVT_L_S = ((4U << 3) + 5),
CVT_PS_S = ((4 << 3) + 6), CVT_PS_S = ((4U << 3) + 6),
// COP1 Encoding of Function Field When rs=D. // COP1 Encoding of Function Field When rs=D.
ADD_D = ((0 << 3) + 0), ADD_D = ((0U << 3) + 0),
SUB_D = ((0 << 3) + 1), SUB_D = ((0U << 3) + 1),
MUL_D = ((0 << 3) + 2), MUL_D = ((0U << 3) + 2),
DIV_D = ((0 << 3) + 3), DIV_D = ((0U << 3) + 3),
SQRT_D = ((0 << 3) + 4), SQRT_D = ((0U << 3) + 4),
ABS_D = ((0 << 3) + 5), ABS_D = ((0U << 3) + 5),
MOV_D = ((0 << 3) + 6), MOV_D = ((0U << 3) + 6),
NEG_D = ((0 << 3) + 7), NEG_D = ((0U << 3) + 7),
ROUND_L_D = ((1 << 3) + 0), ROUND_L_D = ((1U << 3) + 0),
TRUNC_L_D = ((1 << 3) + 1), TRUNC_L_D = ((1U << 3) + 1),
CEIL_L_D = ((1 << 3) + 2), CEIL_L_D = ((1U << 3) + 2),
FLOOR_L_D = ((1 << 3) + 3), FLOOR_L_D = ((1U << 3) + 3),
ROUND_W_D = ((1 << 3) + 4), ROUND_W_D = ((1U << 3) + 4),
TRUNC_W_D = ((1 << 3) + 5), TRUNC_W_D = ((1U << 3) + 5),
CEIL_W_D = ((1 << 3) + 6), CEIL_W_D = ((1U << 3) + 6),
FLOOR_W_D = ((1 << 3) + 7), FLOOR_W_D = ((1U << 3) + 7),
RECIP_D = ((2 << 3) + 5), RECIP_D = ((2U << 3) + 5),
RSQRT_D = ((2 << 3) + 6), RSQRT_D = ((2U << 3) + 6),
CLASS_D = ((3 << 3) + 3), CLASS_D = ((3U << 3) + 3),
MIN = ((3 << 3) + 4), MIN = ((3U << 3) + 4),
MINA = ((3 << 3) + 5), MINA = ((3U << 3) + 5),
MAX = ((3 << 3) + 6), MAX = ((3U << 3) + 6),
MAXA = ((3 << 3) + 7), MAXA = ((3U << 3) + 7),
CVT_S_D = ((4 << 3) + 0), CVT_S_D = ((4U << 3) + 0),
CVT_W_D = ((4 << 3) + 4), CVT_W_D = ((4U << 3) + 4),
CVT_L_D = ((4 << 3) + 5), CVT_L_D = ((4U << 3) + 5),
C_F_D = ((6 << 3) + 0), C_F_D = ((6U << 3) + 0),
C_UN_D = ((6 << 3) + 1), C_UN_D = ((6U << 3) + 1),
C_EQ_D = ((6 << 3) + 2), C_EQ_D = ((6U << 3) + 2),
C_UEQ_D = ((6 << 3) + 3), C_UEQ_D = ((6U << 3) + 3),
C_OLT_D = ((6 << 3) + 4), C_OLT_D = ((6U << 3) + 4),
C_ULT_D = ((6 << 3) + 5), C_ULT_D = ((6U << 3) + 5),
C_OLE_D = ((6 << 3) + 6), C_OLE_D = ((6U << 3) + 6),
C_ULE_D = ((6 << 3) + 7), C_ULE_D = ((6U << 3) + 7),
// COP1 Encoding of Function Field When rs=W or L. // COP1 Encoding of Function Field When rs=W or L.
CVT_S_W = ((4 << 3) + 0), CVT_S_W = ((4U << 3) + 0),
CVT_D_W = ((4 << 3) + 1), CVT_D_W = ((4U << 3) + 1),
CVT_S_L = ((4 << 3) + 0), CVT_S_L = ((4U << 3) + 0),
CVT_D_L = ((4 << 3) + 1), CVT_D_L = ((4U << 3) + 1),
BC1EQZ = ((2 << 2) + 1) << 21, BC1EQZ = ((2U << 2) + 1) << 21,
BC1NEZ = ((3 << 2) + 1) << 21, BC1NEZ = ((3U << 2) + 1) << 21,
// COP1 CMP positive predicates Bit 5..4 = 00. // COP1 CMP positive predicates Bit 5..4 = 00.
CMP_AF = ((0 << 3) + 0), CMP_AF = ((0U << 3) + 0),
CMP_UN = ((0 << 3) + 1), CMP_UN = ((0U << 3) + 1),
CMP_EQ = ((0 << 3) + 2), CMP_EQ = ((0U << 3) + 2),
CMP_UEQ = ((0 << 3) + 3), CMP_UEQ = ((0U << 3) + 3),
CMP_LT = ((0 << 3) + 4), CMP_LT = ((0U << 3) + 4),
CMP_ULT = ((0 << 3) + 5), CMP_ULT = ((0U << 3) + 5),
CMP_LE = ((0 << 3) + 6), CMP_LE = ((0U << 3) + 6),
CMP_ULE = ((0 << 3) + 7), CMP_ULE = ((0U << 3) + 7),
CMP_SAF = ((1 << 3) + 0), CMP_SAF = ((1U << 3) + 0),
CMP_SUN = ((1 << 3) + 1), CMP_SUN = ((1U << 3) + 1),
CMP_SEQ = ((1 << 3) + 2), CMP_SEQ = ((1U << 3) + 2),
CMP_SUEQ = ((1 << 3) + 3), CMP_SUEQ = ((1U << 3) + 3),
CMP_SSLT = ((1 << 3) + 4), CMP_SSLT = ((1U << 3) + 4),
CMP_SSULT = ((1 << 3) + 5), CMP_SSULT = ((1U << 3) + 5),
CMP_SLE = ((1 << 3) + 6), CMP_SLE = ((1U << 3) + 6),
CMP_SULE = ((1 << 3) + 7), CMP_SULE = ((1U << 3) + 7),
// COP1 CMP negative predicates Bit 5..4 = 01. // COP1 CMP negative predicates Bit 5..4 = 01.
CMP_AT = ((2 << 3) + 0), // Reserved, not implemented. CMP_AT = ((2U << 3) + 0), // Reserved, not implemented.
CMP_OR = ((2 << 3) + 1), CMP_OR = ((2U << 3) + 1),
CMP_UNE = ((2 << 3) + 2), CMP_UNE = ((2U << 3) + 2),
CMP_NE = ((2 << 3) + 3), CMP_NE = ((2U << 3) + 3),
CMP_UGE = ((2 << 3) + 4), // Reserved, not implemented. CMP_UGE = ((2U << 3) + 4), // Reserved, not implemented.
CMP_OGE = ((2 << 3) + 5), // Reserved, not implemented. CMP_OGE = ((2U << 3) + 5), // Reserved, not implemented.
CMP_UGT = ((2 << 3) + 6), // Reserved, not implemented. CMP_UGT = ((2U << 3) + 6), // Reserved, not implemented.
CMP_OGT = ((2 << 3) + 7), // Reserved, not implemented. CMP_OGT = ((2U << 3) + 7), // Reserved, not implemented.
CMP_SAT = ((3 << 3) + 0), // Reserved, not implemented. CMP_SAT = ((3U << 3) + 0), // Reserved, not implemented.
CMP_SOR = ((3 << 3) + 1), CMP_SOR = ((3U << 3) + 1),
CMP_SUNE = ((3 << 3) + 2), CMP_SUNE = ((3U << 3) + 2),
CMP_SNE = ((3 << 3) + 3), CMP_SNE = ((3U << 3) + 3),
CMP_SUGE = ((3 << 3) + 4), // Reserved, not implemented. CMP_SUGE = ((3U << 3) + 4), // Reserved, not implemented.
CMP_SOGE = ((3 << 3) + 5), // Reserved, not implemented. CMP_SOGE = ((3U << 3) + 5), // Reserved, not implemented.
CMP_SUGT = ((3 << 3) + 6), // Reserved, not implemented. CMP_SUGT = ((3U << 3) + 6), // Reserved, not implemented.
CMP_SOGT = ((3 << 3) + 7), // Reserved, not implemented. CMP_SOGT = ((3U << 3) + 7), // Reserved, not implemented.
SEL = ((2 << 3) + 0), SEL = ((2U << 3) + 0),
MOVZ_C = ((2 << 3) + 2), MOVZ_C = ((2U << 3) + 2),
MOVN_C = ((2 << 3) + 3), MOVN_C = ((2U << 3) + 3),
SELEQZ_C = ((2 << 3) + 4), // COP1 on FPR registers. SELEQZ_C = ((2U << 3) + 4), // COP1 on FPR registers.
MOVF = ((2 << 3) + 1), // Function field for MOVT.fmt and MOVF.fmt MOVF = ((2U << 3) + 1), // Function field for MOVT.fmt and MOVF.fmt
SELNEZ_C = ((2 << 3) + 7), // COP1 on FPR registers. SELNEZ_C = ((2U << 3) + 7), // COP1 on FPR registers.
// COP1 Encoding of Function Field When rs=PS. // COP1 Encoding of Function Field When rs=PS.
// COP1X Encoding of Function Field. // COP1X Encoding of Function Field.
MADD_D = ((4 << 3) + 1), MADD_D = ((4U << 3) + 1),
// PCREL Encoding of rt Field. // PCREL Encoding of rt Field.
ADDIUPC = ((0 << 2) + 0), ADDIUPC = ((0U << 2) + 0),
LWPC = ((0 << 2) + 1), LWPC = ((0U << 2) + 1),
AUIPC = ((3 << 3) + 6), AUIPC = ((3U << 3) + 6),
ALUIPC = ((3 << 3) + 7), ALUIPC = ((3U << 3) + 7),
// POP66 Encoding of rs Field. // POP66 Encoding of rs Field.
JIC = ((0 << 5) + 0), JIC = ((0U << 5) + 0),
// POP76 Encoding of rs Field. // POP76 Encoding of rs Field.
JIALC = ((0 << 5) + 0), JIALC = ((0U << 5) + 0),
NULLSF = 0 NULLSF = 0U
}; };
...@@ -820,6 +828,10 @@ const Instr rtCallRedirInstr = SPECIAL | BREAK | call_rt_redirected << 6; ...@@ -820,6 +828,10 @@ const Instr rtCallRedirInstr = SPECIAL | BREAK | call_rt_redirected << 6;
// A nop instruction. (Encoding of sll 0 0 0). // A nop instruction. (Encoding of sll 0 0 0).
const Instr nopInstr = 0; const Instr nopInstr = 0;
static constexpr uint64_t OpcodeToBitNumber(Opcode opcode) {
return 1ULL << (static_cast<uint32_t>(opcode) >> kOpcodeShift);
}
class Instruction { class Instruction {
public: public:
...@@ -848,7 +860,7 @@ class Instruction { ...@@ -848,7 +860,7 @@ class Instruction {
// Read a bit field out of the instruction bits. // Read a bit field out of the instruction bits.
inline int Bits(int hi, int lo) const { inline int Bits(int hi, int lo) const {
return (InstructionBits() >> lo) & ((2 << (hi - lo)) - 1); return (InstructionBits() >> lo) & ((2U << (hi - lo)) - 1);
} }
// Instruction type. // Instruction type.
...@@ -862,10 +874,7 @@ class Instruction { ...@@ -862,10 +874,7 @@ class Instruction {
enum TypeChecks { NORMAL, EXTRA }; enum TypeChecks { NORMAL, EXTRA };
#define OpcodeToBitNumber(opcode) \ static constexpr uint64_t kOpcodeImmediateTypeMask =
(1ULL << (static_cast<uint32_t>(opcode) >> kOpcodeShift))
static const uint64_t kOpcodeImmediateTypeMask =
OpcodeToBitNumber(REGIMM) | OpcodeToBitNumber(BEQ) | OpcodeToBitNumber(REGIMM) | OpcodeToBitNumber(BEQ) |
OpcodeToBitNumber(BNE) | OpcodeToBitNumber(BLEZ) | OpcodeToBitNumber(BNE) | OpcodeToBitNumber(BLEZ) |
OpcodeToBitNumber(BGTZ) | OpcodeToBitNumber(ADDI) | OpcodeToBitNumber(BGTZ) | OpcodeToBitNumber(ADDI) |
...@@ -1032,6 +1041,11 @@ class Instruction { ...@@ -1032,6 +1041,11 @@ class Instruction {
} }
} }
inline int32_t ImmValue(int bits) const {
DCHECK(InstructionType() == kImmediateType);
return Bits(bits - 1, 0);
}
inline int32_t Imm16Value() const { inline int32_t Imm16Value() const {
DCHECK(InstructionType() == kImmediateType); DCHECK(InstructionType() == kImmediateType);
return Bits(kImm16Shift + kImm16Bits - 1, kImm16Shift); return Bits(kImm16Shift + kImm16Bits - 1, kImm16Shift);
...@@ -1058,8 +1072,18 @@ class Instruction { ...@@ -1058,8 +1072,18 @@ class Instruction {
return Bits(kImm26Shift + kImm26Bits - 1, kImm26Shift); return Bits(kImm26Shift + kImm26Bits - 1, kImm26Shift);
} }
// Say if the instruction should not be used in a branch delay slot. static bool IsForbiddenAfterBranchInstr(Instr instr);
bool IsForbiddenInBranchDelay() const;
// Say if the instruction should not be used in a branch delay slot or
// immediately after a compact branch.
inline bool IsForbiddenAfterBranch() const {
return IsForbiddenAfterBranchInstr(InstructionBits());
}
inline bool IsForbiddenInBranchDelay() const {
return IsForbiddenAfterBranch();
}
// Say if the instruction 'links'. e.g. jal, bal. // Say if the instruction 'links'. e.g. jal, bal.
bool IsLinkingInstruction() const; bool IsLinkingInstruction() const;
// Say if the instruction is a break or a trap. // Say if the instruction is a break or a trap.
......
...@@ -1377,12 +1377,12 @@ void Decoder::DecodeTypeImmediate(Instruction* instr) { ...@@ -1377,12 +1377,12 @@ void Decoder::DecodeTypeImmediate(Instruction* instr) {
Format(instr, "blez 'rs, 'imm16u -> 'imm16p4s2"); Format(instr, "blez 'rs, 'imm16u -> 'imm16p4s2");
} else if ((instr->RtValue() != instr->RsValue()) && } else if ((instr->RtValue() != instr->RsValue()) &&
(instr->RsValue() != 0) && (instr->RtValue() != 0)) { (instr->RsValue() != 0) && (instr->RtValue() != 0)) {
Format(instr, "bgeuc 'rs, 'rt, 'imm16u -> 'imm16p4s2"); Format(instr, "bgeuc 'rs, 'rt, 'imm16u -> 'imm16p4s2");
} else if ((instr->RtValue() == instr->RsValue()) && } else if ((instr->RtValue() == instr->RsValue()) &&
(instr->RtValue() != 0)) { (instr->RtValue() != 0)) {
Format(instr, "bgezalc 'rs, 'imm16u -> 'imm16p4s2"); Format(instr, "bgezalc 'rs, 'imm16u -> 'imm16p4s2");
} else if ((instr->RsValue() == 0) && (instr->RtValue() != 0)) { } else if ((instr->RsValue() == 0) && (instr->RtValue() != 0)) {
Format(instr, "blezalc 'rt, 'imm16u -> 'imm16p4s2"); Format(instr, "blezalc 'rt, 'imm16u -> 'imm16p4s2");
} else { } else {
UNREACHABLE(); UNREACHABLE();
} }
...@@ -1419,7 +1419,7 @@ void Decoder::DecodeTypeImmediate(Instruction* instr) { ...@@ -1419,7 +1419,7 @@ void Decoder::DecodeTypeImmediate(Instruction* instr) {
Format(instr, "bltzc 'rt, 'imm16u -> 'imm16p4s2"); Format(instr, "bltzc 'rt, 'imm16u -> 'imm16p4s2");
} else if ((instr->RtValue() != instr->RsValue()) && } else if ((instr->RtValue() != instr->RsValue()) &&
(instr->RsValue() != 0) && (instr->RtValue() != 0)) { (instr->RsValue() != 0) && (instr->RtValue() != 0)) {
Format(instr, "bltc 'rs, 'rt, 'imm16u -> 'imm16p4s2"); Format(instr, "bltc 'rs, 'rt, 'imm16u -> 'imm16p4s2");
} else if ((instr->RsValue() == 0) && (instr->RtValue() != 0)) { } else if ((instr->RsValue() == 0) && (instr->RtValue() != 0)) {
Format(instr, "bgtzc 'rt, 'imm16u -> 'imm16p4s2"); Format(instr, "bgtzc 'rt, 'imm16u -> 'imm16p4s2");
} else { } else {
...@@ -1445,25 +1445,33 @@ void Decoder::DecodeTypeImmediate(Instruction* instr) { ...@@ -1445,25 +1445,33 @@ void Decoder::DecodeTypeImmediate(Instruction* instr) {
if (!IsMipsArchVariant(kMips32r6)) { if (!IsMipsArchVariant(kMips32r6)) {
Format(instr, "addi 'rt, 'rs, 'imm16s"); Format(instr, "addi 'rt, 'rs, 'imm16s");
} else { } else {
// Check if BOVC or BEQC instruction. int rs_reg = instr->RsValue();
if (instr->RsValue() >= instr->RtValue()) { int rt_reg = instr->RtValue();
// Check if BOVC, BEQZALC or BEQC instruction.
if (rs_reg >= rt_reg) {
Format(instr, "bovc 'rs, 'rt, 'imm16s -> 'imm16p4s2"); Format(instr, "bovc 'rs, 'rt, 'imm16s -> 'imm16p4s2");
} else if (instr->RsValue() < instr->RtValue()) {
Format(instr, "beqc 'rs, 'rt, 'imm16s -> 'imm16p4s2");
} else { } else {
UNREACHABLE(); if (rs_reg == 0) {
Format(instr, "beqzalc 'rt, 'imm16s -> 'imm16p4s2");
} else {
Format(instr, "beqc 'rs, 'rt, 'imm16s -> 'imm16p4s2");
}
} }
} }
break; break;
case DADDI: case DADDI:
if (IsMipsArchVariant(kMips32r6)) { if (IsMipsArchVariant(kMips32r6)) {
// Check if BNVC or BNEC instruction. int rs_reg = instr->RsValue();
if (instr->RsValue() >= instr->RtValue()) { int rt_reg = instr->RtValue();
// Check if BNVC, BNEZALC or BNEC instruction.
if (rs_reg >= rt_reg) {
Format(instr, "bnvc 'rs, 'rt, 'imm16s -> 'imm16p4s2"); Format(instr, "bnvc 'rs, 'rt, 'imm16s -> 'imm16p4s2");
} else if (instr->RsValue() < instr->RtValue()) {
Format(instr, "bnec 'rs, 'rt, 'imm16s -> 'imm16p4s2");
} else { } else {
UNREACHABLE(); if (rs_reg == 0) {
Format(instr, "bnezalc 'rt, 'imm16s -> 'imm16p4s2");
} else {
Format(instr, "bnec 'rs, 'rt, 'imm16s -> 'imm16p4s2");
}
} }
} }
break; break;
......
...@@ -1938,21 +1938,23 @@ void MacroAssembler::GetLeastBitsFromInt32(Register dst, ...@@ -1938,21 +1938,23 @@ void MacroAssembler::GetLeastBitsFromInt32(Register dst,
(cond != cc_always && (!rs.is(zero_reg) || !rt.rm().is(zero_reg)))) (cond != cc_always && (!rs.is(zero_reg) || !rt.rm().is(zero_reg))))
void MacroAssembler::Branch(int16_t offset, BranchDelaySlot bdslot) { void MacroAssembler::Branch(int32_t offset, BranchDelaySlot bdslot) {
DCHECK(IsMipsArchVariant(kMips32r6) ? is_int26(offset) : is_int16(offset));
BranchShort(offset, bdslot); BranchShort(offset, bdslot);
} }
void MacroAssembler::Branch(int16_t offset, Condition cond, Register rs, void MacroAssembler::Branch(int32_t offset, Condition cond, Register rs,
const Operand& rt, const Operand& rt, BranchDelaySlot bdslot) {
BranchDelaySlot bdslot) { bool is_near = BranchShortCheck(offset, nullptr, cond, rs, rt, bdslot);
BranchShort(offset, cond, rs, rt, bdslot); DCHECK(is_near);
USE(is_near);
} }
void MacroAssembler::Branch(Label* L, BranchDelaySlot bdslot) { void MacroAssembler::Branch(Label* L, BranchDelaySlot bdslot) {
if (L->is_bound()) { if (L->is_bound()) {
if (is_near(L)) { if (is_near_branch(L)) {
BranchShort(L, bdslot); BranchShort(L, bdslot);
} else { } else {
Jr(L, bdslot); Jr(L, bdslot);
...@@ -1971,9 +1973,7 @@ void MacroAssembler::Branch(Label* L, Condition cond, Register rs, ...@@ -1971,9 +1973,7 @@ void MacroAssembler::Branch(Label* L, Condition cond, Register rs,
const Operand& rt, const Operand& rt,
BranchDelaySlot bdslot) { BranchDelaySlot bdslot) {
if (L->is_bound()) { if (L->is_bound()) {
if (is_near(L)) { if (!BranchShortCheck(0, L, cond, rs, rt, bdslot)) {
BranchShort(L, cond, rs, rt, bdslot);
} else {
if (cond != cc_always) { if (cond != cc_always) {
Label skip; Label skip;
Condition neg_cond = NegateCondition(cond); Condition neg_cond = NegateCondition(cond);
...@@ -2012,7 +2012,9 @@ void MacroAssembler::Branch(Label* L, ...@@ -2012,7 +2012,9 @@ void MacroAssembler::Branch(Label* L,
} }
void MacroAssembler::BranchShort(int16_t offset, BranchDelaySlot bdslot) { void MacroAssembler::BranchShortHelper(int16_t offset, Label* L,
BranchDelaySlot bdslot) {
offset = GetOffset(offset, L, OffsetSize::kOffset16);
b(offset); b(offset);
// Emit a nop in the branch delay slot if required. // Emit a nop in the branch delay slot if required.
...@@ -2021,542 +2023,529 @@ void MacroAssembler::BranchShort(int16_t offset, BranchDelaySlot bdslot) { ...@@ -2021,542 +2023,529 @@ void MacroAssembler::BranchShort(int16_t offset, BranchDelaySlot bdslot) {
} }
void MacroAssembler::BranchShort(int16_t offset, Condition cond, Register rs, void MacroAssembler::BranchShortHelperR6(int32_t offset, Label* L) {
const Operand& rt, offset = GetOffset(offset, L, OffsetSize::kOffset26);
BranchDelaySlot bdslot) { bc(offset);
BRANCH_ARGS_CHECK(cond, rs, rt); }
DCHECK(!rs.is(zero_reg));
Register r2 = no_reg;
Register scratch = at; void MacroAssembler::BranchShort(int32_t offset, BranchDelaySlot bdslot) {
if (IsMipsArchVariant(kMips32r6) && bdslot == PROTECT) {
DCHECK(is_int26(offset));
BranchShortHelperR6(offset, nullptr);
} else {
DCHECK(is_int16(offset));
BranchShortHelper(offset, nullptr, bdslot);
}
}
void MacroAssembler::BranchShort(Label* L, BranchDelaySlot bdslot) {
if (IsMipsArchVariant(kMips32r6) && bdslot == PROTECT) {
BranchShortHelperR6(0, L);
} else {
BranchShortHelper(0, L, bdslot);
}
}
static inline bool IsZero(const Operand& rt) {
if (rt.is_reg()) {
return rt.rm().is(zero_reg);
} else {
return rt.immediate() == 0;
}
}
int32_t MacroAssembler::GetOffset(int32_t offset, Label* L, OffsetSize bits) {
if (L) {
offset = branch_offset_helper(L, bits) >> 2;
} else {
DCHECK(is_intn(offset, bits));
}
return offset;
}
Register MacroAssembler::GetRtAsRegisterHelper(const Operand& rt,
Register scratch) {
Register r2 = no_reg;
if (rt.is_reg()) { if (rt.is_reg()) {
// NOTE: 'at' can be clobbered by Branch but it is legal to use it as rs or
// rt.
BlockTrampolinePoolScope block_trampoline_pool(this);
r2 = rt.rm_; r2 = rt.rm_;
switch (cond) {
case cc_always:
b(offset);
break;
case eq:
beq(rs, r2, offset);
break;
case ne:
bne(rs, r2, offset);
break;
// Signed comparison.
case greater:
if (r2.is(zero_reg)) {
bgtz(rs, offset);
} else {
slt(scratch, r2, rs);
bne(scratch, zero_reg, offset);
}
break;
case greater_equal:
if (r2.is(zero_reg)) {
bgez(rs, offset);
} else {
slt(scratch, rs, r2);
beq(scratch, zero_reg, offset);
}
break;
case less:
if (r2.is(zero_reg)) {
bltz(rs, offset);
} else {
slt(scratch, rs, r2);
bne(scratch, zero_reg, offset);
}
break;
case less_equal:
if (r2.is(zero_reg)) {
blez(rs, offset);
} else {
slt(scratch, r2, rs);
beq(scratch, zero_reg, offset);
}
break;
// Unsigned comparison.
case Ugreater:
if (r2.is(zero_reg)) {
bne(rs, zero_reg, offset);
} else {
sltu(scratch, r2, rs);
bne(scratch, zero_reg, offset);
}
break;
case Ugreater_equal:
if (r2.is(zero_reg)) {
b(offset);
} else {
sltu(scratch, rs, r2);
beq(scratch, zero_reg, offset);
}
break;
case Uless:
if (r2.is(zero_reg)) {
// No code needs to be emitted.
return;
} else {
sltu(scratch, rs, r2);
bne(scratch, zero_reg, offset);
}
break;
case Uless_equal:
if (r2.is(zero_reg)) {
beq(rs, zero_reg, offset);
} else {
sltu(scratch, r2, rs);
beq(scratch, zero_reg, offset);
}
break;
default:
UNREACHABLE();
}
} else { } else {
// Be careful to always use shifted_branch_offset only just before the r2 = scratch;
// branch instruction, as the location will be remember for patching the li(r2, rt);
// target.
BlockTrampolinePoolScope block_trampoline_pool(this);
switch (cond) {
case cc_always:
b(offset);
break;
case eq:
if (rt.imm32_ == 0) {
beq(rs, zero_reg, offset);
} else {
// We don't want any other register but scratch clobbered.
DCHECK(!scratch.is(rs));
r2 = scratch;
li(r2, rt);
beq(rs, r2, offset);
}
break;
case ne:
if (rt.imm32_ == 0) {
bne(rs, zero_reg, offset);
} else {
// We don't want any other register but scratch clobbered.
DCHECK(!scratch.is(rs));
r2 = scratch;
li(r2, rt);
bne(rs, r2, offset);
}
break;
// Signed comparison.
case greater:
if (rt.imm32_ == 0) {
bgtz(rs, offset);
} else {
r2 = scratch;
li(r2, rt);
slt(scratch, r2, rs);
bne(scratch, zero_reg, offset);
}
break;
case greater_equal:
if (rt.imm32_ == 0) {
bgez(rs, offset);
} else if (is_int16(rt.imm32_)) {
slti(scratch, rs, rt.imm32_);
beq(scratch, zero_reg, offset);
} else {
r2 = scratch;
li(r2, rt);
slt(scratch, rs, r2);
beq(scratch, zero_reg, offset);
}
break;
case less:
if (rt.imm32_ == 0) {
bltz(rs, offset);
} else if (is_int16(rt.imm32_)) {
slti(scratch, rs, rt.imm32_);
bne(scratch, zero_reg, offset);
} else {
r2 = scratch;
li(r2, rt);
slt(scratch, rs, r2);
bne(scratch, zero_reg, offset);
}
break;
case less_equal:
if (rt.imm32_ == 0) {
blez(rs, offset);
} else {
r2 = scratch;
li(r2, rt);
slt(scratch, r2, rs);
beq(scratch, zero_reg, offset);
}
break;
// Unsigned comparison.
case Ugreater:
if (rt.imm32_ == 0) {
bne(rs, zero_reg, offset);
} else {
r2 = scratch;
li(r2, rt);
sltu(scratch, r2, rs);
bne(scratch, zero_reg, offset);
}
break;
case Ugreater_equal:
if (rt.imm32_ == 0) {
b(offset);
} else if (is_int16(rt.imm32_)) {
sltiu(scratch, rs, rt.imm32_);
beq(scratch, zero_reg, offset);
} else {
r2 = scratch;
li(r2, rt);
sltu(scratch, rs, r2);
beq(scratch, zero_reg, offset);
}
break;
case Uless:
if (rt.imm32_ == 0) {
// No code needs to be emitted.
return;
} else if (is_int16(rt.imm32_)) {
sltiu(scratch, rs, rt.imm32_);
bne(scratch, zero_reg, offset);
} else {
r2 = scratch;
li(r2, rt);
sltu(scratch, rs, r2);
bne(scratch, zero_reg, offset);
}
break;
case Uless_equal:
if (rt.imm32_ == 0) {
beq(rs, zero_reg, offset);
} else {
r2 = scratch;
li(r2, rt);
sltu(scratch, r2, rs);
beq(scratch, zero_reg, offset);
}
break;
default:
UNREACHABLE();
}
} }
// Emit a nop in the branch delay slot if required.
if (bdslot == PROTECT) return r2;
nop();
} }
void MacroAssembler::BranchShort(Label* L, BranchDelaySlot bdslot) { bool MacroAssembler::BranchShortHelperR6(int32_t offset, Label* L,
// We use branch_offset as an argument for the branch instructions to be sure Condition cond, Register rs,
// it is called just before generating the branch instruction, as needed. const Operand& rt) {
Register scratch = rs.is(at) ? t8 : at;
OffsetSize bits = OffsetSize::kOffset16;
// Be careful to always use shifted_branch_offset only just before the
// branch instruction, as the location will be remember for patching the
// target.
BlockTrampolinePoolScope block_trampoline_pool(this);
switch (cond) {
case cc_always:
bits = OffsetSize::kOffset26;
if (!is_near(L, bits)) return false;
offset = GetOffset(offset, L, bits);
bc(offset);
break;
case eq:
if (rs.code() == rt.rm_.reg_code) {
// Pre R6 beq is used here to make the code patchable. Otherwise bc
// should be used which has no condition field so is not patchable.
bits = OffsetSize::kOffset16;
if (!is_near(L, bits)) return false;
scratch = GetRtAsRegisterHelper(rt, scratch);
offset = GetOffset(offset, L, bits);
beq(rs, scratch, offset);
nop();
} else if (IsZero(rt)) {
bits = OffsetSize::kOffset21;
if (!is_near(L, bits)) return false;
offset = GetOffset(offset, L, bits);
beqzc(rs, offset);
} else {
// We don't want any other register but scratch clobbered.
bits = OffsetSize::kOffset16;
if (!is_near(L, bits)) return false;
scratch = GetRtAsRegisterHelper(rt, scratch);
offset = GetOffset(offset, L, bits);
beqc(rs, scratch, offset);
}
break;
case ne:
if (rs.code() == rt.rm_.reg_code) {
// Pre R6 bne is used here to make the code patchable. Otherwise we
// should not generate any instruction.
bits = OffsetSize::kOffset16;
if (!is_near(L, bits)) return false;
scratch = GetRtAsRegisterHelper(rt, scratch);
offset = GetOffset(offset, L, bits);
bne(rs, scratch, offset);
nop();
} else if (IsZero(rt)) {
bits = OffsetSize::kOffset21;
if (!is_near(L, bits)) return false;
offset = GetOffset(offset, L, bits);
bnezc(rs, offset);
} else {
// We don't want any other register but scratch clobbered.
bits = OffsetSize::kOffset16;
if (!is_near(L, bits)) return false;
scratch = GetRtAsRegisterHelper(rt, scratch);
offset = GetOffset(offset, L, bits);
bnec(rs, scratch, offset);
}
break;
b(shifted_branch_offset(L, false)); // Signed comparison.
case greater:
// rs > rt
if (rs.code() == rt.rm_.reg_code) {
break; // No code needs to be emitted.
} else if (rs.is(zero_reg)) {
bits = OffsetSize::kOffset16;
if (!is_near(L, bits)) return false;
scratch = GetRtAsRegisterHelper(rt, scratch);
offset = GetOffset(offset, L, bits);
bltzc(scratch, offset);
} else if (IsZero(rt)) {
bits = OffsetSize::kOffset16;
if (!is_near(L, bits)) return false;
offset = GetOffset(offset, L, bits);
bgtzc(rs, offset);
} else {
bits = OffsetSize::kOffset16;
if (!is_near(L, bits)) return false;
scratch = GetRtAsRegisterHelper(rt, scratch);
DCHECK(!rs.is(scratch));
offset = GetOffset(offset, L, bits);
bltc(scratch, rs, offset);
}
break;
case greater_equal:
// rs >= rt
if (rs.code() == rt.rm_.reg_code) {
bits = OffsetSize::kOffset26;
if (!is_near(L, bits)) return false;
offset = GetOffset(offset, L, bits);
bc(offset);
} else if (rs.is(zero_reg)) {
bits = OffsetSize::kOffset16;
if (!is_near(L, bits)) return false;
scratch = GetRtAsRegisterHelper(rt, scratch);
offset = GetOffset(offset, L, bits);
blezc(scratch, offset);
} else if (IsZero(rt)) {
bits = OffsetSize::kOffset16;
if (!is_near(L, bits)) return false;
offset = GetOffset(offset, L, bits);
bgezc(rs, offset);
} else {
bits = OffsetSize::kOffset16;
if (!is_near(L, bits)) return false;
scratch = GetRtAsRegisterHelper(rt, scratch);
DCHECK(!rs.is(scratch));
offset = GetOffset(offset, L, bits);
bgec(rs, scratch, offset);
}
break;
case less:
// rs < rt
if (rs.code() == rt.rm_.reg_code) {
break; // No code needs to be emitted.
} else if (rs.is(zero_reg)) {
bits = OffsetSize::kOffset16;
if (!is_near(L, bits)) return false;
scratch = GetRtAsRegisterHelper(rt, scratch);
offset = GetOffset(offset, L, bits);
bgtzc(scratch, offset);
} else if (IsZero(rt)) {
bits = OffsetSize::kOffset16;
if (!is_near(L, bits)) return false;
offset = GetOffset(offset, L, bits);
bltzc(rs, offset);
} else {
bits = OffsetSize::kOffset16;
if (!is_near(L, bits)) return false;
scratch = GetRtAsRegisterHelper(rt, scratch);
DCHECK(!rs.is(scratch));
offset = GetOffset(offset, L, bits);
bltc(rs, scratch, offset);
}
break;
case less_equal:
// rs <= rt
if (rs.code() == rt.rm_.reg_code) {
bits = OffsetSize::kOffset26;
if (!is_near(L, bits)) return false;
offset = GetOffset(offset, L, bits);
bc(offset);
} else if (rs.is(zero_reg)) {
bits = OffsetSize::kOffset16;
if (!is_near(L, bits)) return false;
scratch = GetRtAsRegisterHelper(rt, scratch);
offset = GetOffset(offset, L, bits);
bgezc(scratch, offset);
} else if (IsZero(rt)) {
bits = OffsetSize::kOffset16;
if (!is_near(L, bits)) return false;
offset = GetOffset(offset, L, bits);
blezc(rs, offset);
} else {
bits = OffsetSize::kOffset16;
if (!is_near(L, bits)) return false;
scratch = GetRtAsRegisterHelper(rt, scratch);
DCHECK(!rs.is(scratch));
offset = GetOffset(offset, L, bits);
bgec(scratch, rs, offset);
}
break;
// Unsigned comparison.
case Ugreater:
// rs > rt
if (rs.code() == rt.rm_.reg_code) {
break; // No code needs to be emitted.
} else if (rs.is(zero_reg)) {
bits = OffsetSize::kOffset21;
if (!is_near(L, bits)) return false;
scratch = GetRtAsRegisterHelper(rt, scratch);
offset = GetOffset(offset, L, bits);
bnezc(scratch, offset);
} else if (IsZero(rt)) {
bits = OffsetSize::kOffset21;
if (!is_near(L, bits)) return false;
offset = GetOffset(offset, L, bits);
bnezc(rs, offset);
} else {
bits = OffsetSize::kOffset16;
if (!is_near(L, bits)) return false;
scratch = GetRtAsRegisterHelper(rt, scratch);
DCHECK(!rs.is(scratch));
offset = GetOffset(offset, L, bits);
bltuc(scratch, rs, offset);
}
break;
case Ugreater_equal:
// rs >= rt
if (rs.code() == rt.rm_.reg_code) {
bits = OffsetSize::kOffset26;
if (!is_near(L, bits)) return false;
offset = GetOffset(offset, L, bits);
bc(offset);
} else if (rs.is(zero_reg)) {
bits = OffsetSize::kOffset21;
if (!is_near(L, bits)) return false;
scratch = GetRtAsRegisterHelper(rt, scratch);
offset = GetOffset(offset, L, bits);
beqzc(scratch, offset);
} else if (IsZero(rt)) {
bits = OffsetSize::kOffset26;
if (!is_near(L, bits)) return false;
offset = GetOffset(offset, L, bits);
bc(offset);
} else {
bits = OffsetSize::kOffset16;
if (!is_near(L, bits)) return false;
scratch = GetRtAsRegisterHelper(rt, scratch);
DCHECK(!rs.is(scratch));
offset = GetOffset(offset, L, bits);
bgeuc(rs, scratch, offset);
}
break;
case Uless:
// rs < rt
if (rs.code() == rt.rm_.reg_code) {
break; // No code needs to be emitted.
} else if (rs.is(zero_reg)) {
bits = OffsetSize::kOffset21;
if (!is_near(L, bits)) return false;
scratch = GetRtAsRegisterHelper(rt, scratch);
offset = GetOffset(offset, L, bits);
bnezc(scratch, offset);
} else if (IsZero(rt)) {
break; // No code needs to be emitted.
} else {
bits = OffsetSize::kOffset16;
if (!is_near(L, bits)) return false;
scratch = GetRtAsRegisterHelper(rt, scratch);
DCHECK(!rs.is(scratch));
offset = GetOffset(offset, L, bits);
bltuc(rs, scratch, offset);
}
break;
case Uless_equal:
// rs <= rt
if (rs.code() == rt.rm_.reg_code) {
bits = OffsetSize::kOffset26;
if (!is_near(L, bits)) return false;
offset = GetOffset(offset, L, bits);
bc(offset);
} else if (rs.is(zero_reg)) {
bits = OffsetSize::kOffset26;
if (!is_near(L, bits)) return false;
scratch = GetRtAsRegisterHelper(rt, scratch);
offset = GetOffset(offset, L, bits);
bc(offset);
} else if (IsZero(rt)) {
bits = OffsetSize::kOffset21;
if (!is_near(L, bits)) return false;
offset = GetOffset(offset, L, bits);
beqzc(rs, offset);
} else {
bits = OffsetSize::kOffset16;
if (!is_near(L, bits)) return false;
scratch = GetRtAsRegisterHelper(rt, scratch);
DCHECK(!rs.is(scratch));
offset = GetOffset(offset, L, bits);
bgeuc(scratch, rs, offset);
}
break;
default:
UNREACHABLE();
}
return true;
}
bool MacroAssembler::BranchShortHelper(int16_t offset, Label* L, Condition cond,
Register rs, const Operand& rt,
BranchDelaySlot bdslot) {
if (!is_near(L, OffsetSize::kOffset16)) return false;
Register scratch = at;
int32_t offset32;
// Be careful to always use shifted_branch_offset only just before the
// branch instruction, as the location will be remember for patching the
// target.
BlockTrampolinePoolScope block_trampoline_pool(this);
switch (cond) {
case cc_always:
offset32 = GetOffset(offset, L, OffsetSize::kOffset16);
b(offset32);
break;
case eq:
if (IsZero(rt)) {
offset32 = GetOffset(offset, L, OffsetSize::kOffset16);
beq(rs, zero_reg, offset32);
} else {
// We don't want any other register but scratch clobbered.
scratch = GetRtAsRegisterHelper(rt, scratch);
offset32 = GetOffset(offset, L, OffsetSize::kOffset16);
beq(rs, scratch, offset32);
}
break;
case ne:
if (IsZero(rt)) {
offset32 = GetOffset(offset, L, OffsetSize::kOffset16);
bne(rs, zero_reg, offset32);
} else {
// We don't want any other register but scratch clobbered.
scratch = GetRtAsRegisterHelper(rt, scratch);
offset32 = GetOffset(offset, L, OffsetSize::kOffset16);
bne(rs, scratch, offset32);
}
break;
// Signed comparison.
case greater:
if (IsZero(rt)) {
offset32 = GetOffset(offset, L, OffsetSize::kOffset16);
bgtz(rs, offset32);
} else {
Slt(scratch, GetRtAsRegisterHelper(rt, scratch), rs);
offset32 = GetOffset(offset, L, OffsetSize::kOffset16);
bne(scratch, zero_reg, offset32);
}
break;
case greater_equal:
if (IsZero(rt)) {
offset32 = GetOffset(offset, L, OffsetSize::kOffset16);
bgez(rs, offset32);
} else {
Slt(scratch, rs, rt);
offset32 = GetOffset(offset, L, OffsetSize::kOffset16);
beq(scratch, zero_reg, offset32);
}
break;
case less:
if (IsZero(rt)) {
offset32 = GetOffset(offset, L, OffsetSize::kOffset16);
bltz(rs, offset32);
} else {
Slt(scratch, rs, rt);
offset32 = GetOffset(offset, L, OffsetSize::kOffset16);
bne(scratch, zero_reg, offset32);
}
break;
case less_equal:
if (IsZero(rt)) {
offset32 = GetOffset(offset, L, OffsetSize::kOffset16);
blez(rs, offset32);
} else {
Slt(scratch, GetRtAsRegisterHelper(rt, scratch), rs);
offset32 = GetOffset(offset, L, OffsetSize::kOffset16);
beq(scratch, zero_reg, offset32);
}
break;
// Unsigned comparison.
case Ugreater:
if (IsZero(rt)) {
offset32 = GetOffset(offset, L, OffsetSize::kOffset16);
bne(rs, zero_reg, offset32);
} else {
Sltu(scratch, GetRtAsRegisterHelper(rt, scratch), rs);
offset32 = GetOffset(offset, L, OffsetSize::kOffset16);
bne(scratch, zero_reg, offset32);
}
break;
case Ugreater_equal:
if (IsZero(rt)) {
offset32 = GetOffset(offset, L, OffsetSize::kOffset16);
b(offset32);
} else {
Sltu(scratch, rs, rt);
offset32 = GetOffset(offset, L, OffsetSize::kOffset16);
beq(scratch, zero_reg, offset32);
}
break;
case Uless:
if (IsZero(rt)) {
return true; // No code needs to be emitted.
} else {
Sltu(scratch, rs, rt);
offset32 = GetOffset(offset, L, OffsetSize::kOffset16);
bne(scratch, zero_reg, offset32);
}
break;
case Uless_equal:
if (IsZero(rt)) {
offset32 = GetOffset(offset, L, OffsetSize::kOffset16);
beq(rs, zero_reg, offset32);
} else {
Sltu(scratch, GetRtAsRegisterHelper(rt, scratch), rs);
offset32 = GetOffset(offset, L, OffsetSize::kOffset16);
beq(scratch, zero_reg, offset32);
}
break;
default:
UNREACHABLE();
}
// Emit a nop in the branch delay slot if required. // Emit a nop in the branch delay slot if required.
if (bdslot == PROTECT) if (bdslot == PROTECT)
nop(); nop();
return true;
} }
void MacroAssembler::BranchShort(Label* L, Condition cond, Register rs, bool MacroAssembler::BranchShortCheck(int32_t offset, Label* L, Condition cond,
const Operand& rt, Register rs, const Operand& rt,
BranchDelaySlot bdslot) { BranchDelaySlot bdslot) {
BRANCH_ARGS_CHECK(cond, rs, rt); BRANCH_ARGS_CHECK(cond, rs, rt);
int32_t offset = 0; if (!L) {
Register r2 = no_reg; if (IsMipsArchVariant(kMips32r6) && bdslot == PROTECT) {
Register scratch = at; DCHECK(is_int26(offset));
if (rt.is_reg()) { return BranchShortHelperR6(offset, nullptr, cond, rs, rt);
BlockTrampolinePoolScope block_trampoline_pool(this); } else {
r2 = rt.rm_; DCHECK(is_int16(offset));
// Be careful to always use shifted_branch_offset only just before the return BranchShortHelper(offset, nullptr, cond, rs, rt, bdslot);
// branch instruction, as the location will be remember for patching the
// target.
switch (cond) {
case cc_always:
offset = shifted_branch_offset(L, false);
b(offset);
break;
case eq:
offset = shifted_branch_offset(L, false);
beq(rs, r2, offset);
break;
case ne:
offset = shifted_branch_offset(L, false);
bne(rs, r2, offset);
break;
// Signed comparison.
case greater:
if (r2.is(zero_reg)) {
offset = shifted_branch_offset(L, false);
bgtz(rs, offset);
} else {
slt(scratch, r2, rs);
offset = shifted_branch_offset(L, false);
bne(scratch, zero_reg, offset);
}
break;
case greater_equal:
if (r2.is(zero_reg)) {
offset = shifted_branch_offset(L, false);
bgez(rs, offset);
} else {
slt(scratch, rs, r2);
offset = shifted_branch_offset(L, false);
beq(scratch, zero_reg, offset);
}
break;
case less:
if (r2.is(zero_reg)) {
offset = shifted_branch_offset(L, false);
bltz(rs, offset);
} else {
slt(scratch, rs, r2);
offset = shifted_branch_offset(L, false);
bne(scratch, zero_reg, offset);
}
break;
case less_equal:
if (r2.is(zero_reg)) {
offset = shifted_branch_offset(L, false);
blez(rs, offset);
} else {
slt(scratch, r2, rs);
offset = shifted_branch_offset(L, false);
beq(scratch, zero_reg, offset);
}
break;
// Unsigned comparison.
case Ugreater:
if (r2.is(zero_reg)) {
offset = shifted_branch_offset(L, false);
bne(rs, zero_reg, offset);
} else {
sltu(scratch, r2, rs);
offset = shifted_branch_offset(L, false);
bne(scratch, zero_reg, offset);
}
break;
case Ugreater_equal:
if (r2.is(zero_reg)) {
offset = shifted_branch_offset(L, false);
b(offset);
} else {
sltu(scratch, rs, r2);
offset = shifted_branch_offset(L, false);
beq(scratch, zero_reg, offset);
}
break;
case Uless:
if (r2.is(zero_reg)) {
// No code needs to be emitted.
return;
} else {
sltu(scratch, rs, r2);
offset = shifted_branch_offset(L, false);
bne(scratch, zero_reg, offset);
}
break;
case Uless_equal:
if (r2.is(zero_reg)) {
offset = shifted_branch_offset(L, false);
beq(rs, zero_reg, offset);
} else {
sltu(scratch, r2, rs);
offset = shifted_branch_offset(L, false);
beq(scratch, zero_reg, offset);
}
break;
default:
UNREACHABLE();
} }
} else { } else {
// Be careful to always use shifted_branch_offset only just before the DCHECK(offset == 0);
// branch instruction, as the location will be remember for patching the if (IsMipsArchVariant(kMips32r6) && bdslot == PROTECT) {
// target. return BranchShortHelperR6(0, L, cond, rs, rt);
BlockTrampolinePoolScope block_trampoline_pool(this); } else {
switch (cond) { return BranchShortHelper(0, L, cond, rs, rt, bdslot);
case cc_always:
offset = shifted_branch_offset(L, false);
b(offset);
break;
case eq:
if (rt.imm32_ == 0) {
offset = shifted_branch_offset(L, false);
beq(rs, zero_reg, offset);
} else {
DCHECK(!scratch.is(rs));
r2 = scratch;
li(r2, rt);
offset = shifted_branch_offset(L, false);
beq(rs, r2, offset);
}
break;
case ne:
if (rt.imm32_ == 0) {
offset = shifted_branch_offset(L, false);
bne(rs, zero_reg, offset);
} else {
DCHECK(!scratch.is(rs));
r2 = scratch;
li(r2, rt);
offset = shifted_branch_offset(L, false);
bne(rs, r2, offset);
}
break;
// Signed comparison.
case greater:
if (rt.imm32_ == 0) {
offset = shifted_branch_offset(L, false);
bgtz(rs, offset);
} else {
DCHECK(!scratch.is(rs));
r2 = scratch;
li(r2, rt);
slt(scratch, r2, rs);
offset = shifted_branch_offset(L, false);
bne(scratch, zero_reg, offset);
}
break;
case greater_equal:
if (rt.imm32_ == 0) {
offset = shifted_branch_offset(L, false);
bgez(rs, offset);
} else if (is_int16(rt.imm32_)) {
slti(scratch, rs, rt.imm32_);
offset = shifted_branch_offset(L, false);
beq(scratch, zero_reg, offset);
} else {
DCHECK(!scratch.is(rs));
r2 = scratch;
li(r2, rt);
slt(scratch, rs, r2);
offset = shifted_branch_offset(L, false);
beq(scratch, zero_reg, offset);
}
break;
case less:
if (rt.imm32_ == 0) {
offset = shifted_branch_offset(L, false);
bltz(rs, offset);
} else if (is_int16(rt.imm32_)) {
slti(scratch, rs, rt.imm32_);
offset = shifted_branch_offset(L, false);
bne(scratch, zero_reg, offset);
} else {
DCHECK(!scratch.is(rs));
r2 = scratch;
li(r2, rt);
slt(scratch, rs, r2);
offset = shifted_branch_offset(L, false);
bne(scratch, zero_reg, offset);
}
break;
case less_equal:
if (rt.imm32_ == 0) {
offset = shifted_branch_offset(L, false);
blez(rs, offset);
} else {
DCHECK(!scratch.is(rs));
r2 = scratch;
li(r2, rt);
slt(scratch, r2, rs);
offset = shifted_branch_offset(L, false);
beq(scratch, zero_reg, offset);
}
break;
// Unsigned comparison.
case Ugreater:
if (rt.imm32_ == 0) {
offset = shifted_branch_offset(L, false);
bne(rs, zero_reg, offset);
} else {
DCHECK(!scratch.is(rs));
r2 = scratch;
li(r2, rt);
sltu(scratch, r2, rs);
offset = shifted_branch_offset(L, false);
bne(scratch, zero_reg, offset);
}
break;
case Ugreater_equal:
if (rt.imm32_ == 0) {
offset = shifted_branch_offset(L, false);
b(offset);
} else if (is_int16(rt.imm32_)) {
sltiu(scratch, rs, rt.imm32_);
offset = shifted_branch_offset(L, false);
beq(scratch, zero_reg, offset);
} else {
DCHECK(!scratch.is(rs));
r2 = scratch;
li(r2, rt);
sltu(scratch, rs, r2);
offset = shifted_branch_offset(L, false);
beq(scratch, zero_reg, offset);
}
break;
case Uless:
if (rt.imm32_ == 0) {
// No code needs to be emitted.
return;
} else if (is_int16(rt.imm32_)) {
sltiu(scratch, rs, rt.imm32_);
offset = shifted_branch_offset(L, false);
bne(scratch, zero_reg, offset);
} else {
DCHECK(!scratch.is(rs));
r2 = scratch;
li(r2, rt);
sltu(scratch, rs, r2);
offset = shifted_branch_offset(L, false);
bne(scratch, zero_reg, offset);
}
break;
case Uless_equal:
if (rt.imm32_ == 0) {
offset = shifted_branch_offset(L, false);
beq(rs, zero_reg, offset);
} else {
DCHECK(!scratch.is(rs));
r2 = scratch;
li(r2, rt);
sltu(scratch, r2, rs);
offset = shifted_branch_offset(L, false);
beq(scratch, zero_reg, offset);
}
break;
default:
UNREACHABLE();
} }
} }
// Check that offset could actually hold on an int16_t. return false;
DCHECK(is_int16(offset)); }
// Emit a nop in the branch delay slot if required.
if (bdslot == PROTECT)
nop(); void MacroAssembler::BranchShort(int32_t offset, Condition cond, Register rs,
const Operand& rt, BranchDelaySlot bdslot) {
BranchShortCheck(offset, nullptr, cond, rs, rt, bdslot);
} }
void MacroAssembler::BranchAndLink(int16_t offset, BranchDelaySlot bdslot) { void MacroAssembler::BranchShort(Label* L, Condition cond, Register rs,
const Operand& rt, BranchDelaySlot bdslot) {
BranchShortCheck(0, L, cond, rs, rt, bdslot);
}
void MacroAssembler::BranchAndLink(int32_t offset, BranchDelaySlot bdslot) {
BranchAndLinkShort(offset, bdslot); BranchAndLinkShort(offset, bdslot);
} }
void MacroAssembler::BranchAndLink(int16_t offset, Condition cond, Register rs, void MacroAssembler::BranchAndLink(int32_t offset, Condition cond, Register rs,
const Operand& rt, const Operand& rt, BranchDelaySlot bdslot) {
BranchDelaySlot bdslot) { bool is_near = BranchAndLinkShortCheck(offset, nullptr, cond, rs, rt, bdslot);
BranchAndLinkShort(offset, cond, rs, rt, bdslot); DCHECK(is_near);
USE(is_near);
} }
void MacroAssembler::BranchAndLink(Label* L, BranchDelaySlot bdslot) { void MacroAssembler::BranchAndLink(Label* L, BranchDelaySlot bdslot) {
if (L->is_bound()) { if (L->is_bound()) {
if (is_near(L)) { if (is_near_branch(L)) {
BranchAndLinkShort(L, bdslot); BranchAndLinkShort(L, bdslot);
} else { } else {
Jalr(L, bdslot); Jalr(L, bdslot);
...@@ -2575,9 +2564,7 @@ void MacroAssembler::BranchAndLink(Label* L, Condition cond, Register rs, ...@@ -2575,9 +2564,7 @@ void MacroAssembler::BranchAndLink(Label* L, Condition cond, Register rs,
const Operand& rt, const Operand& rt,
BranchDelaySlot bdslot) { BranchDelaySlot bdslot) {
if (L->is_bound()) { if (L->is_bound()) {
if (is_near(L)) { if (!BranchAndLinkShortCheck(0, L, cond, rs, rt, bdslot)) {
BranchAndLinkShort(L, cond, rs, rt, bdslot);
} else {
Label skip; Label skip;
Condition neg_cond = NegateCondition(cond); Condition neg_cond = NegateCondition(cond);
BranchShort(&skip, neg_cond, rs, rt); BranchShort(&skip, neg_cond, rs, rt);
...@@ -2592,17 +2579,15 @@ void MacroAssembler::BranchAndLink(Label* L, Condition cond, Register rs, ...@@ -2592,17 +2579,15 @@ void MacroAssembler::BranchAndLink(Label* L, Condition cond, Register rs,
Jalr(L, bdslot); Jalr(L, bdslot);
bind(&skip); bind(&skip);
} else { } else {
BranchAndLinkShort(L, cond, rs, rt, bdslot); BranchAndLinkShortCheck(0, L, cond, rs, rt, bdslot);
} }
} }
} }
// We need to use a bgezal or bltzal, but they can't be used directly with the void MacroAssembler::BranchAndLinkShortHelper(int16_t offset, Label* L,
// slt instructions. We could use sub or add instead but we would miss overflow BranchDelaySlot bdslot) {
// cases, so we keep slt and add an intermediate third instruction. offset = GetOffset(offset, L, OffsetSize::kOffset16);
void MacroAssembler::BranchAndLinkShort(int16_t offset,
BranchDelaySlot bdslot) {
bal(offset); bal(offset);
// Emit a nop in the branch delay slot if required. // Emit a nop in the branch delay slot if required.
...@@ -2611,371 +2596,303 @@ void MacroAssembler::BranchAndLinkShort(int16_t offset, ...@@ -2611,371 +2596,303 @@ void MacroAssembler::BranchAndLinkShort(int16_t offset,
} }
void MacroAssembler::BranchAndLinkShort(int16_t offset, Condition cond, void MacroAssembler::BranchAndLinkShortHelperR6(int32_t offset, Label* L) {
Register rs, const Operand& rt, offset = GetOffset(offset, L, OffsetSize::kOffset26);
BranchDelaySlot bdslot) { balc(offset);
BRANCH_ARGS_CHECK(cond, rs, rt); }
Register r2 = no_reg;
Register scratch = at;
if (rt.is_reg()) {
r2 = rt.rm_;
} else if (cond != cc_always) {
r2 = scratch;
li(r2, rt);
}
if (!IsMipsArchVariant(kMips32r6)) { void MacroAssembler::BranchAndLinkShort(int32_t offset,
BlockTrampolinePoolScope block_trampoline_pool(this); BranchDelaySlot bdslot) {
switch (cond) { if (IsMipsArchVariant(kMips32r6) && bdslot == PROTECT) {
case cc_always: DCHECK(is_int26(offset));
bal(offset); BranchAndLinkShortHelperR6(offset, nullptr);
break;
case eq:
bne(rs, r2, 2);
nop();
bal(offset);
break;
case ne:
beq(rs, r2, 2);
nop();
bal(offset);
break;
// Signed comparison.
case greater:
slt(scratch, r2, rs);
addiu(scratch, scratch, -1);
bgezal(scratch, offset);
break;
case greater_equal:
slt(scratch, rs, r2);
addiu(scratch, scratch, -1);
bltzal(scratch, offset);
break;
case less:
slt(scratch, rs, r2);
addiu(scratch, scratch, -1);
bgezal(scratch, offset);
break;
case less_equal:
slt(scratch, r2, rs);
addiu(scratch, scratch, -1);
bltzal(scratch, offset);
break;
// Unsigned comparison.
case Ugreater:
sltu(scratch, r2, rs);
addiu(scratch, scratch, -1);
bgezal(scratch, offset);
break;
case Ugreater_equal:
sltu(scratch, rs, r2);
addiu(scratch, scratch, -1);
bltzal(scratch, offset);
break;
case Uless:
sltu(scratch, rs, r2);
addiu(scratch, scratch, -1);
bgezal(scratch, offset);
break;
case Uless_equal:
sltu(scratch, r2, rs);
addiu(scratch, scratch, -1);
bltzal(scratch, offset);
break;
default:
UNREACHABLE();
}
} else { } else {
BlockTrampolinePoolScope block_trampoline_pool(this); DCHECK(is_int16(offset));
switch (cond) { BranchAndLinkShortHelper(offset, nullptr, bdslot);
case cc_always: }
bal(offset); }
break;
case eq:
bne(rs, r2, 2);
nop();
bal(offset);
break;
case ne:
beq(rs, r2, 2);
nop();
bal(offset);
break;
// Signed comparison.
case greater:
// rs > rt
slt(scratch, r2, rs);
beq(scratch, zero_reg, 2);
nop();
bal(offset);
break;
case greater_equal:
// rs >= rt
slt(scratch, rs, r2);
bne(scratch, zero_reg, 2);
nop();
bal(offset);
break;
case less:
// rs < r2
slt(scratch, rs, r2);
bne(scratch, zero_reg, 2);
nop();
bal(offset);
break;
case less_equal:
// rs <= r2
slt(scratch, r2, rs);
bne(scratch, zero_reg, 2);
nop();
bal(offset);
break;
// Unsigned comparison. void MacroAssembler::BranchAndLinkShort(Label* L, BranchDelaySlot bdslot) {
case Ugreater: if (IsMipsArchVariant(kMips32r6) && bdslot == PROTECT) {
// rs > rt BranchAndLinkShortHelperR6(0, L);
sltu(scratch, r2, rs); } else {
beq(scratch, zero_reg, 2); BranchAndLinkShortHelper(0, L, bdslot);
nop();
bal(offset);
break;
case Ugreater_equal:
// rs >= rt
sltu(scratch, rs, r2);
bne(scratch, zero_reg, 2);
nop();
bal(offset);
break;
case Uless:
// rs < r2
sltu(scratch, rs, r2);
bne(scratch, zero_reg, 2);
nop();
bal(offset);
break;
case Uless_equal:
// rs <= r2
sltu(scratch, r2, rs);
bne(scratch, zero_reg, 2);
nop();
bal(offset);
break;
default:
UNREACHABLE();
}
} }
// Emit a nop in the branch delay slot if required.
if (bdslot == PROTECT)
nop();
} }
void MacroAssembler::BranchAndLinkShort(Label* L, BranchDelaySlot bdslot) { bool MacroAssembler::BranchAndLinkShortHelperR6(int32_t offset, Label* L,
bal(shifted_branch_offset(L, false)); Condition cond, Register rs,
const Operand& rt) {
Register scratch = rs.is(at) ? t8 : at;
OffsetSize bits = OffsetSize::kOffset16;
// Emit a nop in the branch delay slot if required. BlockTrampolinePoolScope block_trampoline_pool(this);
if (bdslot == PROTECT) DCHECK((cond == cc_always && is_int26(offset)) || is_int16(offset));
nop(); switch (cond) {
} case cc_always:
bits = OffsetSize::kOffset26;
if (!is_near(L, bits)) return false;
offset = GetOffset(offset, L, bits);
balc(offset);
break;
case eq:
if (!is_near(L, bits)) return false;
Subu(scratch, rs, rt);
offset = GetOffset(offset, L, bits);
beqzalc(scratch, offset);
break;
case ne:
if (!is_near(L, bits)) return false;
Subu(scratch, rs, rt);
offset = GetOffset(offset, L, bits);
bnezalc(scratch, offset);
break;
// Signed comparison.
case greater:
// rs > rt
if (rs.code() == rt.rm_.reg_code) {
break; // No code needs to be emitted.
} else if (rs.is(zero_reg)) {
if (!is_near(L, bits)) return false;
scratch = GetRtAsRegisterHelper(rt, scratch);
offset = GetOffset(offset, L, bits);
bltzalc(scratch, offset);
} else if (IsZero(rt)) {
if (!is_near(L, bits)) return false;
offset = GetOffset(offset, L, bits);
bgtzalc(rs, offset);
} else {
if (!is_near(L, bits)) return false;
Slt(scratch, GetRtAsRegisterHelper(rt, scratch), rs);
offset = GetOffset(offset, L, bits);
bnezalc(scratch, offset);
}
break;
case greater_equal:
// rs >= rt
if (rs.code() == rt.rm_.reg_code) {
bits = OffsetSize::kOffset26;
if (!is_near(L, bits)) return false;
offset = GetOffset(offset, L, bits);
balc(offset);
} else if (rs.is(zero_reg)) {
if (!is_near(L, bits)) return false;
scratch = GetRtAsRegisterHelper(rt, scratch);
offset = GetOffset(offset, L, bits);
blezalc(scratch, offset);
} else if (IsZero(rt)) {
if (!is_near(L, bits)) return false;
offset = GetOffset(offset, L, bits);
bgezalc(rs, offset);
} else {
if (!is_near(L, bits)) return false;
Slt(scratch, rs, rt);
offset = GetOffset(offset, L, bits);
beqzalc(scratch, offset);
}
break;
case less:
// rs < rt
if (rs.code() == rt.rm_.reg_code) {
break; // No code needs to be emitted.
} else if (rs.is(zero_reg)) {
if (!is_near(L, bits)) return false;
scratch = GetRtAsRegisterHelper(rt, scratch);
offset = GetOffset(offset, L, bits);
bgtzalc(scratch, offset);
} else if (IsZero(rt)) {
if (!is_near(L, bits)) return false;
offset = GetOffset(offset, L, bits);
bltzalc(rs, offset);
} else {
if (!is_near(L, bits)) return false;
Slt(scratch, rs, rt);
offset = GetOffset(offset, L, bits);
bnezalc(scratch, offset);
}
break;
case less_equal:
// rs <= r2
if (rs.code() == rt.rm_.reg_code) {
bits = OffsetSize::kOffset26;
if (!is_near(L, bits)) return false;
offset = GetOffset(offset, L, bits);
balc(offset);
} else if (rs.is(zero_reg)) {
if (!is_near(L, bits)) return false;
scratch = GetRtAsRegisterHelper(rt, scratch);
offset = GetOffset(offset, L, bits);
bgezalc(scratch, offset);
} else if (IsZero(rt)) {
if (!is_near(L, bits)) return false;
offset = GetOffset(offset, L, bits);
blezalc(rs, offset);
} else {
if (!is_near(L, bits)) return false;
Slt(scratch, GetRtAsRegisterHelper(rt, scratch), rs);
offset = GetOffset(offset, L, bits);
beqzalc(scratch, offset);
}
break;
void MacroAssembler::BranchAndLinkShort(Label* L, Condition cond, Register rs,
const Operand& rt,
BranchDelaySlot bdslot) {
BRANCH_ARGS_CHECK(cond, rs, rt);
int32_t offset = 0; // Unsigned comparison.
Register r2 = no_reg; case Ugreater:
Register scratch = at; // rs > r2
if (rt.is_reg()) { if (!is_near(L, bits)) return false;
r2 = rt.rm_; Sltu(scratch, GetRtAsRegisterHelper(rt, scratch), rs);
} else if (cond != cc_always) { offset = GetOffset(offset, L, bits);
r2 = scratch; bnezalc(scratch, offset);
li(r2, rt); break;
case Ugreater_equal:
// rs >= r2
if (!is_near(L, bits)) return false;
Sltu(scratch, rs, rt);
offset = GetOffset(offset, L, bits);
beqzalc(scratch, offset);
break;
case Uless:
// rs < r2
if (!is_near(L, bits)) return false;
Sltu(scratch, rs, rt);
offset = GetOffset(offset, L, bits);
bnezalc(scratch, offset);
break;
case Uless_equal:
// rs <= r2
if (!is_near(L, bits)) return false;
Sltu(scratch, GetRtAsRegisterHelper(rt, scratch), rs);
offset = GetOffset(offset, L, bits);
beqzalc(scratch, offset);
break;
default:
UNREACHABLE();
} }
return true;
}
if (!IsMipsArchVariant(kMips32r6)) {
BlockTrampolinePoolScope block_trampoline_pool(this);
switch (cond) {
case cc_always:
offset = shifted_branch_offset(L, false);
bal(offset);
break;
case eq:
bne(rs, r2, 2);
nop();
offset = shifted_branch_offset(L, false);
bal(offset);
break;
case ne:
beq(rs, r2, 2);
nop();
offset = shifted_branch_offset(L, false);
bal(offset);
break;
// Signed comparison.
case greater:
slt(scratch, r2, rs);
addiu(scratch, scratch, -1);
offset = shifted_branch_offset(L, false);
bgezal(scratch, offset);
break;
case greater_equal:
slt(scratch, rs, r2);
addiu(scratch, scratch, -1);
offset = shifted_branch_offset(L, false);
bltzal(scratch, offset);
break;
case less:
slt(scratch, rs, r2);
addiu(scratch, scratch, -1);
offset = shifted_branch_offset(L, false);
bgezal(scratch, offset);
break;
case less_equal:
slt(scratch, r2, rs);
addiu(scratch, scratch, -1);
offset = shifted_branch_offset(L, false);
bltzal(scratch, offset);
break;
// Unsigned comparison.
case Ugreater:
sltu(scratch, r2, rs);
addiu(scratch, scratch, -1);
offset = shifted_branch_offset(L, false);
bgezal(scratch, offset);
break;
case Ugreater_equal:
sltu(scratch, rs, r2);
addiu(scratch, scratch, -1);
offset = shifted_branch_offset(L, false);
bltzal(scratch, offset);
break;
case Uless:
sltu(scratch, rs, r2);
addiu(scratch, scratch, -1);
offset = shifted_branch_offset(L, false);
bgezal(scratch, offset);
break;
case Uless_equal:
sltu(scratch, r2, rs);
addiu(scratch, scratch, -1);
offset = shifted_branch_offset(L, false);
bltzal(scratch, offset);
break;
default:
UNREACHABLE();
}
} else {
BlockTrampolinePoolScope block_trampoline_pool(this);
switch (cond) {
case cc_always:
offset = shifted_branch_offset(L, false);
bal(offset);
break;
case eq:
bne(rs, r2, 2);
nop();
offset = shifted_branch_offset(L, false);
bal(offset);
break;
case ne:
beq(rs, r2, 2);
nop();
offset = shifted_branch_offset(L, false);
bal(offset);
break;
// Signed comparison.
case greater:
// rs > rt
slt(scratch, r2, rs);
beq(scratch, zero_reg, 2);
nop();
offset = shifted_branch_offset(L, false);
bal(offset);
break;
case greater_equal:
// rs >= rt
slt(scratch, rs, r2);
bne(scratch, zero_reg, 2);
nop();
offset = shifted_branch_offset(L, false);
bal(offset);
break;
case less:
// rs < r2
slt(scratch, rs, r2);
bne(scratch, zero_reg, 2);
nop();
offset = shifted_branch_offset(L, false);
bal(offset);
break;
case less_equal:
// rs <= r2
slt(scratch, r2, rs);
bne(scratch, zero_reg, 2);
nop();
offset = shifted_branch_offset(L, false);
bal(offset);
break;
// Pre r6 we need to use a bgezal or bltzal, but they can't be used directly
// with the slt instructions. We could use sub or add instead but we would miss
// overflow cases, so we keep slt and add an intermediate third instruction.
bool MacroAssembler::BranchAndLinkShortHelper(int16_t offset, Label* L,
Condition cond, Register rs,
const Operand& rt,
BranchDelaySlot bdslot) {
if (!is_near(L, OffsetSize::kOffset16)) return false;
// Unsigned comparison. Register scratch = t8;
case Ugreater: BlockTrampolinePoolScope block_trampoline_pool(this);
// rs > rt
sltu(scratch, r2, rs);
beq(scratch, zero_reg, 2);
nop();
offset = shifted_branch_offset(L, false);
bal(offset);
break;
case Ugreater_equal:
// rs >= rt
sltu(scratch, rs, r2);
bne(scratch, zero_reg, 2);
nop();
offset = shifted_branch_offset(L, false);
bal(offset);
break;
case Uless:
// rs < r2
sltu(scratch, rs, r2);
bne(scratch, zero_reg, 2);
nop();
offset = shifted_branch_offset(L, false);
bal(offset);
break;
case Uless_equal:
// rs <= r2
sltu(scratch, r2, rs);
bne(scratch, zero_reg, 2);
nop();
offset = shifted_branch_offset(L, false);
bal(offset);
break;
default: switch (cond) {
UNREACHABLE(); case cc_always:
} offset = GetOffset(offset, L, OffsetSize::kOffset16);
} bal(offset);
break;
case eq:
bne(rs, GetRtAsRegisterHelper(rt, scratch), 2);
nop();
offset = GetOffset(offset, L, OffsetSize::kOffset16);
bal(offset);
break;
case ne:
beq(rs, GetRtAsRegisterHelper(rt, scratch), 2);
nop();
offset = GetOffset(offset, L, OffsetSize::kOffset16);
bal(offset);
break;
// Signed comparison.
case greater:
Slt(scratch, GetRtAsRegisterHelper(rt, scratch), rs);
addiu(scratch, scratch, -1);
offset = GetOffset(offset, L, OffsetSize::kOffset16);
bgezal(scratch, offset);
break;
case greater_equal:
Slt(scratch, rs, rt);
addiu(scratch, scratch, -1);
offset = GetOffset(offset, L, OffsetSize::kOffset16);
bltzal(scratch, offset);
break;
case less:
Slt(scratch, rs, rt);
addiu(scratch, scratch, -1);
offset = GetOffset(offset, L, OffsetSize::kOffset16);
bgezal(scratch, offset);
break;
case less_equal:
Slt(scratch, GetRtAsRegisterHelper(rt, scratch), rs);
addiu(scratch, scratch, -1);
offset = GetOffset(offset, L, OffsetSize::kOffset16);
bltzal(scratch, offset);
break;
// Unsigned comparison.
case Ugreater:
Sltu(scratch, GetRtAsRegisterHelper(rt, scratch), rs);
addiu(scratch, scratch, -1);
offset = GetOffset(offset, L, OffsetSize::kOffset16);
bgezal(scratch, offset);
break;
case Ugreater_equal:
Sltu(scratch, rs, rt);
addiu(scratch, scratch, -1);
offset = GetOffset(offset, L, OffsetSize::kOffset16);
bltzal(scratch, offset);
break;
case Uless:
Sltu(scratch, rs, rt);
addiu(scratch, scratch, -1);
offset = GetOffset(offset, L, OffsetSize::kOffset16);
bgezal(scratch, offset);
break;
case Uless_equal:
Sltu(scratch, GetRtAsRegisterHelper(rt, scratch), rs);
addiu(scratch, scratch, -1);
offset = GetOffset(offset, L, OffsetSize::kOffset16);
bltzal(scratch, offset);
break;
// Check that offset could actually hold on an int16_t. default:
DCHECK(is_int16(offset)); UNREACHABLE();
}
// Emit a nop in the branch delay slot if required. // Emit a nop in the branch delay slot if required.
if (bdslot == PROTECT) if (bdslot == PROTECT)
nop(); nop();
return true;
}
bool MacroAssembler::BranchAndLinkShortCheck(int32_t offset, Label* L,
Condition cond, Register rs,
const Operand& rt,
BranchDelaySlot bdslot) {
BRANCH_ARGS_CHECK(cond, rs, rt);
if (!L) {
if (IsMipsArchVariant(kMips32r6) && bdslot == PROTECT) {
DCHECK(is_int26(offset));
return BranchAndLinkShortHelperR6(offset, nullptr, cond, rs, rt);
} else {
DCHECK(is_int16(offset));
return BranchAndLinkShortHelper(offset, nullptr, cond, rs, rt, bdslot);
}
} else {
DCHECK(offset == 0);
if (IsMipsArchVariant(kMips32r6) && bdslot == PROTECT) {
return BranchAndLinkShortHelperR6(0, L, cond, rs, rt);
} else {
return BranchAndLinkShortHelper(0, L, cond, rs, rt, bdslot);
}
}
return false;
} }
...@@ -3065,6 +2982,10 @@ void MacroAssembler::Call(Register target, ...@@ -3065,6 +2982,10 @@ void MacroAssembler::Call(Register target,
Register rs, Register rs,
const Operand& rt, const Operand& rt,
BranchDelaySlot bd) { BranchDelaySlot bd) {
#ifdef DEBUG
int size = IsPrevInstrCompactBranch() ? kInstrSize : 0;
#endif
BlockTrampolinePoolScope block_trampoline_pool(this); BlockTrampolinePoolScope block_trampoline_pool(this);
Label start; Label start;
bind(&start); bind(&start);
...@@ -3079,8 +3000,10 @@ void MacroAssembler::Call(Register target, ...@@ -3079,8 +3000,10 @@ void MacroAssembler::Call(Register target,
if (bd == PROTECT) if (bd == PROTECT)
nop(); nop();
DCHECK_EQ(CallSize(target, cond, rs, rt, bd), #ifdef DEBUG
SizeOfCodeGeneratedSince(&start)); CHECK_EQ(size + CallSize(target, cond, rs, rt, bd),
SizeOfCodeGeneratedSince(&start));
#endif
} }
...@@ -5887,25 +5810,10 @@ void CodePatcher::Emit(Address addr) { ...@@ -5887,25 +5810,10 @@ void CodePatcher::Emit(Address addr) {
} }
void CodePatcher::ChangeBranchCondition(Condition cond) { void CodePatcher::ChangeBranchCondition(Instr current_instr,
Instr instr = Assembler::instr_at(masm_.pc_); uint32_t new_opcode) {
DCHECK(Assembler::IsBranch(instr)); current_instr = (current_instr & ~kOpcodeMask) | new_opcode;
uint32_t opcode = Assembler::GetOpcodeField(instr); masm_.emit(current_instr);
// Currently only the 'eq' and 'ne' cond values are supported and the simple
// branch instructions (with opcode being the branch type).
// There are some special cases (see Assembler::IsBranch()) so extending this
// would be tricky.
DCHECK(opcode == BEQ ||
opcode == BNE ||
opcode == BLEZ ||
opcode == BGTZ ||
opcode == BEQL ||
opcode == BNEL ||
opcode == BLEZL ||
opcode == BGTZL);
opcode = (cond == eq) ? BEQ : BNE;
instr = (instr & ~kOpcodeMask) | opcode;
masm_.emit(instr);
} }
......
...@@ -164,9 +164,9 @@ class MacroAssembler: public Assembler { ...@@ -164,9 +164,9 @@ class MacroAssembler: public Assembler {
Name(target, COND_ARGS, bd); \ Name(target, COND_ARGS, bd); \
} }
#define DECLARE_BRANCH_PROTOTYPES(Name) \ #define DECLARE_BRANCH_PROTOTYPES(Name) \
DECLARE_NORELOC_PROTOTYPE(Name, Label*) \ DECLARE_NORELOC_PROTOTYPE(Name, Label*) \
DECLARE_NORELOC_PROTOTYPE(Name, int16_t) DECLARE_NORELOC_PROTOTYPE(Name, int32_t)
DECLARE_BRANCH_PROTOTYPES(Branch) DECLARE_BRANCH_PROTOTYPES(Branch)
DECLARE_BRANCH_PROTOTYPES(BranchAndLink) DECLARE_BRANCH_PROTOTYPES(BranchAndLink)
...@@ -203,6 +203,8 @@ class MacroAssembler: public Assembler { ...@@ -203,6 +203,8 @@ class MacroAssembler: public Assembler {
Ret(cond, rs, rt, bd); Ret(cond, rs, rt, bd);
} }
bool IsNear(Label* L, Condition cond, int rs_reg);
void Branch(Label* L, void Branch(Label* L,
Condition cond, Condition cond,
Register rs, Register rs,
...@@ -1632,14 +1634,30 @@ const Operand& rt = Operand(zero_reg), BranchDelaySlot bd = PROTECT ...@@ -1632,14 +1634,30 @@ const Operand& rt = Operand(zero_reg), BranchDelaySlot bd = PROTECT
int num_reg_arguments, int num_reg_arguments,
int num_double_arguments); int num_double_arguments);
void BranchAndLinkShort(int16_t offset, BranchDelaySlot bdslot = PROTECT); inline Register GetRtAsRegisterHelper(const Operand& rt, Register scratch);
void BranchAndLinkShort(int16_t offset, Condition cond, Register rs, inline int32_t GetOffset(int32_t offset, Label* L, OffsetSize bits);
const Operand& rt, void BranchShortHelperR6(int32_t offset, Label* L);
BranchDelaySlot bdslot = PROTECT); void BranchShortHelper(int16_t offset, Label* L, BranchDelaySlot bdslot);
bool BranchShortHelperR6(int32_t offset, Label* L, Condition cond,
Register rs, const Operand& rt);
bool BranchShortHelper(int16_t offset, Label* L, Condition cond, Register rs,
const Operand& rt, BranchDelaySlot bdslot);
bool BranchShortCheck(int32_t offset, Label* L, Condition cond, Register rs,
const Operand& rt, BranchDelaySlot bdslot);
void BranchAndLinkShortHelperR6(int32_t offset, Label* L);
void BranchAndLinkShortHelper(int16_t offset, Label* L,
BranchDelaySlot bdslot);
void BranchAndLinkShort(int32_t offset, BranchDelaySlot bdslot = PROTECT);
void BranchAndLinkShort(Label* L, BranchDelaySlot bdslot = PROTECT); void BranchAndLinkShort(Label* L, BranchDelaySlot bdslot = PROTECT);
void BranchAndLinkShort(Label* L, Condition cond, Register rs, bool BranchAndLinkShortHelperR6(int32_t offset, Label* L, Condition cond,
const Operand& rt, Register rs, const Operand& rt);
BranchDelaySlot bdslot = PROTECT); bool BranchAndLinkShortHelper(int16_t offset, Label* L, Condition cond,
Register rs, const Operand& rt,
BranchDelaySlot bdslot);
bool BranchAndLinkShortCheck(int32_t offset, Label* L, Condition cond,
Register rs, const Operand& rt,
BranchDelaySlot bdslot);
void Jr(Label* L, BranchDelaySlot bdslot); void Jr(Label* L, BranchDelaySlot bdslot);
void Jalr(Label* L, BranchDelaySlot bdslot); void Jalr(Label* L, BranchDelaySlot bdslot);
...@@ -1726,7 +1744,7 @@ class CodePatcher { ...@@ -1726,7 +1744,7 @@ class CodePatcher {
// Change the condition part of an instruction leaving the rest of the current // Change the condition part of an instruction leaving the rest of the current
// instruction unchanged. // instruction unchanged.
void ChangeBranchCondition(Condition cond); void ChangeBranchCondition(Instr current_instr, uint32_t new_opcode);
private: private:
byte* address_; // The address of the code being patched. byte* address_; // The address of the code being patched.
......
...@@ -129,7 +129,7 @@ void MipsDebugger::Stop(Instruction* instr) { ...@@ -129,7 +129,7 @@ void MipsDebugger::Stop(Instruction* instr) {
#else // GENERATED_CODE_COVERAGE #else // GENERATED_CODE_COVERAGE
#define UNSUPPORTED() printf("Unsupported instruction.\n"); #define UNSUPPORTED() printf("Sim: Unsupported instruction.\n");
static void InitializeCoverage() {} static void InitializeCoverage() {}
...@@ -3736,26 +3736,7 @@ void Simulator::DecodeTypeRegister(Instruction* instr) { ...@@ -3736,26 +3736,7 @@ void Simulator::DecodeTypeRegister(Instruction* instr) {
} }
// Branch instructions common part. // Type 2: instructions using a 16, 21 or 26 bits immediate. (e.g. beq, beqc).
#define BranchAndLinkHelper(do_branch) \
execute_branch_delay_instruction = true; \
if (do_branch) { \
next_pc = current_pc + (imm16 << 2) + Instruction::kInstrSize; \
set_register(31, current_pc + 2 * Instruction::kInstrSize); \
} else { \
next_pc = current_pc + 2 * Instruction::kInstrSize; \
}
#define BranchHelper(do_branch) \
execute_branch_delay_instruction = true; \
if (do_branch) { \
next_pc = current_pc + (imm16 << 2) + Instruction::kInstrSize; \
} else { \
next_pc = current_pc + 2 * Instruction::kInstrSize; \
}
// Type 2: instructions using a 16 bytes immediate. (e.g. addi, beq).
void Simulator::DecodeTypeImmediate(Instruction* instr) { void Simulator::DecodeTypeImmediate(Instruction* instr) {
// Instruction fields. // Instruction fields.
Opcode op = instr->OpcodeFieldRaw(); Opcode op = instr->OpcodeFieldRaw();
...@@ -3765,20 +3746,14 @@ void Simulator::DecodeTypeImmediate(Instruction* instr) { ...@@ -3765,20 +3746,14 @@ void Simulator::DecodeTypeImmediate(Instruction* instr) {
int32_t rt_reg = instr->RtValue(); // Destination register. int32_t rt_reg = instr->RtValue(); // Destination register.
int32_t rt = get_register(rt_reg); int32_t rt = get_register(rt_reg);
int16_t imm16 = instr->Imm16Value(); int16_t imm16 = instr->Imm16Value();
int32_t imm21 = instr->Imm21Value();
int32_t imm26 = instr->Imm26Value();
int32_t ft_reg = instr->FtValue(); // Destination register. int32_t ft_reg = instr->FtValue(); // Destination register.
int64_t ft;
// Zero extended immediate. // Zero extended immediate.
uint32_t oe_imm16 = 0xffff & imm16; uint32_t oe_imm16 = 0xffff & imm16;
// Sign extended immediate. // Sign extended immediate.
int32_t se_imm16 = imm16; int32_t se_imm16 = imm16;
int32_t se_imm26 = imm26 | ((imm26 & 0x2000000) ? 0xfc000000 : 0);
// Get current pc.
int32_t current_pc = get_pc();
// Next pc. // Next pc.
int32_t next_pc = bad_ra; int32_t next_pc = bad_ra;
...@@ -3791,7 +3766,58 @@ void Simulator::DecodeTypeImmediate(Instruction* instr) { ...@@ -3791,7 +3766,58 @@ void Simulator::DecodeTypeImmediate(Instruction* instr) {
// Used for memory instructions. // Used for memory instructions.
int32_t addr = 0x0; int32_t addr = 0x0;
// ---------- Configuration (and execution for REGIMM). // Branch instructions common part.
auto BranchAndLinkHelper = [this, instr, &next_pc,
&execute_branch_delay_instruction](
bool do_branch) {
execute_branch_delay_instruction = true;
int32_t current_pc = get_pc();
if (do_branch) {
int16_t imm16 = instr->Imm16Value();
next_pc = current_pc + (imm16 << 2) + Instruction::kInstrSize;
set_register(31, current_pc + 2 * Instruction::kInstrSize);
} else {
next_pc = current_pc + 2 * Instruction::kInstrSize;
}
};
auto BranchHelper = [this, instr, &next_pc,
&execute_branch_delay_instruction](bool do_branch) {
execute_branch_delay_instruction = true;
int32_t current_pc = get_pc();
if (do_branch) {
int16_t imm16 = instr->Imm16Value();
next_pc = current_pc + (imm16 << 2) + Instruction::kInstrSize;
} else {
next_pc = current_pc + 2 * Instruction::kInstrSize;
}
};
auto BranchAndLinkCompactHelper = [this, instr, &next_pc](bool do_branch,
int bits) {
int32_t current_pc = get_pc();
CheckForbiddenSlot(current_pc);
if (do_branch) {
int32_t imm = instr->ImmValue(bits);
imm <<= 32 - bits;
imm >>= 32 - bits;
next_pc = current_pc + (imm << 2) + Instruction::kInstrSize;
set_register(31, current_pc + Instruction::kInstrSize);
}
};
auto BranchCompactHelper = [&next_pc, this, instr](bool do_branch, int bits) {
int32_t current_pc = get_pc();
CheckForbiddenSlot(current_pc);
if (do_branch) {
int32_t imm = instr->ImmValue(bits);
imm <<= 32 - bits;
imm >>= 32 - bits;
next_pc = get_pc() + (imm << 2) + Instruction::kInstrSize;
}
};
switch (op) { switch (op) {
// ------------- COP1. Coprocessor instructions. // ------------- COP1. Coprocessor instructions.
case COP1: case COP1:
...@@ -3802,34 +3828,14 @@ void Simulator::DecodeTypeImmediate(Instruction* instr) { ...@@ -3802,34 +3828,14 @@ void Simulator::DecodeTypeImmediate(Instruction* instr) {
uint32_t fcsr_cc = get_fcsr_condition_bit(cc); uint32_t fcsr_cc = get_fcsr_condition_bit(cc);
uint32_t cc_value = test_fcsr_bit(fcsr_cc); uint32_t cc_value = test_fcsr_bit(fcsr_cc);
bool do_branch = (instr->FBtrueValue()) ? cc_value : !cc_value; bool do_branch = (instr->FBtrueValue()) ? cc_value : !cc_value;
execute_branch_delay_instruction = true; BranchHelper(do_branch);
// Set next_pc.
if (do_branch) {
next_pc = current_pc + (imm16 << 2) + Instruction::kInstrSize;
} else {
next_pc = current_pc + kBranchReturnOffset;
}
break; break;
} }
case BC1EQZ: case BC1EQZ:
ft = get_fpu_register(ft_reg); BranchHelper(!(get_fpu_register(ft_reg) & 0x1));
execute_branch_delay_instruction = true;
// Set next_pc.
if (!(ft & 0x1)) {
next_pc = current_pc + (imm16 << 2) + Instruction::kInstrSize;
} else {
next_pc = current_pc + kBranchReturnOffset;
}
break; break;
case BC1NEZ: case BC1NEZ:
ft = get_fpu_register(ft_reg); BranchHelper(get_fpu_register(ft_reg) & 0x1);
execute_branch_delay_instruction = true;
// Set next_pc.
if (ft & 0x1) {
next_pc = current_pc + (imm16 << 2) + Instruction::kInstrSize;
} else {
next_pc = current_pc + kBranchReturnOffset;
}
break; break;
default: default:
UNREACHABLE(); UNREACHABLE();
...@@ -3863,54 +3869,158 @@ void Simulator::DecodeTypeImmediate(Instruction* instr) { ...@@ -3863,54 +3869,158 @@ void Simulator::DecodeTypeImmediate(Instruction* instr) {
case BNE: case BNE:
BranchHelper(rs != rt); BranchHelper(rs != rt);
break; break;
case BLEZ: case POP06: // BLEZALC, BGEZALC, BGEUC, BLEZ (pre-r6)
BranchHelper(rs <= 0); if (IsMipsArchVariant(kMips32r6)) {
break; if (rt_reg != 0) {
case BGTZ: if (rs_reg == 0) { // BLEZALC
BranchHelper(rs > 0); BranchAndLinkCompactHelper(rt <= 0, 16);
break; } else {
case POP66: { if (rs_reg == rt_reg) { // BGEZALC
if (rs_reg) { // BEQZC BranchAndLinkCompactHelper(rt >= 0, 16);
int32_t se_imm21 = } else { // BGEUC
static_cast<int32_t>(imm21 << (kOpcodeBits + kRsBits)); BranchCompactHelper(
se_imm21 = se_imm21 >> (kOpcodeBits + kRsBits); static_cast<uint32_t>(rs) >= static_cast<uint32_t>(rt), 16);
if (rs == 0) }
next_pc = current_pc + 4 + (se_imm21 << 2); }
else } else { // BLEZ
next_pc = current_pc + 4; BranchHelper(rs <= 0);
}
} else { // BLEZ
BranchHelper(rs <= 0);
}
break;
case POP07: // BGTZALC, BLTZALC, BLTUC, BGTZ (pre-r6)
if (IsMipsArchVariant(kMips32r6)) {
if (rt_reg != 0) {
if (rs_reg == 0) { // BGTZALC
BranchAndLinkCompactHelper(rt > 0, 16);
} else {
if (rt_reg == rs_reg) { // BLTZALC
BranchAndLinkCompactHelper(rt < 0, 16);
} else { // BLTUC
BranchCompactHelper(
static_cast<uint32_t>(rs) < static_cast<uint32_t>(rt), 16);
}
}
} else { // BGTZ
BranchHelper(rs > 0);
}
} else { // BGTZ
BranchHelper(rs > 0);
}
break;
case POP26: // BLEZC, BGEZC, BGEC/BLEC / BLEZL (pre-r6)
if (IsMipsArchVariant(kMips32r6)) {
if (rt_reg != 0) {
if (rs_reg == 0) { // BLEZC
BranchCompactHelper(rt <= 0, 16);
} else {
if (rs_reg == rt_reg) { // BGEZC
BranchCompactHelper(rt >= 0, 16);
} else { // BGEC/BLEC
BranchCompactHelper(rs >= rt, 16);
}
}
}
} else { // BLEZL
BranchAndLinkHelper(rs <= 0);
}
break;
case POP27: // BGTZC, BLTZC, BLTC/BGTC / BGTZL (pre-r6)
if (IsMipsArchVariant(kMips32r6)) {
if (rt_reg != 0) {
if (rs_reg == 0) { // BGTZC
BranchCompactHelper(rt > 0, 16);
} else {
if (rs_reg == rt_reg) { // BLTZC
BranchCompactHelper(rt < 0, 16);
} else { // BLTC/BGTC
BranchCompactHelper(rs < rt, 16);
}
}
}
} else { // BGTZL
BranchAndLinkHelper(rs > 0);
}
break;
case POP66: // BEQZC, JIC
if (rs_reg != 0) { // BEQZC
BranchCompactHelper(rs == 0, 21);
} else { // JIC } else { // JIC
CheckForbiddenSlot(get_pc());
next_pc = rt + imm16; next_pc = rt + imm16;
} }
break; break;
} case POP76: // BNEZC, JIALC
case BC: { if (rs_reg != 0) { // BNEZC
next_pc = current_pc + 4 + (se_imm26 << 2); BranchCompactHelper(rs != 0, 21);
set_pc(next_pc); } else { // JIALC
pc_modified_ = true; int32_t current_pc = get_pc();
CheckForbiddenSlot(current_pc);
set_register(31, current_pc + Instruction::kInstrSize);
next_pc = rt + imm16;
}
break; break;
} case BC:
case BALC: { BranchCompactHelper(true, 26);
set_register(31, current_pc + 4);
next_pc = current_pc + 4 + (se_imm26 << 2);
set_pc(next_pc);
pc_modified_ = true;
break; break;
} case BALC:
// ------------- Arithmetic instructions. BranchAndLinkCompactHelper(true, 26);
case ADDI: break;
if (HaveSameSign(rs, se_imm16)) { case POP10: // BOVC, BEQZALC, BEQC / ADDI (pre-r6)
if (rs > 0) { if (IsMipsArchVariant(kMips32r6)) {
if (rs <= (Registers::kMaxValue - se_imm16)) { if (rs_reg >= rt_reg) { // BOVC
SignalException(kIntegerOverflow); if (HaveSameSign(rs, rt)) {
if (rs > 0) {
BranchCompactHelper(rs > Registers::kMaxValue - rt, 16);
} else if (rs < 0) {
BranchCompactHelper(rs < Registers::kMinValue - rt, 16);
}
} }
} else if (rs < 0) { } else {
if (rs >= (Registers::kMinValue - se_imm16)) { if (rs_reg == 0) { // BEQZALC
SignalException(kIntegerUnderflow); BranchAndLinkCompactHelper(rt == 0, 16);
} else { // BEQC
BranchCompactHelper(rt == rs, 16);
} }
} }
} else { // ADDI
if (HaveSameSign(rs, se_imm16)) {
if (rs > 0) {
if (rs <= Registers::kMaxValue - se_imm16) {
SignalException(kIntegerOverflow);
}
} else if (rs < 0) {
if (rs >= Registers::kMinValue - se_imm16) {
SignalException(kIntegerUnderflow);
}
}
}
SetResult(rt_reg, rs + se_imm16);
} }
SetResult(rt_reg, rs + se_imm16);
break; break;
case POP30: // BNVC, BNEZALC, BNEC / DADDI (pre-r6)
if (IsMipsArchVariant(kMips32r6)) {
if (rs_reg >= rt_reg) { // BNVC
if (!HaveSameSign(rs, rt) || rs == 0 || rt == 0) {
BranchCompactHelper(true, 16);
} else {
if (rs > 0) {
BranchCompactHelper(rs <= Registers::kMaxValue - rt, 16);
} else if (rs < 0) {
BranchCompactHelper(rs >= Registers::kMinValue - rt, 16);
}
}
} else {
if (rs_reg == 0) { // BNEZALC
BranchAndLinkCompactHelper(rt != 0, 16);
} else { // BNEC
BranchCompactHelper(rt != rs, 16);
}
}
}
break;
// ------------- Arithmetic instructions.
case ADDIU: case ADDIU:
SetResult(rt_reg, rs + se_imm16); SetResult(rt_reg, rs + se_imm16);
break; break;
...@@ -4014,22 +4124,11 @@ void Simulator::DecodeTypeImmediate(Instruction* instr) { ...@@ -4014,22 +4124,11 @@ void Simulator::DecodeTypeImmediate(Instruction* instr) {
case SDC1: case SDC1:
WriteD(rs + se_imm16, get_fpu_register_double(ft_reg), instr); WriteD(rs + se_imm16, get_fpu_register_double(ft_reg), instr);
break; break;
// ------------- JIALC and BNEZC instructions.
case POP76: {
// Next pc.
next_pc = rt + se_imm16;
// The instruction after the jump is NOT executed.
int16_t pc_increment = Instruction::kInstrSize;
if (instr->IsLinkingInstruction()) {
set_register(31, current_pc + pc_increment);
}
set_pc(next_pc);
pc_modified_ = true;
break;
}
// ------------- PC-Relative instructions. // ------------- PC-Relative instructions.
case PCREL: { case PCREL: {
// rt field: checking 5-bits. // rt field: checking 5-bits.
int32_t imm21 = instr->Imm21Value();
int32_t current_pc = get_pc();
uint8_t rt = (imm21 >> kImm16Bits); uint8_t rt = (imm21 >> kImm16Bits);
switch (rt) { switch (rt) {
case ALUIPC: case ALUIPC:
...@@ -4076,7 +4175,7 @@ void Simulator::DecodeTypeImmediate(Instruction* instr) { ...@@ -4076,7 +4175,7 @@ void Simulator::DecodeTypeImmediate(Instruction* instr) {
// We don't check for end_sim_pc. First it should not be met as the current // We don't check for end_sim_pc. First it should not be met as the current
// pc is valid. Secondly a jump should always execute its branch delay slot. // pc is valid. Secondly a jump should always execute its branch delay slot.
Instruction* branch_delay_instr = Instruction* branch_delay_instr =
reinterpret_cast<Instruction*>(current_pc+Instruction::kInstrSize); reinterpret_cast<Instruction*>(get_pc() + Instruction::kInstrSize);
BranchDelayInstructionDecode(branch_delay_instr); BranchDelayInstructionDecode(branch_delay_instr);
} }
...@@ -4086,9 +4185,6 @@ void Simulator::DecodeTypeImmediate(Instruction* instr) { ...@@ -4086,9 +4185,6 @@ void Simulator::DecodeTypeImmediate(Instruction* instr) {
} }
} }
#undef BranchHelper
#undef BranchAndLinkHelper
// Type 3: instructions using a 26 bytes immediate. (e.g. j, jal). // Type 3: instructions using a 26 bytes immediate. (e.g. j, jal).
void Simulator::DecodeTypeJump(Instruction* instr) { void Simulator::DecodeTypeJump(Instruction* instr) {
...@@ -4174,7 +4270,7 @@ void Simulator::Execute() { ...@@ -4174,7 +4270,7 @@ void Simulator::Execute() {
while (program_counter != end_sim_pc) { while (program_counter != end_sim_pc) {
Instruction* instr = reinterpret_cast<Instruction*>(program_counter); Instruction* instr = reinterpret_cast<Instruction*>(program_counter);
icount_++; icount_++;
if (icount_ == ::v8::internal::FLAG_stop_sim_at) { if (icount_ == static_cast<uint64_t>(::v8::internal::FLAG_stop_sim_at)) {
MipsDebugger dbg(this); MipsDebugger dbg(this);
dbg.Debug(); dbg.Debug();
} else { } else {
......
...@@ -315,6 +315,7 @@ class Simulator { ...@@ -315,6 +315,7 @@ class Simulator {
void DecodeTypeRegisterLRsType(); void DecodeTypeRegisterLRsType();
Instruction* currentInstr_; Instruction* currentInstr_;
inline Instruction* get_instr() const { return currentInstr_; } inline Instruction* get_instr() const { return currentInstr_; }
inline void set_instr(Instruction* instr) { currentInstr_ = instr; } inline void set_instr(Instruction* instr) { currentInstr_ = instr; }
...@@ -346,6 +347,18 @@ class Simulator { ...@@ -346,6 +347,18 @@ class Simulator {
// Used for breakpoints and traps. // Used for breakpoints and traps.
void SoftwareInterrupt(Instruction* instr); void SoftwareInterrupt(Instruction* instr);
// Compact branch guard.
void CheckForbiddenSlot(int32_t current_pc) {
Instruction* instr_aftter_compact_branch =
reinterpret_cast<Instruction*>(current_pc + Instruction::kInstrSize);
if (instr_aftter_compact_branch->IsForbiddenInBranchDelay()) {
V8_Fatal(__FILE__, __LINE__,
"Error: Unexpected instruction 0x%08x immediately after a "
"compact branch instruction.",
*reinterpret_cast<uint32_t*>(instr_aftter_compact_branch));
}
}
// Stop helper functions. // Stop helper functions.
bool IsWatchpoint(uint32_t code); bool IsWatchpoint(uint32_t code);
void PrintWatchpoint(uint32_t code); void PrintWatchpoint(uint32_t code);
......
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