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