diff options
Diffstat (limited to 'compiler/utils')
| -rw-r--r-- | compiler/utils/mips/assembler_mips.cc | 309 | ||||
| -rw-r--r-- | compiler/utils/mips/assembler_mips.h | 115 | ||||
| -rw-r--r-- | compiler/utils/mips/assembler_mips32r6_test.cc | 644 | ||||
| -rw-r--r-- | compiler/utils/mips/assembler_mips_test.cc | 653 | ||||
| -rw-r--r-- | compiler/utils/mips64/assembler_mips64.cc | 370 | ||||
| -rw-r--r-- | compiler/utils/mips64/assembler_mips64.h | 108 | ||||
| -rw-r--r-- | compiler/utils/mips64/assembler_mips64_test.cc | 914 |
7 files changed, 2038 insertions, 1075 deletions
diff --git a/compiler/utils/mips/assembler_mips.cc b/compiler/utils/mips/assembler_mips.cc index 2cbabcfb32..18099d8e13 100644 --- a/compiler/utils/mips/assembler_mips.cc +++ b/compiler/utils/mips/assembler_mips.cc @@ -935,11 +935,11 @@ void MipsAssembler::Bne(Register rs, Register rt, uint16_t imm16) { } void MipsAssembler::Beqz(Register rt, uint16_t imm16) { - Beq(ZERO, rt, imm16); + Beq(rt, ZERO, imm16); } void MipsAssembler::Bnez(Register rt, uint16_t imm16) { - Bne(ZERO, rt, imm16); + Bne(rt, ZERO, imm16); } void MipsAssembler::Bltz(Register rt, uint16_t imm16) { @@ -3118,7 +3118,7 @@ void MipsAssembler::Branch::InitShortOrLong(MipsAssembler::Branch::OffsetBits of } void MipsAssembler::Branch::InitializeType(Type initial_type, bool is_r6) { - OffsetBits offset_size = GetOffsetSizeNeeded(location_, target_); + OffsetBits offset_size_needed = GetOffsetSizeNeeded(location_, target_); if (is_r6) { // R6 switch (initial_type) { @@ -3131,23 +3131,31 @@ void MipsAssembler::Branch::InitializeType(Type initial_type, bool is_r6) { type_ = kR6Literal; break; case kCall: - InitShortOrLong(offset_size, kR6Call, kR6LongCall); + InitShortOrLong(offset_size_needed, kR6Call, kR6LongCall); break; case kCondBranch: switch (condition_) { case kUncond: - InitShortOrLong(offset_size, kR6UncondBranch, kR6LongUncondBranch); + InitShortOrLong(offset_size_needed, kR6UncondBranch, kR6LongUncondBranch); break; case kCondEQZ: case kCondNEZ: // Special case for beqzc/bnezc with longer offset than in other b<cond>c instructions. - type_ = (offset_size <= kOffset23) ? kR6CondBranch : kR6LongCondBranch; + type_ = (offset_size_needed <= kOffset23) ? kR6CondBranch : kR6LongCondBranch; break; default: - InitShortOrLong(offset_size, kR6CondBranch, kR6LongCondBranch); + InitShortOrLong(offset_size_needed, kR6CondBranch, kR6LongCondBranch); break; } break; + case kBareCall: + type_ = kR6BareCall; + CHECK_LE(offset_size_needed, GetOffsetSize()); + break; + case kBareCondBranch: + type_ = (condition_ == kUncond) ? kR6BareUncondBranch : kR6BareCondBranch; + CHECK_LE(offset_size_needed, GetOffsetSize()); + break; default: LOG(FATAL) << "Unexpected branch type " << initial_type; UNREACHABLE(); @@ -3164,18 +3172,26 @@ void MipsAssembler::Branch::InitializeType(Type initial_type, bool is_r6) { type_ = kLiteral; break; case kCall: - InitShortOrLong(offset_size, kCall, kLongCall); + InitShortOrLong(offset_size_needed, kCall, kLongCall); break; case kCondBranch: switch (condition_) { case kUncond: - InitShortOrLong(offset_size, kUncondBranch, kLongUncondBranch); + InitShortOrLong(offset_size_needed, kUncondBranch, kLongUncondBranch); break; default: - InitShortOrLong(offset_size, kCondBranch, kLongCondBranch); + InitShortOrLong(offset_size_needed, kCondBranch, kLongCondBranch); break; } break; + case kBareCall: + type_ = kBareCall; + CHECK_LE(offset_size_needed, GetOffsetSize()); + break; + case kBareCondBranch: + type_ = (condition_ == kUncond) ? kBareUncondBranch : kBareCondBranch; + CHECK_LE(offset_size_needed, GetOffsetSize()); + break; default: LOG(FATAL) << "Unexpected branch type " << initial_type; UNREACHABLE(); @@ -3210,7 +3226,11 @@ bool MipsAssembler::Branch::IsUncond(BranchCondition condition, Register lhs, Re } } -MipsAssembler::Branch::Branch(bool is_r6, uint32_t location, uint32_t target, bool is_call) +MipsAssembler::Branch::Branch(bool is_r6, + uint32_t location, + uint32_t target, + bool is_call, + bool is_bare) : old_location_(location), location_(location), target_(target), @@ -3218,7 +3238,9 @@ MipsAssembler::Branch::Branch(bool is_r6, uint32_t location, uint32_t target, bo rhs_reg_(0), condition_(kUncond), delayed_instruction_(kUnfilledDelaySlot) { - InitializeType((is_call ? kCall : kCondBranch), is_r6); + InitializeType( + (is_call ? (is_bare ? kBareCall : kCall) : (is_bare ? kBareCondBranch : kCondBranch)), + is_r6); } MipsAssembler::Branch::Branch(bool is_r6, @@ -3226,7 +3248,8 @@ MipsAssembler::Branch::Branch(bool is_r6, uint32_t target, MipsAssembler::BranchCondition condition, Register lhs_reg, - Register rhs_reg) + Register rhs_reg, + bool is_bare) : old_location_(location), location_(location), target_(target), @@ -3276,7 +3299,7 @@ MipsAssembler::Branch::Branch(bool is_r6, // Branch condition is always true, make the branch unconditional. condition_ = kUncond; } - InitializeType(kCondBranch, is_r6); + InitializeType((is_bare ? kBareCondBranch : kCondBranch), is_r6); } MipsAssembler::Branch::Branch(bool is_r6, @@ -3419,20 +3442,44 @@ uint32_t MipsAssembler::Branch::GetOldEndLocation() const { return GetOldLocation() + GetOldSize(); } +bool MipsAssembler::Branch::IsBare() const { + switch (type_) { + // R2 short branches (can't be promoted to long), delay slots filled manually. + case kBareUncondBranch: + case kBareCondBranch: + case kBareCall: + // R6 short branches (can't be promoted to long), forbidden/delay slots filled manually. + case kR6BareUncondBranch: + case kR6BareCondBranch: + case kR6BareCall: + return true; + default: + return false; + } +} + bool MipsAssembler::Branch::IsLong() const { switch (type_) { - // R2 short branches. + // R2 short branches (can be promoted to long). case kUncondBranch: case kCondBranch: case kCall: + // R2 short branches (can't be promoted to long), delay slots filled manually. + case kBareUncondBranch: + case kBareCondBranch: + case kBareCall: // R2 near label. case kLabel: // R2 near literal. case kLiteral: - // R6 short branches. + // R6 short branches (can be promoted to long). case kR6UncondBranch: case kR6CondBranch: case kR6Call: + // R6 short branches (can't be promoted to long), forbidden/delay slots filled manually. + case kR6BareUncondBranch: + case kR6BareCondBranch: + case kR6BareCall: // R6 near label. case kR6Label: // R6 near literal. @@ -3464,8 +3511,9 @@ bool MipsAssembler::Branch::IsResolved() const { } MipsAssembler::Branch::OffsetBits MipsAssembler::Branch::GetOffsetSize() const { + bool r6_cond_branch = (type_ == kR6CondBranch || type_ == kR6BareCondBranch); OffsetBits offset_size = - (type_ == kR6CondBranch && (condition_ == kCondEQZ || condition_ == kCondNEZ)) + (r6_cond_branch && (condition_ == kCondEQZ || condition_ == kCondNEZ)) ? kOffset23 : branch_info_[type_].offset_size; return offset_size; @@ -3511,8 +3559,9 @@ void MipsAssembler::Branch::Relocate(uint32_t expand_location, uint32_t delta) { } void MipsAssembler::Branch::PromoteToLong() { + CHECK(!IsBare()); // Bare branches do not promote. switch (type_) { - // R2 short branches. + // R2 short branches (can be promoted to long). case kUncondBranch: type_ = kLongUncondBranch; break; @@ -3530,7 +3579,7 @@ void MipsAssembler::Branch::PromoteToLong() { case kLiteral: type_ = kFarLiteral; break; - // R6 short branches. + // R6 short branches (can be promoted to long). case kR6UncondBranch: type_ = kR6LongUncondBranch; break; @@ -3585,7 +3634,7 @@ uint32_t MipsAssembler::Branch::PromoteIfNeeded(uint32_t location, uint32_t max_ } // The following logic is for debugging/testing purposes. // Promote some short branches to long when it's not really required. - if (UNLIKELY(max_short_distance != std::numeric_limits<uint32_t>::max())) { + if (UNLIKELY(max_short_distance != std::numeric_limits<uint32_t>::max() && !IsBare())) { int64_t distance = static_cast<int64_t>(target_) - location; distance = (distance >= 0) ? distance : -distance; if (distance >= max_short_distance) { @@ -3851,6 +3900,10 @@ void MipsAssembler::Branch::DecrementLocations() { } void MipsAssembler::MoveInstructionToDelaySlot(Branch& branch) { + if (branch.IsBare()) { + // Delay slots are filled manually in bare branches. + return; + } if (branch.CanHaveDelayedInstruction(delay_slot_)) { // The last instruction cannot be used in a different delay slot, // do not commit the label before it (if any). @@ -3870,27 +3923,32 @@ void MipsAssembler::MoveInstructionToDelaySlot(Branch& branch) { } } -void MipsAssembler::Buncond(MipsLabel* label) { +void MipsAssembler::Buncond(MipsLabel* label, bool is_r6, bool is_bare) { uint32_t target = label->IsBound() ? GetLabelLocation(label) : Branch::kUnresolved; - branches_.emplace_back(IsR6(), buffer_.Size(), target, /* is_call */ false); + branches_.emplace_back(is_r6, buffer_.Size(), target, /* is_call */ false, is_bare); MoveInstructionToDelaySlot(branches_.back()); FinalizeLabeledBranch(label); } -void MipsAssembler::Bcond(MipsLabel* label, BranchCondition condition, Register lhs, Register rhs) { +void MipsAssembler::Bcond(MipsLabel* label, + bool is_r6, + bool is_bare, + BranchCondition condition, + Register lhs, + Register rhs) { // If lhs = rhs, this can be a NOP. if (Branch::IsNop(condition, lhs, rhs)) { return; } uint32_t target = label->IsBound() ? GetLabelLocation(label) : Branch::kUnresolved; - branches_.emplace_back(IsR6(), buffer_.Size(), target, condition, lhs, rhs); + branches_.emplace_back(is_r6, buffer_.Size(), target, condition, lhs, rhs, is_bare); MoveInstructionToDelaySlot(branches_.back()); FinalizeLabeledBranch(label); } -void MipsAssembler::Call(MipsLabel* label) { +void MipsAssembler::Call(MipsLabel* label, bool is_r6, bool is_bare) { uint32_t target = label->IsBound() ? GetLabelLocation(label) : Branch::kUnresolved; - branches_.emplace_back(IsR6(), buffer_.Size(), target, /* is_call */ true); + branches_.emplace_back(is_r6, buffer_.Size(), target, /* is_call */ true, is_bare); MoveInstructionToDelaySlot(branches_.back()); FinalizeLabeledBranch(label); } @@ -4038,10 +4096,14 @@ void MipsAssembler::PromoteBranches() { // Note: make sure branch_info_[] and EmitBranch() are kept synchronized. const MipsAssembler::Branch::BranchInfo MipsAssembler::Branch::branch_info_[] = { - // R2 short branches. + // R2 short branches (can be promoted to long). { 2, 0, 1, MipsAssembler::Branch::kOffset18, 2 }, // kUncondBranch { 2, 0, 1, MipsAssembler::Branch::kOffset18, 2 }, // kCondBranch { 2, 0, 1, MipsAssembler::Branch::kOffset18, 2 }, // kCall + // R2 short branches (can't be promoted to long), delay slots filled manually. + { 1, 0, 1, MipsAssembler::Branch::kOffset18, 2 }, // kBareUncondBranch + { 1, 0, 1, MipsAssembler::Branch::kOffset18, 2 }, // kBareCondBranch + { 1, 0, 1, MipsAssembler::Branch::kOffset18, 2 }, // kBareCall // R2 near label. { 1, 0, 0, MipsAssembler::Branch::kOffset16, 0 }, // kLabel // R2 near literal. @@ -4054,11 +4116,16 @@ const MipsAssembler::Branch::BranchInfo MipsAssembler::Branch::branch_info_[] = { 3, 0, 0, MipsAssembler::Branch::kOffset32, 0 }, // kFarLabel // R2 far literal. { 3, 0, 0, MipsAssembler::Branch::kOffset32, 0 }, // kFarLiteral - // R6 short branches. + // R6 short branches (can be promoted to long). { 1, 0, 1, MipsAssembler::Branch::kOffset28, 2 }, // kR6UncondBranch { 2, 0, 1, MipsAssembler::Branch::kOffset18, 2 }, // kR6CondBranch // Exception: kOffset23 for beqzc/bnezc. { 1, 0, 1, MipsAssembler::Branch::kOffset28, 2 }, // kR6Call + // R6 short branches (can't be promoted to long), forbidden/delay slots filled manually. + { 1, 0, 1, MipsAssembler::Branch::kOffset28, 2 }, // kR6BareUncondBranch + { 1, 0, 1, MipsAssembler::Branch::kOffset18, 2 }, // kR6BareCondBranch + // Exception: kOffset23 for beqzc/bnezc. + { 1, 0, 1, MipsAssembler::Branch::kOffset28, 2 }, // kR6BareCall // R6 near label. { 1, 0, 0, MipsAssembler::Branch::kOffset21, 2 }, // kR6Label // R6 near literal. @@ -4124,6 +4191,21 @@ void MipsAssembler::EmitBranch(MipsAssembler::Branch* branch) { Bal(offset); Emit(delayed_instruction); break; + case Branch::kBareUncondBranch: + DCHECK_EQ(delayed_instruction, Branch::kUnfilledDelaySlot); + CHECK_EQ(overwrite_location_, branch->GetOffsetLocation()); + B(offset); + break; + case Branch::kBareCondBranch: + DCHECK_EQ(delayed_instruction, Branch::kUnfilledDelaySlot); + CHECK_EQ(overwrite_location_, branch->GetOffsetLocation()); + EmitBcondR2(condition, lhs, rhs, offset); + break; + case Branch::kBareCall: + DCHECK_EQ(delayed_instruction, Branch::kUnfilledDelaySlot); + CHECK_EQ(overwrite_location_, branch->GetOffsetLocation()); + Bal(offset); + break; // R2 near label. case Branch::kLabel: @@ -4249,6 +4331,21 @@ void MipsAssembler::EmitBranch(MipsAssembler::Branch* branch) { CHECK_EQ(overwrite_location_, branch->GetOffsetLocation()); Balc(offset); break; + case Branch::kR6BareUncondBranch: + DCHECK_EQ(delayed_instruction, Branch::kUnfilledDelaySlot); + CHECK_EQ(overwrite_location_, branch->GetOffsetLocation()); + Bc(offset); + break; + case Branch::kR6BareCondBranch: + DCHECK_EQ(delayed_instruction, Branch::kUnfilledDelaySlot); + CHECK_EQ(overwrite_location_, branch->GetOffsetLocation()); + EmitBcondR6(condition, lhs, rhs, offset); + break; + case Branch::kR6BareCall: + DCHECK_EQ(delayed_instruction, Branch::kUnfilledDelaySlot); + CHECK_EQ(overwrite_location_, branch->GetOffsetLocation()); + Balc(offset); + break; // R6 near label. case Branch::kR6Label: @@ -4311,44 +4408,44 @@ void MipsAssembler::EmitBranch(MipsAssembler::Branch* branch) { CHECK_LT(branch->GetSize(), static_cast<uint32_t>(Branch::kMaxBranchSize)); } -void MipsAssembler::B(MipsLabel* label) { - Buncond(label); +void MipsAssembler::B(MipsLabel* label, bool is_bare) { + Buncond(label, /* is_r6 */ (IsR6() && !is_bare), is_bare); } -void MipsAssembler::Bal(MipsLabel* label) { - Call(label); +void MipsAssembler::Bal(MipsLabel* label, bool is_bare) { + Call(label, /* is_r6 */ (IsR6() && !is_bare), is_bare); } -void MipsAssembler::Beq(Register rs, Register rt, MipsLabel* label) { - Bcond(label, kCondEQ, rs, rt); +void MipsAssembler::Beq(Register rs, Register rt, MipsLabel* label, bool is_bare) { + Bcond(label, /* is_r6 */ (IsR6() && !is_bare), is_bare, kCondEQ, rs, rt); } -void MipsAssembler::Bne(Register rs, Register rt, MipsLabel* label) { - Bcond(label, kCondNE, rs, rt); +void MipsAssembler::Bne(Register rs, Register rt, MipsLabel* label, bool is_bare) { + Bcond(label, /* is_r6 */ (IsR6() && !is_bare), is_bare, kCondNE, rs, rt); } -void MipsAssembler::Beqz(Register rt, MipsLabel* label) { - Bcond(label, kCondEQZ, rt); +void MipsAssembler::Beqz(Register rt, MipsLabel* label, bool is_bare) { + Bcond(label, /* is_r6 */ (IsR6() && !is_bare), is_bare, kCondEQZ, rt); } -void MipsAssembler::Bnez(Register rt, MipsLabel* label) { - Bcond(label, kCondNEZ, rt); +void MipsAssembler::Bnez(Register rt, MipsLabel* label, bool is_bare) { + Bcond(label, /* is_r6 */ (IsR6() && !is_bare), is_bare, kCondNEZ, rt); } -void MipsAssembler::Bltz(Register rt, MipsLabel* label) { - Bcond(label, kCondLTZ, rt); +void MipsAssembler::Bltz(Register rt, MipsLabel* label, bool is_bare) { + Bcond(label, /* is_r6 */ (IsR6() && !is_bare), is_bare, kCondLTZ, rt); } -void MipsAssembler::Bgez(Register rt, MipsLabel* label) { - Bcond(label, kCondGEZ, rt); +void MipsAssembler::Bgez(Register rt, MipsLabel* label, bool is_bare) { + Bcond(label, /* is_r6 */ (IsR6() && !is_bare), is_bare, kCondGEZ, rt); } -void MipsAssembler::Blez(Register rt, MipsLabel* label) { - Bcond(label, kCondLEZ, rt); +void MipsAssembler::Blez(Register rt, MipsLabel* label, bool is_bare) { + Bcond(label, /* is_r6 */ (IsR6() && !is_bare), is_bare, kCondLEZ, rt); } -void MipsAssembler::Bgtz(Register rt, MipsLabel* label) { - Bcond(label, kCondGTZ, rt); +void MipsAssembler::Bgtz(Register rt, MipsLabel* label, bool is_bare) { + Bcond(label, /* is_r6 */ (IsR6() && !is_bare), is_bare, kCondGTZ, rt); } bool MipsAssembler::CanExchangeWithSlt(Register rs, Register rt) const { @@ -4399,74 +4496,130 @@ void MipsAssembler::GenerateSltForCondBranch(bool unsigned_slt, Register rs, Reg } } -void MipsAssembler::Blt(Register rs, Register rt, MipsLabel* label) { - if (IsR6()) { - Bcond(label, kCondLT, rs, rt); +void MipsAssembler::Blt(Register rs, Register rt, MipsLabel* label, bool is_bare) { + if (IsR6() && !is_bare) { + Bcond(label, IsR6(), is_bare, kCondLT, rs, rt); } else if (!Branch::IsNop(kCondLT, rs, rt)) { // Synthesize the instruction (not available on R2). GenerateSltForCondBranch(/* unsigned_slt */ false, rs, rt); - Bnez(AT, label); + Bnez(AT, label, is_bare); } } -void MipsAssembler::Bge(Register rs, Register rt, MipsLabel* label) { - if (IsR6()) { - Bcond(label, kCondGE, rs, rt); +void MipsAssembler::Bge(Register rs, Register rt, MipsLabel* label, bool is_bare) { + if (IsR6() && !is_bare) { + Bcond(label, IsR6(), is_bare, kCondGE, rs, rt); } else if (Branch::IsUncond(kCondGE, rs, rt)) { - B(label); + B(label, is_bare); } else { // Synthesize the instruction (not available on R2). GenerateSltForCondBranch(/* unsigned_slt */ false, rs, rt); - Beqz(AT, label); + Beqz(AT, label, is_bare); } } -void MipsAssembler::Bltu(Register rs, Register rt, MipsLabel* label) { - if (IsR6()) { - Bcond(label, kCondLTU, rs, rt); +void MipsAssembler::Bltu(Register rs, Register rt, MipsLabel* label, bool is_bare) { + if (IsR6() && !is_bare) { + Bcond(label, IsR6(), is_bare, kCondLTU, rs, rt); } else if (!Branch::IsNop(kCondLTU, rs, rt)) { // Synthesize the instruction (not available on R2). GenerateSltForCondBranch(/* unsigned_slt */ true, rs, rt); - Bnez(AT, label); + Bnez(AT, label, is_bare); } } -void MipsAssembler::Bgeu(Register rs, Register rt, MipsLabel* label) { - if (IsR6()) { - Bcond(label, kCondGEU, rs, rt); +void MipsAssembler::Bgeu(Register rs, Register rt, MipsLabel* label, bool is_bare) { + if (IsR6() && !is_bare) { + Bcond(label, IsR6(), is_bare, kCondGEU, rs, rt); } else if (Branch::IsUncond(kCondGEU, rs, rt)) { - B(label); + B(label, is_bare); } else { // Synthesize the instruction (not available on R2). GenerateSltForCondBranch(/* unsigned_slt */ true, rs, rt); - Beqz(AT, label); + Beqz(AT, label, is_bare); } } -void MipsAssembler::Bc1f(MipsLabel* label) { - Bc1f(0, label); +void MipsAssembler::Bc1f(MipsLabel* label, bool is_bare) { + Bc1f(0, label, is_bare); } -void MipsAssembler::Bc1f(int cc, MipsLabel* label) { +void MipsAssembler::Bc1f(int cc, MipsLabel* label, bool is_bare) { CHECK(IsUint<3>(cc)) << cc; - Bcond(label, kCondF, static_cast<Register>(cc), ZERO); + Bcond(label, /* is_r6 */ false, is_bare, kCondF, static_cast<Register>(cc), ZERO); } -void MipsAssembler::Bc1t(MipsLabel* label) { - Bc1t(0, label); +void MipsAssembler::Bc1t(MipsLabel* label, bool is_bare) { + Bc1t(0, label, is_bare); } -void MipsAssembler::Bc1t(int cc, MipsLabel* label) { +void MipsAssembler::Bc1t(int cc, MipsLabel* label, bool is_bare) { CHECK(IsUint<3>(cc)) << cc; - Bcond(label, kCondT, static_cast<Register>(cc), ZERO); + Bcond(label, /* is_r6 */ false, is_bare, kCondT, static_cast<Register>(cc), ZERO); +} + +void MipsAssembler::Bc(MipsLabel* label, bool is_bare) { + Buncond(label, /* is_r6 */ true, is_bare); +} + +void MipsAssembler::Balc(MipsLabel* label, bool is_bare) { + Call(label, /* is_r6 */ true, is_bare); +} + +void MipsAssembler::Beqc(Register rs, Register rt, MipsLabel* label, bool is_bare) { + Bcond(label, /* is_r6 */ true, is_bare, kCondEQ, rs, rt); +} + +void MipsAssembler::Bnec(Register rs, Register rt, MipsLabel* label, bool is_bare) { + Bcond(label, /* is_r6 */ true, is_bare, kCondNE, rs, rt); +} + +void MipsAssembler::Beqzc(Register rt, MipsLabel* label, bool is_bare) { + Bcond(label, /* is_r6 */ true, is_bare, kCondEQZ, rt); +} + +void MipsAssembler::Bnezc(Register rt, MipsLabel* label, bool is_bare) { + Bcond(label, /* is_r6 */ true, is_bare, kCondNEZ, rt); +} + +void MipsAssembler::Bltzc(Register rt, MipsLabel* label, bool is_bare) { + Bcond(label, /* is_r6 */ true, is_bare, kCondLTZ, rt); +} + +void MipsAssembler::Bgezc(Register rt, MipsLabel* label, bool is_bare) { + Bcond(label, /* is_r6 */ true, is_bare, kCondGEZ, rt); +} + +void MipsAssembler::Blezc(Register rt, MipsLabel* label, bool is_bare) { + Bcond(label, /* is_r6 */ true, is_bare, kCondLEZ, rt); +} + +void MipsAssembler::Bgtzc(Register rt, MipsLabel* label, bool is_bare) { + Bcond(label, /* is_r6 */ true, is_bare, kCondGTZ, rt); +} + +void MipsAssembler::Bltc(Register rs, Register rt, MipsLabel* label, bool is_bare) { + Bcond(label, /* is_r6 */ true, is_bare, kCondLT, rs, rt); +} + +void MipsAssembler::Bgec(Register rs, Register rt, MipsLabel* label, bool is_bare) { + Bcond(label, /* is_r6 */ true, is_bare, kCondGE, rs, rt); +} + +void MipsAssembler::Bltuc(Register rs, Register rt, MipsLabel* label, bool is_bare) { + Bcond(label, /* is_r6 */ true, is_bare, kCondLTU, rs, rt); +} + +void MipsAssembler::Bgeuc(Register rs, Register rt, MipsLabel* label, bool is_bare) { + Bcond(label, /* is_r6 */ true, is_bare, kCondGEU, rs, rt); } -void MipsAssembler::Bc1eqz(FRegister ft, MipsLabel* label) { - Bcond(label, kCondF, static_cast<Register>(ft), ZERO); +void MipsAssembler::Bc1eqz(FRegister ft, MipsLabel* label, bool is_bare) { + Bcond(label, /* is_r6 */ true, is_bare, kCondF, static_cast<Register>(ft), ZERO); } -void MipsAssembler::Bc1nez(FRegister ft, MipsLabel* label) { - Bcond(label, kCondT, static_cast<Register>(ft), ZERO); +void MipsAssembler::Bc1nez(FRegister ft, MipsLabel* label, bool is_bare) { + Bcond(label, /* is_r6 */ true, is_bare, kCondT, static_cast<Register>(ft), ZERO); } void MipsAssembler::AdjustBaseAndOffset(Register& base, diff --git a/compiler/utils/mips/assembler_mips.h b/compiler/utils/mips/assembler_mips.h index a7ff931e7e..7f9d5763f1 100644 --- a/compiler/utils/mips/assembler_mips.h +++ b/compiler/utils/mips/assembler_mips.h @@ -636,29 +636,69 @@ class MipsAssembler FINAL : public Assembler, public JNIMacroAssembler<PointerSi void LoadSConst32(FRegister r, int32_t value, Register temp); void Addiu32(Register rt, Register rs, int32_t value, Register rtmp = AT); - // These will generate R2 branches or R6 branches as appropriate and take care of - // the delay/forbidden slots. void Bind(MipsLabel* label); - void B(MipsLabel* label); - void Bal(MipsLabel* label); - void Beq(Register rs, Register rt, MipsLabel* label); - void Bne(Register rs, Register rt, MipsLabel* label); - void Beqz(Register rt, MipsLabel* label); - void Bnez(Register rt, MipsLabel* label); - void Bltz(Register rt, MipsLabel* label); - void Bgez(Register rt, MipsLabel* label); - void Blez(Register rt, MipsLabel* label); - void Bgtz(Register rt, MipsLabel* label); - void Blt(Register rs, Register rt, MipsLabel* label); - void Bge(Register rs, Register rt, MipsLabel* label); - void Bltu(Register rs, Register rt, MipsLabel* label); - void Bgeu(Register rs, Register rt, MipsLabel* label); - void Bc1f(MipsLabel* label); // R2 - void Bc1f(int cc, MipsLabel* label); // R2 - void Bc1t(MipsLabel* label); // R2 - void Bc1t(int cc, MipsLabel* label); // R2 - void Bc1eqz(FRegister ft, MipsLabel* label); // R6 - void Bc1nez(FRegister ft, MipsLabel* label); // R6 + // When `is_bare` is false, the branches will promote to long (if the range + // of the individual branch instruction is insufficient) and the delay/ + // forbidden slots will be taken care of. + // Use `is_bare = false` when the branch target may be out of reach of the + // individual branch instruction. IOW, this is for general purpose use. + // + // When `is_bare` is true, just the branch instructions will be generated + // leaving delay/forbidden slot filling up to the caller and the branches + // won't promote to long if the range is insufficient (you'll get a + // compilation error when the range is exceeded). + // Use `is_bare = true` when the branch target is known to be within reach + // of the individual branch instruction. This is intended for small local + // optimizations around delay/forbidden slots. + // Also prefer using `is_bare = true` if the code near the branch is to be + // patched or analyzed at run time (e.g. introspection) to + // - show the intent and + // - fail during compilation rather than during patching/execution if the + // bare branch range is insufficent but the code size and layout are + // expected to remain unchanged + // + // R2 branches with delay slots that are also available on R6. + // On R6 when `is_bare` is false these convert to equivalent R6 compact + // branches (to reduce code size). On R2 or when `is_bare` is true they + // remain R2 branches with delay slots. + void B(MipsLabel* label, bool is_bare = false); + void Bal(MipsLabel* label, bool is_bare = false); + void Beq(Register rs, Register rt, MipsLabel* label, bool is_bare = false); + void Bne(Register rs, Register rt, MipsLabel* label, bool is_bare = false); + void Beqz(Register rt, MipsLabel* label, bool is_bare = false); + void Bnez(Register rt, MipsLabel* label, bool is_bare = false); + void Bltz(Register rt, MipsLabel* label, bool is_bare = false); + void Bgez(Register rt, MipsLabel* label, bool is_bare = false); + void Blez(Register rt, MipsLabel* label, bool is_bare = false); + void Bgtz(Register rt, MipsLabel* label, bool is_bare = false); + void Blt(Register rs, Register rt, MipsLabel* label, bool is_bare = false); + void Bge(Register rs, Register rt, MipsLabel* label, bool is_bare = false); + void Bltu(Register rs, Register rt, MipsLabel* label, bool is_bare = false); + void Bgeu(Register rs, Register rt, MipsLabel* label, bool is_bare = false); + // R2-only branches with delay slots. + void Bc1f(MipsLabel* label, bool is_bare = false); // R2 + void Bc1f(int cc, MipsLabel* label, bool is_bare = false); // R2 + void Bc1t(MipsLabel* label, bool is_bare = false); // R2 + void Bc1t(int cc, MipsLabel* label, bool is_bare = false); // R2 + // R6-only compact branches without delay/forbidden slots. + void Bc(MipsLabel* label, bool is_bare = false); // R6 + void Balc(MipsLabel* label, bool is_bare = false); // R6 + // R6-only compact branches with forbidden slots. + void Beqc(Register rs, Register rt, MipsLabel* label, bool is_bare = false); // R6 + void Bnec(Register rs, Register rt, MipsLabel* label, bool is_bare = false); // R6 + void Beqzc(Register rt, MipsLabel* label, bool is_bare = false); // R6 + void Bnezc(Register rt, MipsLabel* label, bool is_bare = false); // R6 + void Bltzc(Register rt, MipsLabel* label, bool is_bare = false); // R6 + void Bgezc(Register rt, MipsLabel* label, bool is_bare = false); // R6 + void Blezc(Register rt, MipsLabel* label, bool is_bare = false); // R6 + void Bgtzc(Register rt, MipsLabel* label, bool is_bare = false); // R6 + void Bltc(Register rs, Register rt, MipsLabel* label, bool is_bare = false); // R6 + void Bgec(Register rs, Register rt, MipsLabel* label, bool is_bare = false); // R6 + void Bltuc(Register rs, Register rt, MipsLabel* label, bool is_bare = false); // R6 + void Bgeuc(Register rs, Register rt, MipsLabel* label, bool is_bare = false); // R6 + // R6-only branches with delay slots. + void Bc1eqz(FRegister ft, MipsLabel* label, bool is_bare = false); // R6 + void Bc1nez(FRegister ft, MipsLabel* label, bool is_bare = false); // R6 void EmitLoad(ManagedRegister m_dst, Register src_register, int32_t src_offset, size_t size); void AdjustBaseAndOffset(Register& base, @@ -1268,10 +1308,14 @@ class MipsAssembler FINAL : public Assembler, public JNIMacroAssembler<PointerSi class Branch { public: enum Type { - // R2 short branches. + // R2 short branches (can be promoted to long). kUncondBranch, kCondBranch, kCall, + // R2 short branches (can't be promoted to long), delay slots filled manually. + kBareUncondBranch, + kBareCondBranch, + kBareCall, // R2 near label. kLabel, // R2 near literal. @@ -1284,10 +1328,14 @@ class MipsAssembler FINAL : public Assembler, public JNIMacroAssembler<PointerSi kFarLabel, // R2 far literal. kFarLiteral, - // R6 short branches. + // R6 short branches (can be promoted to long). kR6UncondBranch, kR6CondBranch, kR6Call, + // R6 short branches (can't be promoted to long), forbidden/delay slots filled manually. + kR6BareUncondBranch, + kR6BareCondBranch, + kR6BareCall, // R6 near label. kR6Label, // R6 near literal. @@ -1337,7 +1385,7 @@ class MipsAssembler FINAL : public Assembler, public JNIMacroAssembler<PointerSi // instructions) from the instruction containing the offset. uint32_t pc_org; // How large (in bits) a PC-relative offset can be for a given type of branch (kR6CondBranch - // is an exception: use kOffset23 for beqzc/bnezc). + // and kR6BareCondBranch are an exception: use kOffset23 for beqzc/bnezc). OffsetBits offset_size; // Some MIPS instructions with PC-relative offsets shift the offset by 2. Encode the shift // count. @@ -1346,14 +1394,15 @@ class MipsAssembler FINAL : public Assembler, public JNIMacroAssembler<PointerSi static const BranchInfo branch_info_[/* Type */]; // Unconditional branch or call. - Branch(bool is_r6, uint32_t location, uint32_t target, bool is_call); + Branch(bool is_r6, uint32_t location, uint32_t target, bool is_call, bool is_bare); // Conditional branch. Branch(bool is_r6, uint32_t location, uint32_t target, BranchCondition condition, Register lhs_reg, - Register rhs_reg); + Register rhs_reg, + bool is_bare); // Label address (in literal area) or literal. Branch(bool is_r6, uint32_t location, @@ -1385,6 +1434,7 @@ class MipsAssembler FINAL : public Assembler, public JNIMacroAssembler<PointerSi uint32_t GetOldSize() const; uint32_t GetEndLocation() const; uint32_t GetOldEndLocation() const; + bool IsBare() const; bool IsLong() const; bool IsResolved() const; @@ -1513,9 +1563,14 @@ class MipsAssembler FINAL : public Assembler, public JNIMacroAssembler<PointerSi VectorRegister wd, int minor_opcode); - void Buncond(MipsLabel* label); - void Bcond(MipsLabel* label, BranchCondition condition, Register lhs, Register rhs = ZERO); - void Call(MipsLabel* label); + void Buncond(MipsLabel* label, bool is_r6, bool is_bare); + void Bcond(MipsLabel* label, + bool is_r6, + bool is_bare, + BranchCondition condition, + Register lhs, + Register rhs = ZERO); + void Call(MipsLabel* label, bool is_r6, bool is_bare); void FinalizeLabeledBranch(MipsLabel* label); // Various helpers for branch delay slot management. diff --git a/compiler/utils/mips/assembler_mips32r6_test.cc b/compiler/utils/mips/assembler_mips32r6_test.cc index b72a14e906..6e52b17000 100644 --- a/compiler/utils/mips/assembler_mips32r6_test.cc +++ b/compiler/utils/mips/assembler_mips32r6_test.cc @@ -259,12 +259,119 @@ class AssemblerMIPS32r6Test : public AssemblerTest<mips::MipsAssembler, return result; } + void BranchHelper(void (mips::MipsAssembler::*f)(mips::MipsLabel*, + bool), + const std::string& instr_name, + bool has_slot, + bool is_bare = false) { + __ SetReorder(false); + mips::MipsLabel label1, label2; + (Base::GetAssembler()->*f)(&label1, is_bare); + constexpr size_t kAdduCount1 = 63; + for (size_t i = 0; i != kAdduCount1; ++i) { + __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); + } + __ Bind(&label1); + (Base::GetAssembler()->*f)(&label2, is_bare); + constexpr size_t kAdduCount2 = 64; + for (size_t i = 0; i != kAdduCount2; ++i) { + __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); + } + __ Bind(&label2); + (Base::GetAssembler()->*f)(&label1, is_bare); + __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); + + std::string expected = + ".set noreorder\n" + + instr_name + " 1f\n" + + ((is_bare || !has_slot) ? "" : "nop\n") + + RepeatInsn(kAdduCount1, "addu $zero, $zero, $zero\n") + + "1:\n" + + instr_name + " 2f\n" + + ((is_bare || !has_slot) ? "" : "nop\n") + + RepeatInsn(kAdduCount2, "addu $zero, $zero, $zero\n") + + "2:\n" + + instr_name + " 1b\n" + + ((is_bare || !has_slot) ? "" : "nop\n") + + "addu $zero, $zero, $zero\n"; + DriverStr(expected, instr_name); + } + + void BranchCondOneRegHelper(void (mips::MipsAssembler::*f)(mips::Register, + mips::MipsLabel*, + bool), + const std::string& instr_name, + bool is_bare = false) { + __ SetReorder(false); + mips::MipsLabel label; + (Base::GetAssembler()->*f)(mips::A0, &label, is_bare); + constexpr size_t kAdduCount1 = 63; + for (size_t i = 0; i != kAdduCount1; ++i) { + __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); + } + __ Bind(&label); + constexpr size_t kAdduCount2 = 64; + for (size_t i = 0; i != kAdduCount2; ++i) { + __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); + } + (Base::GetAssembler()->*f)(mips::A1, &label, is_bare); + __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); + + std::string expected = + ".set noreorder\n" + + instr_name + " $a0, 1f\n" + + (is_bare ? "" : "nop\n") + + RepeatInsn(kAdduCount1, "addu $zero, $zero, $zero\n") + + "1:\n" + + RepeatInsn(kAdduCount2, "addu $zero, $zero, $zero\n") + + instr_name + " $a1, 1b\n" + + (is_bare ? "" : "nop\n") + + "addu $zero, $zero, $zero\n"; + DriverStr(expected, instr_name); + } + void BranchCondTwoRegsHelper(void (mips::MipsAssembler::*f)(mips::Register, mips::Register, - mips::MipsLabel*), - const std::string& instr_name) { + mips::MipsLabel*, + bool), + const std::string& instr_name, + bool is_bare = false) { + __ SetReorder(false); + mips::MipsLabel label; + (Base::GetAssembler()->*f)(mips::A0, mips::A1, &label, is_bare); + constexpr size_t kAdduCount1 = 63; + for (size_t i = 0; i != kAdduCount1; ++i) { + __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); + } + __ Bind(&label); + constexpr size_t kAdduCount2 = 64; + for (size_t i = 0; i != kAdduCount2; ++i) { + __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); + } + (Base::GetAssembler()->*f)(mips::A2, mips::A3, &label, is_bare); + __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); + + std::string expected = + ".set noreorder\n" + + instr_name + " $a0, $a1, 1f\n" + + (is_bare ? "" : "nop\n") + + RepeatInsn(kAdduCount1, "addu $zero, $zero, $zero\n") + + "1:\n" + + RepeatInsn(kAdduCount2, "addu $zero, $zero, $zero\n") + + instr_name + " $a2, $a3, 1b\n" + + (is_bare ? "" : "nop\n") + + "addu $zero, $zero, $zero\n"; + DriverStr(expected, instr_name); + } + + void BranchFpuCondHelper(void (mips::MipsAssembler::*f)(mips::FRegister, + mips::MipsLabel*, + bool), + const std::string& instr_name, + bool is_bare = false) { + __ SetReorder(false); mips::MipsLabel label; - (Base::GetAssembler()->*f)(mips::A0, mips::A1, &label); + (Base::GetAssembler()->*f)(mips::F0, &label, is_bare); constexpr size_t kAdduCount1 = 63; for (size_t i = 0; i != kAdduCount1; ++i) { __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); @@ -274,17 +381,19 @@ class AssemblerMIPS32r6Test : public AssemblerTest<mips::MipsAssembler, for (size_t i = 0; i != kAdduCount2; ++i) { __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); } - (Base::GetAssembler()->*f)(mips::A2, mips::A3, &label); + (Base::GetAssembler()->*f)(mips::F30, &label, is_bare); + __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); std::string expected = ".set noreorder\n" + - instr_name + " $a0, $a1, 1f\n" - "nop\n" + + instr_name + " $f0, 1f\n" + + (is_bare ? "" : "nop\n") + RepeatInsn(kAdduCount1, "addu $zero, $zero, $zero\n") + "1:\n" + RepeatInsn(kAdduCount2, "addu $zero, $zero, $zero\n") + - instr_name + " $a2, $a3, 1b\n" - "nop\n"; + instr_name + " $f30, 1b\n" + + (is_bare ? "" : "nop\n") + + "addu $zero, $zero, $zero\n"; DriverStr(expected, instr_name); } @@ -947,78 +1056,386 @@ TEST_F(AssemblerMIPS32r6Test, StoreQToOffset) { DriverStr(expected, "StoreQToOffset"); } -TEST_F(AssemblerMIPS32r6Test, LoadFarthestNearLabelAddress) { +////////////// +// BRANCHES // +////////////// + +TEST_F(AssemblerMIPS32r6Test, Bc) { + BranchHelper(&mips::MipsAssembler::Bc, "Bc", /* has_slot */ false); +} + +TEST_F(AssemblerMIPS32r6Test, Balc) { + BranchHelper(&mips::MipsAssembler::Balc, "Balc", /* has_slot */ false); +} + +TEST_F(AssemblerMIPS32r6Test, Beqc) { + BranchCondTwoRegsHelper(&mips::MipsAssembler::Beqc, "Beqc"); +} + +TEST_F(AssemblerMIPS32r6Test, Bnec) { + BranchCondTwoRegsHelper(&mips::MipsAssembler::Bnec, "Bnec"); +} + +TEST_F(AssemblerMIPS32r6Test, Beqzc) { + BranchCondOneRegHelper(&mips::MipsAssembler::Beqzc, "Beqzc"); +} + +TEST_F(AssemblerMIPS32r6Test, Bnezc) { + BranchCondOneRegHelper(&mips::MipsAssembler::Bnezc, "Bnezc"); +} + +TEST_F(AssemblerMIPS32r6Test, Bltzc) { + BranchCondOneRegHelper(&mips::MipsAssembler::Bltzc, "Bltzc"); +} + +TEST_F(AssemblerMIPS32r6Test, Bgezc) { + BranchCondOneRegHelper(&mips::MipsAssembler::Bgezc, "Bgezc"); +} + +TEST_F(AssemblerMIPS32r6Test, Blezc) { + BranchCondOneRegHelper(&mips::MipsAssembler::Blezc, "Blezc"); +} + +TEST_F(AssemblerMIPS32r6Test, Bgtzc) { + BranchCondOneRegHelper(&mips::MipsAssembler::Bgtzc, "Bgtzc"); +} + +TEST_F(AssemblerMIPS32r6Test, Bltc) { + BranchCondTwoRegsHelper(&mips::MipsAssembler::Bltc, "Bltc"); +} + +TEST_F(AssemblerMIPS32r6Test, Bgec) { + BranchCondTwoRegsHelper(&mips::MipsAssembler::Bgec, "Bgec"); +} + +TEST_F(AssemblerMIPS32r6Test, Bltuc) { + BranchCondTwoRegsHelper(&mips::MipsAssembler::Bltuc, "Bltuc"); +} + +TEST_F(AssemblerMIPS32r6Test, Bgeuc) { + BranchCondTwoRegsHelper(&mips::MipsAssembler::Bgeuc, "Bgeuc"); +} + +TEST_F(AssemblerMIPS32r6Test, Bc1eqz) { + BranchFpuCondHelper(&mips::MipsAssembler::Bc1eqz, "Bc1eqz"); +} + +TEST_F(AssemblerMIPS32r6Test, Bc1nez) { + BranchFpuCondHelper(&mips::MipsAssembler::Bc1nez, "Bc1nez"); +} + +TEST_F(AssemblerMIPS32r6Test, B) { + BranchHelper(&mips::MipsAssembler::B, "Bc", /* has_slot */ false); +} + +TEST_F(AssemblerMIPS32r6Test, Bal) { + BranchHelper(&mips::MipsAssembler::Bal, "Balc", /* has_slot */ false); +} + +TEST_F(AssemblerMIPS32r6Test, Beq) { + BranchCondTwoRegsHelper(&mips::MipsAssembler::Beq, "Beqc"); +} + +TEST_F(AssemblerMIPS32r6Test, Bne) { + BranchCondTwoRegsHelper(&mips::MipsAssembler::Bne, "Bnec"); +} + +TEST_F(AssemblerMIPS32r6Test, Beqz) { + BranchCondOneRegHelper(&mips::MipsAssembler::Beqz, "Beqzc"); +} + +TEST_F(AssemblerMIPS32r6Test, Bnez) { + BranchCondOneRegHelper(&mips::MipsAssembler::Bnez, "Bnezc"); +} + +TEST_F(AssemblerMIPS32r6Test, Bltz) { + BranchCondOneRegHelper(&mips::MipsAssembler::Bltz, "Bltzc"); +} + +TEST_F(AssemblerMIPS32r6Test, Bgez) { + BranchCondOneRegHelper(&mips::MipsAssembler::Bgez, "Bgezc"); +} + +TEST_F(AssemblerMIPS32r6Test, Blez) { + BranchCondOneRegHelper(&mips::MipsAssembler::Blez, "Blezc"); +} + +TEST_F(AssemblerMIPS32r6Test, Bgtz) { + BranchCondOneRegHelper(&mips::MipsAssembler::Bgtz, "Bgtzc"); +} + +TEST_F(AssemblerMIPS32r6Test, Blt) { + BranchCondTwoRegsHelper(&mips::MipsAssembler::Blt, "Bltc"); +} + +TEST_F(AssemblerMIPS32r6Test, Bge) { + BranchCondTwoRegsHelper(&mips::MipsAssembler::Bge, "Bgec"); +} + +TEST_F(AssemblerMIPS32r6Test, Bltu) { + BranchCondTwoRegsHelper(&mips::MipsAssembler::Bltu, "Bltuc"); +} + +TEST_F(AssemblerMIPS32r6Test, Bgeu) { + BranchCondTwoRegsHelper(&mips::MipsAssembler::Bgeu, "Bgeuc"); +} + +TEST_F(AssemblerMIPS32r6Test, BareBc) { + BranchHelper(&mips::MipsAssembler::Bc, "Bc", /* has_slot */ false, /* is_bare */ true); +} + +TEST_F(AssemblerMIPS32r6Test, BareBalc) { + BranchHelper(&mips::MipsAssembler::Balc, "Balc", /* has_slot */ false, /* is_bare */ true); +} + +TEST_F(AssemblerMIPS32r6Test, BareBeqc) { + BranchCondTwoRegsHelper(&mips::MipsAssembler::Beqc, "Beqc", /* is_bare */ true); +} + +TEST_F(AssemblerMIPS32r6Test, BareBnec) { + BranchCondTwoRegsHelper(&mips::MipsAssembler::Bnec, "Bnec", /* is_bare */ true); +} + +TEST_F(AssemblerMIPS32r6Test, BareBeqzc) { + BranchCondOneRegHelper(&mips::MipsAssembler::Beqzc, "Beqzc", /* is_bare */ true); +} + +TEST_F(AssemblerMIPS32r6Test, BareBnezc) { + BranchCondOneRegHelper(&mips::MipsAssembler::Bnezc, "Bnezc", /* is_bare */ true); +} + +TEST_F(AssemblerMIPS32r6Test, BareBltzc) { + BranchCondOneRegHelper(&mips::MipsAssembler::Bltzc, "Bltzc", /* is_bare */ true); +} + +TEST_F(AssemblerMIPS32r6Test, BareBgezc) { + BranchCondOneRegHelper(&mips::MipsAssembler::Bgezc, "Bgezc", /* is_bare */ true); +} + +TEST_F(AssemblerMIPS32r6Test, BareBlezc) { + BranchCondOneRegHelper(&mips::MipsAssembler::Blezc, "Blezc", /* is_bare */ true); +} + +TEST_F(AssemblerMIPS32r6Test, BareBgtzc) { + BranchCondOneRegHelper(&mips::MipsAssembler::Bgtzc, "Bgtzc", /* is_bare */ true); +} + +TEST_F(AssemblerMIPS32r6Test, BareBltc) { + BranchCondTwoRegsHelper(&mips::MipsAssembler::Bltc, "Bltc", /* is_bare */ true); +} + +TEST_F(AssemblerMIPS32r6Test, BareBgec) { + BranchCondTwoRegsHelper(&mips::MipsAssembler::Bgec, "Bgec", /* is_bare */ true); +} + +TEST_F(AssemblerMIPS32r6Test, BareBltuc) { + BranchCondTwoRegsHelper(&mips::MipsAssembler::Bltuc, "Bltuc", /* is_bare */ true); +} + +TEST_F(AssemblerMIPS32r6Test, BareBgeuc) { + BranchCondTwoRegsHelper(&mips::MipsAssembler::Bgeuc, "Bgeuc", /* is_bare */ true); +} + +TEST_F(AssemblerMIPS32r6Test, BareBc1eqz) { + BranchFpuCondHelper(&mips::MipsAssembler::Bc1eqz, "Bc1eqz", /* is_bare */ true); +} + +TEST_F(AssemblerMIPS32r6Test, BareBc1nez) { + BranchFpuCondHelper(&mips::MipsAssembler::Bc1nez, "Bc1nez", /* is_bare */ true); +} + +TEST_F(AssemblerMIPS32r6Test, BareB) { + BranchHelper(&mips::MipsAssembler::B, "B", /* has_slot */ true, /* is_bare */ true); +} + +TEST_F(AssemblerMIPS32r6Test, BareBal) { + BranchHelper(&mips::MipsAssembler::Bal, "Bal", /* has_slot */ true, /* is_bare */ true); +} + +TEST_F(AssemblerMIPS32r6Test, BareBeq) { + BranchCondTwoRegsHelper(&mips::MipsAssembler::Beq, "Beq", /* is_bare */ true); +} + +TEST_F(AssemblerMIPS32r6Test, BareBne) { + BranchCondTwoRegsHelper(&mips::MipsAssembler::Bne, "Bne", /* is_bare */ true); +} + +TEST_F(AssemblerMIPS32r6Test, BareBeqz) { + BranchCondOneRegHelper(&mips::MipsAssembler::Beqz, "Beqz", /* is_bare */ true); +} + +TEST_F(AssemblerMIPS32r6Test, BareBnez) { + BranchCondOneRegHelper(&mips::MipsAssembler::Bnez, "Bnez", /* is_bare */ true); +} + +TEST_F(AssemblerMIPS32r6Test, BareBltz) { + BranchCondOneRegHelper(&mips::MipsAssembler::Bltz, "Bltz", /* is_bare */ true); +} + +TEST_F(AssemblerMIPS32r6Test, BareBgez) { + BranchCondOneRegHelper(&mips::MipsAssembler::Bgez, "Bgez", /* is_bare */ true); +} + +TEST_F(AssemblerMIPS32r6Test, BareBlez) { + BranchCondOneRegHelper(&mips::MipsAssembler::Blez, "Blez", /* is_bare */ true); +} + +TEST_F(AssemblerMIPS32r6Test, BareBgtz) { + BranchCondOneRegHelper(&mips::MipsAssembler::Bgtz, "Bgtz", /* is_bare */ true); +} + +TEST_F(AssemblerMIPS32r6Test, BareBlt) { + BranchCondTwoRegsHelper(&mips::MipsAssembler::Blt, "Blt", /* is_bare */ true); +} + +TEST_F(AssemblerMIPS32r6Test, BareBge) { + BranchCondTwoRegsHelper(&mips::MipsAssembler::Bge, "Bge", /* is_bare */ true); +} + +TEST_F(AssemblerMIPS32r6Test, BareBltu) { + BranchCondTwoRegsHelper(&mips::MipsAssembler::Bltu, "Bltu", /* is_bare */ true); +} + +TEST_F(AssemblerMIPS32r6Test, BareBgeu) { + BranchCondTwoRegsHelper(&mips::MipsAssembler::Bgeu, "Bgeu", /* is_bare */ true); +} + +TEST_F(AssemblerMIPS32r6Test, LongBeqc) { mips::MipsLabel label; - __ LoadLabelAddress(mips::V0, mips::ZERO, &label); - constexpr size_t kAdduCount = 0x3FFDE; - for (size_t i = 0; i != kAdduCount; ++i) { + __ Beqc(mips::A0, mips::A1, &label); + constexpr uint32_t kAdduCount1 = (1u << 15) + 1; + for (uint32_t i = 0; i != kAdduCount1; ++i) { __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); } __ Bind(&label); + constexpr uint32_t kAdduCount2 = (1u << 15) + 1; + for (uint32_t i = 0; i != kAdduCount2; ++i) { + __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); + } + __ Beqc(mips::A2, mips::A3, &label); - std::string expected = - "lapc $v0, 1f\n" + - RepeatInsn(kAdduCount, "addu $zero, $zero, $zero\n") + - "1:\n"; - DriverStr(expected, "LoadFarthestNearLabelAddress"); + uint32_t offset_forward = 2 + kAdduCount1; // 2: account for auipc and jic. + offset_forward <<= 2; + offset_forward += (offset_forward & 0x8000) << 1; // Account for sign extension in jic. + + uint32_t offset_back = -(kAdduCount2 + 1); // 1: account for bnec. + offset_back <<= 2; + offset_back += (offset_back & 0x8000) << 1; // Account for sign extension in jic. + + std::ostringstream oss; + oss << + ".set noreorder\n" + "bnec $a0, $a1, 1f\n" + "auipc $at, 0x" << std::hex << High16Bits(offset_forward) << "\n" + "jic $at, 0x" << std::hex << Low16Bits(offset_forward) << "\n" + "1:\n" << + RepeatInsn(kAdduCount1, "addu $zero, $zero, $zero\n") << + "2:\n" << + RepeatInsn(kAdduCount2, "addu $zero, $zero, $zero\n") << + "bnec $a2, $a3, 3f\n" + "auipc $at, 0x" << std::hex << High16Bits(offset_back) << "\n" + "jic $at, 0x" << std::hex << Low16Bits(offset_back) << "\n" + "3:\n"; + std::string expected = oss.str(); + DriverStr(expected, "LongBeqc"); } -TEST_F(AssemblerMIPS32r6Test, LoadNearestFarLabelAddress) { +TEST_F(AssemblerMIPS32r6Test, LongBeqzc) { + constexpr uint32_t kNopCount1 = (1u << 20) + 1; + constexpr uint32_t kNopCount2 = (1u << 20) + 1; + constexpr uint32_t kRequiredCapacity = (kNopCount1 + kNopCount2 + 6u) * 4u; + ASSERT_LT(__ GetBuffer()->Capacity(), kRequiredCapacity); + __ GetBuffer()->ExtendCapacity(kRequiredCapacity); mips::MipsLabel label; - __ LoadLabelAddress(mips::V0, mips::ZERO, &label); - constexpr size_t kAdduCount = 0x3FFDF; - for (size_t i = 0; i != kAdduCount; ++i) { - __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); + __ Beqzc(mips::A0, &label); + for (uint32_t i = 0; i != kNopCount1; ++i) { + __ Nop(); } __ Bind(&label); + for (uint32_t i = 0; i != kNopCount2; ++i) { + __ Nop(); + } + __ Beqzc(mips::A2, &label); - std::string expected = - "1:\n" - "auipc $at, %hi(2f - 1b)\n" - "addiu $v0, $at, %lo(2f - 1b)\n" + - RepeatInsn(kAdduCount, "addu $zero, $zero, $zero\n") + - "2:\n"; - DriverStr(expected, "LoadNearestFarLabelAddress"); -} + uint32_t offset_forward = 2 + kNopCount1; // 2: account for auipc and jic. + offset_forward <<= 2; + offset_forward += (offset_forward & 0x8000) << 1; // Account for sign extension in jic. -TEST_F(AssemblerMIPS32r6Test, LoadFarthestNearLiteral) { - mips::Literal* literal = __ NewLiteral<uint32_t>(0x12345678); - __ LoadLiteral(mips::V0, mips::ZERO, literal); - constexpr size_t kAdduCount = 0x3FFDE; - for (size_t i = 0; i != kAdduCount; ++i) { - __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); + uint32_t offset_back = -(kNopCount2 + 1); // 1: account for bnezc. + offset_back <<= 2; + offset_back += (offset_back & 0x8000) << 1; // Account for sign extension in jic. + + // Note, we're using the ".fill" directive to tell the assembler to generate many NOPs + // instead of generating them ourselves in the source code. This saves test time. + std::ostringstream oss; + oss << + ".set noreorder\n" + "bnezc $a0, 1f\n" + "auipc $at, 0x" << std::hex << High16Bits(offset_forward) << "\n" + "jic $at, 0x" << std::hex << Low16Bits(offset_forward) << "\n" + "1:\n" << + ".fill 0x" << std::hex << kNopCount1 << " , 4, 0\n" + "2:\n" << + ".fill 0x" << std::hex << kNopCount2 << " , 4, 0\n" + "bnezc $a2, 3f\n" + "auipc $at, 0x" << std::hex << High16Bits(offset_back) << "\n" + "jic $at, 0x" << std::hex << Low16Bits(offset_back) << "\n" + "3:\n"; + std::string expected = oss.str(); + DriverStr(expected, "LongBeqzc"); +} + +TEST_F(AssemblerMIPS32r6Test, LongBc) { + constexpr uint32_t kNopCount1 = (1u << 25) + 1; + constexpr uint32_t kNopCount2 = (1u << 25) + 1; + constexpr uint32_t kRequiredCapacity = (kNopCount1 + kNopCount2 + 6u) * 4u; + ASSERT_LT(__ GetBuffer()->Capacity(), kRequiredCapacity); + __ GetBuffer()->ExtendCapacity(kRequiredCapacity); + mips::MipsLabel label1, label2; + __ Bc(&label1); + for (uint32_t i = 0; i != kNopCount1; ++i) { + __ Nop(); + } + __ Bind(&label1); + __ Bc(&label2); + for (uint32_t i = 0; i != kNopCount2; ++i) { + __ Nop(); } + __ Bind(&label2); + __ Bc(&label1); - std::string expected = - "lwpc $v0, 1f\n" + - RepeatInsn(kAdduCount, "addu $zero, $zero, $zero\n") + - "1:\n" - ".word 0x12345678\n"; - DriverStr(expected, "LoadFarthestNearLiteral"); -} + uint32_t offset_forward1 = 2 + kNopCount1; // 2: account for auipc and jic. + offset_forward1 <<= 2; + offset_forward1 += (offset_forward1 & 0x8000) << 1; // Account for sign extension in jic. -TEST_F(AssemblerMIPS32r6Test, LoadNearestFarLiteral) { - mips::Literal* literal = __ NewLiteral<uint32_t>(0x12345678); - __ LoadLiteral(mips::V0, mips::ZERO, literal); - constexpr size_t kAdduCount = 0x3FFDF; - for (size_t i = 0; i != kAdduCount; ++i) { - __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); - } + uint32_t offset_forward2 = 2 + kNopCount2; // 2: account for auipc and jic. + offset_forward2 <<= 2; + offset_forward2 += (offset_forward2 & 0x8000) << 1; // Account for sign extension in jic. - std::string expected = + uint32_t offset_back = -(2 + kNopCount2); // 2: account for auipc and jic. + offset_back <<= 2; + offset_back += (offset_back & 0x8000) << 1; // Account for sign extension in jic. + + // Note, we're using the ".fill" directive to tell the assembler to generate many NOPs + // instead of generating them ourselves in the source code. This saves a few minutes + // of test time. + std::ostringstream oss; + oss << + ".set noreorder\n" + "auipc $at, 0x" << std::hex << High16Bits(offset_forward1) << "\n" + "jic $at, 0x" << std::hex << Low16Bits(offset_forward1) << "\n" + ".fill 0x" << std::hex << kNopCount1 << " , 4, 0\n" "1:\n" - "auipc $at, %hi(2f - 1b)\n" - "lw $v0, %lo(2f - 1b)($at)\n" + - RepeatInsn(kAdduCount, "addu $zero, $zero, $zero\n") + + "auipc $at, 0x" << std::hex << High16Bits(offset_forward2) << "\n" + "jic $at, 0x" << std::hex << Low16Bits(offset_forward2) << "\n" + ".fill 0x" << std::hex << kNopCount2 << " , 4, 0\n" "2:\n" - ".word 0x12345678\n"; - DriverStr(expected, "LoadNearestFarLiteral"); + "auipc $at, 0x" << std::hex << High16Bits(offset_back) << "\n" + "jic $at, 0x" << std::hex << Low16Bits(offset_back) << "\n"; + std::string expected = oss.str(); + DriverStr(expected, "LongBc"); } -////////////// -// BRANCHES // -////////////// - TEST_F(AssemblerMIPS32r6Test, ImpossibleReordering) { mips::MipsLabel label; __ SetReorder(true); @@ -1154,43 +1571,80 @@ TEST_F(AssemblerMIPS32r6Test, LongBranchReorder) { "jic $at, 0x" << std::hex << Low16Bits(offset_back) << "\n" "3:\n"; std::string expected = oss.str(); - DriverStr(expected, "LongBeqc"); + DriverStr(expected, "LongBranchReorder"); } -// TODO: MipsAssembler::Bc -// MipsAssembler::Jic -// MipsAssembler::Jialc -// MipsAssembler::Bltc -// MipsAssembler::Bltzc -// MipsAssembler::Bgtzc -// MipsAssembler::Bgec -// MipsAssembler::Bgezc -// MipsAssembler::Blezc -// MipsAssembler::Bltuc -// MipsAssembler::Bgeuc -// MipsAssembler::Beqc -// MipsAssembler::Bnec -// MipsAssembler::Beqzc -// MipsAssembler::Bnezc -// MipsAssembler::Bc1eqz -// MipsAssembler::Bc1nez -// MipsAssembler::Buncond -// MipsAssembler::Bcond -// MipsAssembler::Call - -// TODO: AssemblerMIPS32r6Test.B -// AssemblerMIPS32r6Test.Beq -// AssemblerMIPS32r6Test.Bne -// AssemblerMIPS32r6Test.Beqz -// AssemblerMIPS32r6Test.Bnez -// AssemblerMIPS32r6Test.Bltz -// AssemblerMIPS32r6Test.Bgez -// AssemblerMIPS32r6Test.Blez -// AssemblerMIPS32r6Test.Bgtz -// AssemblerMIPS32r6Test.Blt -// AssemblerMIPS32r6Test.Bge -// AssemblerMIPS32r6Test.Bltu -// AssemblerMIPS32r6Test.Bgeu +/////////////////////// +// Loading Constants // +/////////////////////// + +TEST_F(AssemblerMIPS32r6Test, LoadFarthestNearLabelAddress) { + mips::MipsLabel label; + __ LoadLabelAddress(mips::V0, mips::ZERO, &label); + constexpr size_t kAdduCount = 0x3FFDE; + for (size_t i = 0; i != kAdduCount; ++i) { + __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); + } + __ Bind(&label); + + std::string expected = + "lapc $v0, 1f\n" + + RepeatInsn(kAdduCount, "addu $zero, $zero, $zero\n") + + "1:\n"; + DriverStr(expected, "LoadFarthestNearLabelAddress"); +} + +TEST_F(AssemblerMIPS32r6Test, LoadNearestFarLabelAddress) { + mips::MipsLabel label; + __ LoadLabelAddress(mips::V0, mips::ZERO, &label); + constexpr size_t kAdduCount = 0x3FFDF; + for (size_t i = 0; i != kAdduCount; ++i) { + __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); + } + __ Bind(&label); + + std::string expected = + "1:\n" + "auipc $at, %hi(2f - 1b)\n" + "addiu $v0, $at, %lo(2f - 1b)\n" + + RepeatInsn(kAdduCount, "addu $zero, $zero, $zero\n") + + "2:\n"; + DriverStr(expected, "LoadNearestFarLabelAddress"); +} + +TEST_F(AssemblerMIPS32r6Test, LoadFarthestNearLiteral) { + mips::Literal* literal = __ NewLiteral<uint32_t>(0x12345678); + __ LoadLiteral(mips::V0, mips::ZERO, literal); + constexpr size_t kAdduCount = 0x3FFDE; + for (size_t i = 0; i != kAdduCount; ++i) { + __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); + } + + std::string expected = + "lwpc $v0, 1f\n" + + RepeatInsn(kAdduCount, "addu $zero, $zero, $zero\n") + + "1:\n" + ".word 0x12345678\n"; + DriverStr(expected, "LoadFarthestNearLiteral"); +} + +TEST_F(AssemblerMIPS32r6Test, LoadNearestFarLiteral) { + mips::Literal* literal = __ NewLiteral<uint32_t>(0x12345678); + __ LoadLiteral(mips::V0, mips::ZERO, literal); + constexpr size_t kAdduCount = 0x3FFDF; + for (size_t i = 0; i != kAdduCount; ++i) { + __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); + } + + std::string expected = + "1:\n" + "auipc $at, %hi(2f - 1b)\n" + "lw $v0, %lo(2f - 1b)($at)\n" + + RepeatInsn(kAdduCount, "addu $zero, $zero, $zero\n") + + "2:\n" + ".word 0x12345678\n"; + DriverStr(expected, "LoadNearestFarLiteral"); +} // MSA instructions. diff --git a/compiler/utils/mips/assembler_mips_test.cc b/compiler/utils/mips/assembler_mips_test.cc index 09175309f9..d9bf0b82f4 100644 --- a/compiler/utils/mips/assembler_mips_test.cc +++ b/compiler/utils/mips/assembler_mips_test.cc @@ -186,11 +186,51 @@ class AssemblerMIPSTest : public AssemblerTest<mips::MipsAssembler, return result; } + void BranchHelper(void (mips::MipsAssembler::*f)(mips::MipsLabel*, + bool), + const std::string& instr_name, + bool is_bare = false) { + __ SetReorder(false); + mips::MipsLabel label1, label2; + (Base::GetAssembler()->*f)(&label1, is_bare); + constexpr size_t kAdduCount1 = 63; + for (size_t i = 0; i != kAdduCount1; ++i) { + __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); + } + __ Bind(&label1); + (Base::GetAssembler()->*f)(&label2, is_bare); + constexpr size_t kAdduCount2 = 64; + for (size_t i = 0; i != kAdduCount2; ++i) { + __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); + } + __ Bind(&label2); + (Base::GetAssembler()->*f)(&label1, is_bare); + __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); + + std::string expected = + ".set noreorder\n" + + instr_name + " 1f\n" + + (is_bare ? "" : "nop\n") + + RepeatInsn(kAdduCount1, "addu $zero, $zero, $zero\n") + + "1:\n" + + instr_name + " 2f\n" + + (is_bare ? "" : "nop\n") + + RepeatInsn(kAdduCount2, "addu $zero, $zero, $zero\n") + + "2:\n" + + instr_name + " 1b\n" + + (is_bare ? "" : "nop\n") + + "addu $zero, $zero, $zero\n"; + DriverStr(expected, instr_name); + } + void BranchCondOneRegHelper(void (mips::MipsAssembler::*f)(mips::Register, - mips::MipsLabel*), - const std::string& instr_name) { + mips::MipsLabel*, + bool), + const std::string& instr_name, + bool is_bare = false) { + __ SetReorder(false); mips::MipsLabel label; - (Base::GetAssembler()->*f)(mips::A0, &label); + (Base::GetAssembler()->*f)(mips::A0, &label, is_bare); constexpr size_t kAdduCount1 = 63; for (size_t i = 0; i != kAdduCount1; ++i) { __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); @@ -200,26 +240,64 @@ class AssemblerMIPSTest : public AssemblerTest<mips::MipsAssembler, for (size_t i = 0; i != kAdduCount2; ++i) { __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); } - (Base::GetAssembler()->*f)(mips::A1, &label); + (Base::GetAssembler()->*f)(mips::A1, &label, is_bare); + __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); std::string expected = ".set noreorder\n" + - instr_name + " $a0, 1f\n" - "nop\n" + + instr_name + " $a0, 1f\n" + + (is_bare ? "" : "nop\n") + RepeatInsn(kAdduCount1, "addu $zero, $zero, $zero\n") + "1:\n" + RepeatInsn(kAdduCount2, "addu $zero, $zero, $zero\n") + - instr_name + " $a1, 1b\n" - "nop\n"; + instr_name + " $a1, 1b\n" + + (is_bare ? "" : "nop\n") + + "addu $zero, $zero, $zero\n"; DriverStr(expected, instr_name); } void BranchCondTwoRegsHelper(void (mips::MipsAssembler::*f)(mips::Register, mips::Register, - mips::MipsLabel*), - const std::string& instr_name) { + mips::MipsLabel*, + bool), + const std::string& instr_name, + bool is_bare = false) { + __ SetReorder(false); + mips::MipsLabel label; + (Base::GetAssembler()->*f)(mips::A0, mips::A1, &label, is_bare); + constexpr size_t kAdduCount1 = 63; + for (size_t i = 0; i != kAdduCount1; ++i) { + __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); + } + __ Bind(&label); + constexpr size_t kAdduCount2 = 64; + for (size_t i = 0; i != kAdduCount2; ++i) { + __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); + } + (Base::GetAssembler()->*f)(mips::A2, mips::A3, &label, is_bare); + __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); + + std::string expected = + ".set noreorder\n" + + instr_name + " $a0, $a1, 1f\n" + + (is_bare ? "" : "nop\n") + + RepeatInsn(kAdduCount1, "addu $zero, $zero, $zero\n") + + "1:\n" + + RepeatInsn(kAdduCount2, "addu $zero, $zero, $zero\n") + + instr_name + " $a2, $a3, 1b\n" + + (is_bare ? "" : "nop\n") + + "addu $zero, $zero, $zero\n"; + DriverStr(expected, instr_name); + } + + void BranchFpuCondCodeHelper(void (mips::MipsAssembler::*f)(int, + mips::MipsLabel*, + bool), + const std::string& instr_name, + bool is_bare = false) { + __ SetReorder(false); mips::MipsLabel label; - (Base::GetAssembler()->*f)(mips::A0, mips::A1, &label); + (Base::GetAssembler()->*f)(0, &label, is_bare); constexpr size_t kAdduCount1 = 63; for (size_t i = 0; i != kAdduCount1; ++i) { __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); @@ -229,17 +307,19 @@ class AssemblerMIPSTest : public AssemblerTest<mips::MipsAssembler, for (size_t i = 0; i != kAdduCount2; ++i) { __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); } - (Base::GetAssembler()->*f)(mips::A2, mips::A3, &label); + (Base::GetAssembler()->*f)(7, &label, is_bare); + __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); std::string expected = ".set noreorder\n" + - instr_name + " $a0, $a1, 1f\n" - "nop\n" + + instr_name + " $fcc0, 1f\n" + + (is_bare ? "" : "nop\n") + RepeatInsn(kAdduCount1, "addu $zero, $zero, $zero\n") + "1:\n" + RepeatInsn(kAdduCount2, "addu $zero, $zero, $zero\n") + - instr_name + " $a2, $a3, 1b\n" - "nop\n"; + instr_name + " $fcc7, 1b\n" + + (is_bare ? "" : "nop\n") + + "addu $zero, $zero, $zero\n"; DriverStr(expected, instr_name); } @@ -2072,410 +2152,136 @@ TEST_F(AssemblerMIPSTest, StoreConstToOffset) { DriverStr(expected, "StoreConstToOffset"); } +////////////// +// BRANCHES // +////////////// + TEST_F(AssemblerMIPSTest, B) { - mips::MipsLabel label1, label2; - __ B(&label1); - constexpr size_t kAdduCount1 = 63; - for (size_t i = 0; i != kAdduCount1; ++i) { - __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); - } - __ Bind(&label1); - __ B(&label2); - constexpr size_t kAdduCount2 = 64; - for (size_t i = 0; i != kAdduCount2; ++i) { - __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); - } - __ Bind(&label2); - __ B(&label1); + BranchHelper(&mips::MipsAssembler::B, "B"); +} - std::string expected = - ".set noreorder\n" - "b 1f\n" - "nop\n" + - RepeatInsn(kAdduCount1, "addu $zero, $zero, $zero\n") + - "1:\n" - "b 2f\n" - "nop\n" + - RepeatInsn(kAdduCount2, "addu $zero, $zero, $zero\n") + - "2:\n" - "b 1b\n" - "nop\n"; - DriverStr(expected, "B"); +TEST_F(AssemblerMIPSTest, Bal) { + BranchHelper(&mips::MipsAssembler::Bal, "Bal"); } TEST_F(AssemblerMIPSTest, Beq) { - __ SetReorder(false); BranchCondTwoRegsHelper(&mips::MipsAssembler::Beq, "Beq"); } TEST_F(AssemblerMIPSTest, Bne) { - __ SetReorder(false); BranchCondTwoRegsHelper(&mips::MipsAssembler::Bne, "Bne"); } TEST_F(AssemblerMIPSTest, Beqz) { - __ SetReorder(false); - mips::MipsLabel label; - __ Beqz(mips::A0, &label); - constexpr size_t kAdduCount1 = 63; - for (size_t i = 0; i != kAdduCount1; ++i) { - __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); - } - __ Bind(&label); - constexpr size_t kAdduCount2 = 64; - for (size_t i = 0; i != kAdduCount2; ++i) { - __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); - } - __ Beqz(mips::A1, &label); - - std::string expected = - ".set noreorder\n" - "beq $zero, $a0, 1f\n" - "nop\n" + - RepeatInsn(kAdduCount1, "addu $zero, $zero, $zero\n") + - "1:\n" + - RepeatInsn(kAdduCount2, "addu $zero, $zero, $zero\n") + - "beq $zero, $a1, 1b\n" - "nop\n"; - DriverStr(expected, "Beqz"); + BranchCondOneRegHelper(&mips::MipsAssembler::Beqz, "Beqz"); } TEST_F(AssemblerMIPSTest, Bnez) { - __ SetReorder(false); - mips::MipsLabel label; - __ Bnez(mips::A0, &label); - constexpr size_t kAdduCount1 = 63; - for (size_t i = 0; i != kAdduCount1; ++i) { - __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); - } - __ Bind(&label); - constexpr size_t kAdduCount2 = 64; - for (size_t i = 0; i != kAdduCount2; ++i) { - __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); - } - __ Bnez(mips::A1, &label); - - std::string expected = - ".set noreorder\n" - "bne $zero, $a0, 1f\n" - "nop\n" + - RepeatInsn(kAdduCount1, "addu $zero, $zero, $zero\n") + - "1:\n" + - RepeatInsn(kAdduCount2, "addu $zero, $zero, $zero\n") + - "bne $zero, $a1, 1b\n" - "nop\n"; - DriverStr(expected, "Bnez"); + BranchCondOneRegHelper(&mips::MipsAssembler::Bnez, "Bnez"); } TEST_F(AssemblerMIPSTest, Bltz) { - __ SetReorder(false); BranchCondOneRegHelper(&mips::MipsAssembler::Bltz, "Bltz"); } TEST_F(AssemblerMIPSTest, Bgez) { - __ SetReorder(false); BranchCondOneRegHelper(&mips::MipsAssembler::Bgez, "Bgez"); } TEST_F(AssemblerMIPSTest, Blez) { - __ SetReorder(false); BranchCondOneRegHelper(&mips::MipsAssembler::Blez, "Blez"); } TEST_F(AssemblerMIPSTest, Bgtz) { - __ SetReorder(false); BranchCondOneRegHelper(&mips::MipsAssembler::Bgtz, "Bgtz"); } TEST_F(AssemblerMIPSTest, Blt) { - __ SetReorder(false); - mips::MipsLabel label; - __ Blt(mips::A0, mips::A1, &label); - constexpr size_t kAdduCount1 = 63; - for (size_t i = 0; i != kAdduCount1; ++i) { - __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); - } - __ Bind(&label); - constexpr size_t kAdduCount2 = 64; - for (size_t i = 0; i != kAdduCount2; ++i) { - __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); - } - __ Blt(mips::A2, mips::A3, &label); - - std::string expected = - ".set noreorder\n" - "slt $at, $a0, $a1\n" - "bne $zero, $at, 1f\n" - "nop\n" + - RepeatInsn(kAdduCount1, "addu $zero, $zero, $zero\n") + - "1:\n" + - RepeatInsn(kAdduCount2, "addu $zero, $zero, $zero\n") + - "slt $at, $a2, $a3\n" - "bne $zero, $at, 1b\n" - "nop\n"; - DriverStr(expected, "Blt"); + BranchCondTwoRegsHelper(&mips::MipsAssembler::Blt, "Blt"); } TEST_F(AssemblerMIPSTest, Bge) { - __ SetReorder(false); - mips::MipsLabel label; - __ Bge(mips::A0, mips::A1, &label); - constexpr size_t kAdduCount1 = 63; - for (size_t i = 0; i != kAdduCount1; ++i) { - __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); - } - __ Bind(&label); - constexpr size_t kAdduCount2 = 64; - for (size_t i = 0; i != kAdduCount2; ++i) { - __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); - } - __ Bge(mips::A2, mips::A3, &label); - - std::string expected = - ".set noreorder\n" - "slt $at, $a0, $a1\n" - "beq $zero, $at, 1f\n" - "nop\n" + - RepeatInsn(kAdduCount1, "addu $zero, $zero, $zero\n") + - "1:\n" + - RepeatInsn(kAdduCount2, "addu $zero, $zero, $zero\n") + - "slt $at, $a2, $a3\n" - "beq $zero, $at, 1b\n" - "nop\n"; - DriverStr(expected, "Bge"); + BranchCondTwoRegsHelper(&mips::MipsAssembler::Bge, "Bge"); } TEST_F(AssemblerMIPSTest, Bltu) { - __ SetReorder(false); - mips::MipsLabel label; - __ Bltu(mips::A0, mips::A1, &label); - constexpr size_t kAdduCount1 = 63; - for (size_t i = 0; i != kAdduCount1; ++i) { - __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); - } - __ Bind(&label); - constexpr size_t kAdduCount2 = 64; - for (size_t i = 0; i != kAdduCount2; ++i) { - __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); - } - __ Bltu(mips::A2, mips::A3, &label); - - std::string expected = - ".set noreorder\n" - "sltu $at, $a0, $a1\n" - "bne $zero, $at, 1f\n" - "nop\n" + - RepeatInsn(kAdduCount1, "addu $zero, $zero, $zero\n") + - "1:\n" + - RepeatInsn(kAdduCount2, "addu $zero, $zero, $zero\n") + - "sltu $at, $a2, $a3\n" - "bne $zero, $at, 1b\n" - "nop\n"; - DriverStr(expected, "Bltu"); + BranchCondTwoRegsHelper(&mips::MipsAssembler::Bltu, "Bltu"); } TEST_F(AssemblerMIPSTest, Bgeu) { - __ SetReorder(false); - mips::MipsLabel label; - __ Bgeu(mips::A0, mips::A1, &label); - constexpr size_t kAdduCount1 = 63; - for (size_t i = 0; i != kAdduCount1; ++i) { - __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); - } - __ Bind(&label); - constexpr size_t kAdduCount2 = 64; - for (size_t i = 0; i != kAdduCount2; ++i) { - __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); - } - __ Bgeu(mips::A2, mips::A3, &label); - - std::string expected = - ".set noreorder\n" - "sltu $at, $a0, $a1\n" - "beq $zero, $at, 1f\n" - "nop\n" + - RepeatInsn(kAdduCount1, "addu $zero, $zero, $zero\n") + - "1:\n" + - RepeatInsn(kAdduCount2, "addu $zero, $zero, $zero\n") + - "sltu $at, $a2, $a3\n" - "beq $zero, $at, 1b\n" - "nop\n"; - DriverStr(expected, "Bgeu"); + BranchCondTwoRegsHelper(&mips::MipsAssembler::Bgeu, "Bgeu"); } TEST_F(AssemblerMIPSTest, Bc1f) { - __ SetReorder(false); - mips::MipsLabel label; - __ Bc1f(0, &label); - constexpr size_t kAdduCount1 = 63; - for (size_t i = 0; i != kAdduCount1; ++i) { - __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); - } - __ Bind(&label); - constexpr size_t kAdduCount2 = 64; - for (size_t i = 0; i != kAdduCount2; ++i) { - __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); - } - __ Bc1f(7, &label); - - std::string expected = - ".set noreorder\n" - "bc1f $fcc0, 1f\n" - "nop\n" + - RepeatInsn(kAdduCount1, "addu $zero, $zero, $zero\n") + - "1:\n" + - RepeatInsn(kAdduCount2, "addu $zero, $zero, $zero\n") + - "bc1f $fcc7, 1b\n" - "nop\n"; - DriverStr(expected, "Bc1f"); + BranchFpuCondCodeHelper(&mips::MipsAssembler::Bc1f, "Bc1f"); } TEST_F(AssemblerMIPSTest, Bc1t) { - __ SetReorder(false); - mips::MipsLabel label; - __ Bc1t(0, &label); - constexpr size_t kAdduCount1 = 63; - for (size_t i = 0; i != kAdduCount1; ++i) { - __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); - } - __ Bind(&label); - constexpr size_t kAdduCount2 = 64; - for (size_t i = 0; i != kAdduCount2; ++i) { - __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); - } - __ Bc1t(7, &label); + BranchFpuCondCodeHelper(&mips::MipsAssembler::Bc1t, "Bc1t"); +} - std::string expected = - ".set noreorder\n" - "bc1t $fcc0, 1f\n" - "nop\n" + - RepeatInsn(kAdduCount1, "addu $zero, $zero, $zero\n") + - "1:\n" + - RepeatInsn(kAdduCount2, "addu $zero, $zero, $zero\n") + - "bc1t $fcc7, 1b\n" - "nop\n"; - DriverStr(expected, "Bc1t"); +TEST_F(AssemblerMIPSTest, BareB) { + BranchHelper(&mips::MipsAssembler::B, "B", /* is_bare */ true); } -/////////////////////// -// Loading Constants // -/////////////////////// +TEST_F(AssemblerMIPSTest, BareBal) { + BranchHelper(&mips::MipsAssembler::Bal, "Bal", /* is_bare */ true); +} -TEST_F(AssemblerMIPSTest, LoadConst32) { - // IsUint<16>(value) - __ LoadConst32(mips::V0, 0); - __ LoadConst32(mips::V0, 65535); - // IsInt<16>(value) - __ LoadConst32(mips::V0, -1); - __ LoadConst32(mips::V0, -32768); - // Everything else - __ LoadConst32(mips::V0, 65536); - __ LoadConst32(mips::V0, 65537); - __ LoadConst32(mips::V0, 2147483647); - __ LoadConst32(mips::V0, -32769); - __ LoadConst32(mips::V0, -65536); - __ LoadConst32(mips::V0, -65537); - __ LoadConst32(mips::V0, -2147483647); - __ LoadConst32(mips::V0, -2147483648); +TEST_F(AssemblerMIPSTest, BareBeq) { + BranchCondTwoRegsHelper(&mips::MipsAssembler::Beq, "Beq", /* is_bare */ true); +} - const char* expected = - // IsUint<16>(value) - "ori $v0, $zero, 0\n" // __ LoadConst32(mips::V0, 0); - "ori $v0, $zero, 65535\n" // __ LoadConst32(mips::V0, 65535); - // IsInt<16>(value) - "addiu $v0, $zero, -1\n" // __ LoadConst32(mips::V0, -1); - "addiu $v0, $zero, -32768\n" // __ LoadConst32(mips::V0, -32768); - // Everything else - "lui $v0, 1\n" // __ LoadConst32(mips::V0, 65536); - "lui $v0, 1\n" // __ LoadConst32(mips::V0, 65537); - "ori $v0, 1\n" // " - "lui $v0, 32767\n" // __ LoadConst32(mips::V0, 2147483647); - "ori $v0, 65535\n" // " - "lui $v0, 65535\n" // __ LoadConst32(mips::V0, -32769); - "ori $v0, 32767\n" // " - "lui $v0, 65535\n" // __ LoadConst32(mips::V0, -65536); - "lui $v0, 65534\n" // __ LoadConst32(mips::V0, -65537); - "ori $v0, 65535\n" // " - "lui $v0, 32768\n" // __ LoadConst32(mips::V0, -2147483647); - "ori $v0, 1\n" // " - "lui $v0, 32768\n"; // __ LoadConst32(mips::V0, -2147483648); - DriverStr(expected, "LoadConst32"); +TEST_F(AssemblerMIPSTest, BareBne) { + BranchCondTwoRegsHelper(&mips::MipsAssembler::Bne, "Bne", /* is_bare */ true); } -TEST_F(AssemblerMIPSTest, LoadFarthestNearLabelAddress) { - mips::MipsLabel label; - __ BindPcRelBaseLabel(); - __ LoadLabelAddress(mips::V0, mips::V1, &label); - constexpr size_t kAddiuCount = 0x1FDE; - for (size_t i = 0; i != kAddiuCount; ++i) { - __ Addiu(mips::A0, mips::A1, 0); - } - __ Bind(&label); +TEST_F(AssemblerMIPSTest, BareBeqz) { + BranchCondOneRegHelper(&mips::MipsAssembler::Beqz, "Beqz", /* is_bare */ true); +} - std::string expected = - "1:\n" - "addiu $v0, $v1, %lo(2f - 1b)\n" + - RepeatInsn(kAddiuCount, "addiu $a0, $a1, %hi(2f - 1b)\n") + - "2:\n"; - DriverStr(expected, "LoadFarthestNearLabelAddress"); +TEST_F(AssemblerMIPSTest, BareBnez) { + BranchCondOneRegHelper(&mips::MipsAssembler::Bnez, "Bnez", /* is_bare */ true); } -TEST_F(AssemblerMIPSTest, LoadNearestFarLabelAddress) { - mips::MipsLabel label; - __ BindPcRelBaseLabel(); - __ LoadLabelAddress(mips::V0, mips::V1, &label); - constexpr size_t kAdduCount = 0x1FDF; - for (size_t i = 0; i != kAdduCount; ++i) { - __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); - } - __ Bind(&label); +TEST_F(AssemblerMIPSTest, BareBltz) { + BranchCondOneRegHelper(&mips::MipsAssembler::Bltz, "Bltz", /* is_bare */ true); +} - std::string expected = - "1:\n" - "lui $at, %hi(2f - 1b)\n" - "ori $at, $at, %lo(2f - 1b)\n" - "addu $v0, $at, $v1\n" + - RepeatInsn(kAdduCount, "addu $zero, $zero, $zero\n") + - "2:\n"; - DriverStr(expected, "LoadNearestFarLabelAddress"); +TEST_F(AssemblerMIPSTest, BareBgez) { + BranchCondOneRegHelper(&mips::MipsAssembler::Bgez, "Bgez", /* is_bare */ true); } -TEST_F(AssemblerMIPSTest, LoadFarthestNearLiteral) { - mips::Literal* literal = __ NewLiteral<uint32_t>(0x12345678); - __ BindPcRelBaseLabel(); - __ LoadLiteral(mips::V0, mips::V1, literal); - constexpr size_t kAddiuCount = 0x1FDE; - for (size_t i = 0; i != kAddiuCount; ++i) { - __ Addiu(mips::A0, mips::A1, 0); - } +TEST_F(AssemblerMIPSTest, BareBlez) { + BranchCondOneRegHelper(&mips::MipsAssembler::Blez, "Blez", /* is_bare */ true); +} - std::string expected = - "1:\n" - "lw $v0, %lo(2f - 1b)($v1)\n" + - RepeatInsn(kAddiuCount, "addiu $a0, $a1, %hi(2f - 1b)\n") + - "2:\n" - ".word 0x12345678\n"; - DriverStr(expected, "LoadFarthestNearLiteral"); +TEST_F(AssemblerMIPSTest, BareBgtz) { + BranchCondOneRegHelper(&mips::MipsAssembler::Bgtz, "Bgtz", /* is_bare */ true); } -TEST_F(AssemblerMIPSTest, LoadNearestFarLiteral) { - mips::Literal* literal = __ NewLiteral<uint32_t>(0x12345678); - __ BindPcRelBaseLabel(); - __ LoadLiteral(mips::V0, mips::V1, literal); - constexpr size_t kAdduCount = 0x1FDF; - for (size_t i = 0; i != kAdduCount; ++i) { - __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); - } +TEST_F(AssemblerMIPSTest, BareBlt) { + BranchCondTwoRegsHelper(&mips::MipsAssembler::Blt, "Blt", /* is_bare */ true); +} - std::string expected = - "1:\n" - "lui $at, %hi(2f - 1b)\n" - "addu $at, $at, $v1\n" - "lw $v0, %lo(2f - 1b)($at)\n" + - RepeatInsn(kAdduCount, "addu $zero, $zero, $zero\n") + - "2:\n" - ".word 0x12345678\n"; - DriverStr(expected, "LoadNearestFarLiteral"); +TEST_F(AssemblerMIPSTest, BareBge) { + BranchCondTwoRegsHelper(&mips::MipsAssembler::Bge, "Bge", /* is_bare */ true); +} + +TEST_F(AssemblerMIPSTest, BareBltu) { + BranchCondTwoRegsHelper(&mips::MipsAssembler::Bltu, "Bltu", /* is_bare */ true); +} + +TEST_F(AssemblerMIPSTest, BareBgeu) { + BranchCondTwoRegsHelper(&mips::MipsAssembler::Bgeu, "Bgeu", /* is_bare */ true); +} + +TEST_F(AssemblerMIPSTest, BareBc1f) { + BranchFpuCondCodeHelper(&mips::MipsAssembler::Bc1f, "Bc1f", /* is_bare */ true); +} + +TEST_F(AssemblerMIPSTest, BareBc1t) { + BranchFpuCondCodeHelper(&mips::MipsAssembler::Bc1t, "Bc1t", /* is_bare */ true); } TEST_F(AssemblerMIPSTest, ImpossibleReordering) { @@ -2554,7 +2360,7 @@ TEST_F(AssemblerMIPSTest, ImpossibleReordering) { "nop\n" "addu $t0, $t1, $t2\n" - "beq $zero, $t0, 1b\n" + "beqz $t0, 1b\n" "nop\n" "or $t1, $t2, $t3\n" @@ -2563,17 +2369,17 @@ TEST_F(AssemblerMIPSTest, ImpossibleReordering) { "and $t0, $t1, $t2\n" "slt $at, $t1, $t0\n" - "bne $zero, $at, 1b\n" + "bnez $at, 1b\n" "nop\n" "xor $at, $t0, $t1\n" "slt $at, $t1, $t0\n" - "beq $zero, $at, 1b\n" + "beqz $at, 1b\n" "nop\n" "subu $t0, $t1, $at\n" "sltu $at, $t1, $t0\n" - "bne $zero, $at, 1b\n" + "bnez $at, 1b\n" "nop\n" "c.olt.s $fcc1, $f2, $f4\n" @@ -2606,11 +2412,11 @@ TEST_F(AssemblerMIPSTest, ImpossibleReordering) { "2:\n" - "bne $zero, $t0, 2b\n" + "bnez $t0, 2b\n" "nop\n" "sltu $at, $t1, $t0\n" - "beq $zero, $at, 2b\n" + "beqz $at, 2b\n" "nop\n" "bc1f $fcc2, 2b\n" @@ -2666,22 +2472,22 @@ TEST_F(AssemblerMIPSTest, Reordering) { ".set noreorder\n" "1:\n" - "beq $zero, $t1, 1b\n" + "beqz $t1, 1b\n" "addu $t0, $t1, $t2\n" "bne $t2, $t3, 1b\n" "or $t1, $t2, $t3\n" "slt $at, $t1, $t2\n" - "bne $zero, $at, 1b\n" + "bnez $at, 1b\n" "and $t0, $t1, $t2\n" "slt $at, $t1, $t0\n" - "beq $zero, $at, 1b\n" + "beqz $at, 1b\n" "xor $t2, $t0, $t1\n" "sltu $at, $t1, $t0\n" - "bne $zero, $at, 1b\n" + "bnez $at, 1b\n" "subu $t2, $t1, $t0\n" "bc1t $fcc1, 1b\n" @@ -2882,6 +2688,127 @@ TEST_F(AssemblerMIPSTest, LongBranchReorder) { DriverStr(expected, "LongBranchReorder"); } +/////////////////////// +// Loading Constants // +/////////////////////// + +TEST_F(AssemblerMIPSTest, LoadConst32) { + // IsUint<16>(value) + __ LoadConst32(mips::V0, 0); + __ LoadConst32(mips::V0, 65535); + // IsInt<16>(value) + __ LoadConst32(mips::V0, -1); + __ LoadConst32(mips::V0, -32768); + // Everything else + __ LoadConst32(mips::V0, 65536); + __ LoadConst32(mips::V0, 65537); + __ LoadConst32(mips::V0, 2147483647); + __ LoadConst32(mips::V0, -32769); + __ LoadConst32(mips::V0, -65536); + __ LoadConst32(mips::V0, -65537); + __ LoadConst32(mips::V0, -2147483647); + __ LoadConst32(mips::V0, -2147483648); + + const char* expected = + // IsUint<16>(value) + "ori $v0, $zero, 0\n" // __ LoadConst32(mips::V0, 0); + "ori $v0, $zero, 65535\n" // __ LoadConst32(mips::V0, 65535); + // IsInt<16>(value) + "addiu $v0, $zero, -1\n" // __ LoadConst32(mips::V0, -1); + "addiu $v0, $zero, -32768\n" // __ LoadConst32(mips::V0, -32768); + // Everything else + "lui $v0, 1\n" // __ LoadConst32(mips::V0, 65536); + "lui $v0, 1\n" // __ LoadConst32(mips::V0, 65537); + "ori $v0, 1\n" // " + "lui $v0, 32767\n" // __ LoadConst32(mips::V0, 2147483647); + "ori $v0, 65535\n" // " + "lui $v0, 65535\n" // __ LoadConst32(mips::V0, -32769); + "ori $v0, 32767\n" // " + "lui $v0, 65535\n" // __ LoadConst32(mips::V0, -65536); + "lui $v0, 65534\n" // __ LoadConst32(mips::V0, -65537); + "ori $v0, 65535\n" // " + "lui $v0, 32768\n" // __ LoadConst32(mips::V0, -2147483647); + "ori $v0, 1\n" // " + "lui $v0, 32768\n"; // __ LoadConst32(mips::V0, -2147483648); + DriverStr(expected, "LoadConst32"); +} + +TEST_F(AssemblerMIPSTest, LoadFarthestNearLabelAddress) { + mips::MipsLabel label; + __ BindPcRelBaseLabel(); + __ LoadLabelAddress(mips::V0, mips::V1, &label); + constexpr size_t kAddiuCount = 0x1FDE; + for (size_t i = 0; i != kAddiuCount; ++i) { + __ Addiu(mips::A0, mips::A1, 0); + } + __ Bind(&label); + + std::string expected = + "1:\n" + "addiu $v0, $v1, %lo(2f - 1b)\n" + + RepeatInsn(kAddiuCount, "addiu $a0, $a1, %hi(2f - 1b)\n") + + "2:\n"; + DriverStr(expected, "LoadFarthestNearLabelAddress"); +} + +TEST_F(AssemblerMIPSTest, LoadNearestFarLabelAddress) { + mips::MipsLabel label; + __ BindPcRelBaseLabel(); + __ LoadLabelAddress(mips::V0, mips::V1, &label); + constexpr size_t kAdduCount = 0x1FDF; + for (size_t i = 0; i != kAdduCount; ++i) { + __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); + } + __ Bind(&label); + + std::string expected = + "1:\n" + "lui $at, %hi(2f - 1b)\n" + "ori $at, $at, %lo(2f - 1b)\n" + "addu $v0, $at, $v1\n" + + RepeatInsn(kAdduCount, "addu $zero, $zero, $zero\n") + + "2:\n"; + DriverStr(expected, "LoadNearestFarLabelAddress"); +} + +TEST_F(AssemblerMIPSTest, LoadFarthestNearLiteral) { + mips::Literal* literal = __ NewLiteral<uint32_t>(0x12345678); + __ BindPcRelBaseLabel(); + __ LoadLiteral(mips::V0, mips::V1, literal); + constexpr size_t kAddiuCount = 0x1FDE; + for (size_t i = 0; i != kAddiuCount; ++i) { + __ Addiu(mips::A0, mips::A1, 0); + } + + std::string expected = + "1:\n" + "lw $v0, %lo(2f - 1b)($v1)\n" + + RepeatInsn(kAddiuCount, "addiu $a0, $a1, %hi(2f - 1b)\n") + + "2:\n" + ".word 0x12345678\n"; + DriverStr(expected, "LoadFarthestNearLiteral"); +} + +TEST_F(AssemblerMIPSTest, LoadNearestFarLiteral) { + mips::Literal* literal = __ NewLiteral<uint32_t>(0x12345678); + __ BindPcRelBaseLabel(); + __ LoadLiteral(mips::V0, mips::V1, literal); + constexpr size_t kAdduCount = 0x1FDF; + for (size_t i = 0; i != kAdduCount; ++i) { + __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); + } + + std::string expected = + "1:\n" + "lui $at, %hi(2f - 1b)\n" + "addu $at, $at, $v1\n" + "lw $v0, %lo(2f - 1b)($at)\n" + + RepeatInsn(kAdduCount, "addu $zero, $zero, $zero\n") + + "2:\n" + ".word 0x12345678\n"; + DriverStr(expected, "LoadNearestFarLiteral"); +} + #undef __ } // namespace art diff --git a/compiler/utils/mips64/assembler_mips64.cc b/compiler/utils/mips64/assembler_mips64.cc index 7a1beb656b..3aa09fbdb6 100644 --- a/compiler/utils/mips64/assembler_mips64.cc +++ b/compiler/utils/mips64/assembler_mips64.cc @@ -795,14 +795,42 @@ void Mips64Assembler::Bc1nez(FpuRegister ft, uint16_t imm16) { EmitFI(0x11, 0xD, ft, imm16); } +void Mips64Assembler::Beq(GpuRegister rs, GpuRegister rt, uint16_t imm16) { + EmitI(0x4, rs, rt, imm16); +} + +void Mips64Assembler::Bne(GpuRegister rs, GpuRegister rt, uint16_t imm16) { + EmitI(0x5, rs, rt, imm16); +} + void Mips64Assembler::Beqz(GpuRegister rt, uint16_t imm16) { - EmitI(0x4, ZERO, rt, imm16); + Beq(rt, ZERO, imm16); +} + +void Mips64Assembler::Bnez(GpuRegister rt, uint16_t imm16) { + Bne(rt, ZERO, imm16); +} + +void Mips64Assembler::Bltz(GpuRegister rt, uint16_t imm16) { + EmitI(0x1, rt, static_cast<GpuRegister>(0), imm16); +} + +void Mips64Assembler::Bgez(GpuRegister rt, uint16_t imm16) { + EmitI(0x1, rt, static_cast<GpuRegister>(0x1), imm16); +} + +void Mips64Assembler::Blez(GpuRegister rt, uint16_t imm16) { + EmitI(0x6, rt, static_cast<GpuRegister>(0), imm16); +} + +void Mips64Assembler::Bgtz(GpuRegister rt, uint16_t imm16) { + EmitI(0x7, rt, static_cast<GpuRegister>(0), imm16); } -void Mips64Assembler::EmitBcondc(BranchCondition cond, - GpuRegister rs, - GpuRegister rt, - uint32_t imm16_21) { +void Mips64Assembler::EmitBcondR6(BranchCondition cond, + GpuRegister rs, + GpuRegister rt, + uint32_t imm16_21) { switch (cond) { case kCondLT: Bltc(rs, rt, imm16_21); @@ -866,6 +894,55 @@ void Mips64Assembler::EmitBcondc(BranchCondition cond, } } +void Mips64Assembler::EmitBcondR2(BranchCondition cond, + GpuRegister rs, + GpuRegister rt, + uint16_t imm16) { + switch (cond) { + case kCondLTZ: + CHECK_EQ(rt, ZERO); + Bltz(rs, imm16); + break; + case kCondGEZ: + CHECK_EQ(rt, ZERO); + Bgez(rs, imm16); + break; + case kCondLEZ: + CHECK_EQ(rt, ZERO); + Blez(rs, imm16); + break; + case kCondGTZ: + CHECK_EQ(rt, ZERO); + Bgtz(rs, imm16); + break; + case kCondEQ: + Beq(rs, rt, imm16); + break; + case kCondNE: + Bne(rs, rt, imm16); + break; + case kCondEQZ: + CHECK_EQ(rt, ZERO); + Beqz(rs, imm16); + break; + case kCondNEZ: + CHECK_EQ(rt, ZERO); + Bnez(rs, imm16); + break; + case kCondF: + case kCondT: + case kCondLT: + case kCondGE: + case kCondLE: + case kCondGT: + case kCondLTU: + case kCondGEU: + case kUncond: + LOG(FATAL) << "Unexpected branch condition " << cond; + UNREACHABLE(); + } +} + void Mips64Assembler::AddS(FpuRegister fd, FpuRegister fs, FpuRegister ft) { EmitFR(0x11, 0x10, ft, fs, fd, 0x0); } @@ -2013,37 +2090,67 @@ void Mips64Assembler::Branch::InitShortOrLong(Mips64Assembler::Branch::OffsetBit type_ = (offset_size <= branch_info_[short_type].offset_size) ? short_type : long_type; } -void Mips64Assembler::Branch::InitializeType(Type initial_type) { - OffsetBits offset_size = GetOffsetSizeNeeded(location_, target_); - switch (initial_type) { - case kLabel: - case kLiteral: - case kLiteralUnsigned: - case kLiteralLong: - CHECK(!IsResolved()); - type_ = initial_type; - break; - case kCall: - InitShortOrLong(offset_size, kCall, kLongCall); - break; - case kCondBranch: - switch (condition_) { - case kUncond: - InitShortOrLong(offset_size, kUncondBranch, kLongUncondBranch); - break; - case kCondEQZ: - case kCondNEZ: - // Special case for beqzc/bnezc with longer offset than in other b<cond>c instructions. - type_ = (offset_size <= kOffset23) ? kCondBranch : kLongCondBranch; - break; - default: - InitShortOrLong(offset_size, kCondBranch, kLongCondBranch); - break; - } - break; - default: - LOG(FATAL) << "Unexpected branch type " << initial_type; - UNREACHABLE(); +void Mips64Assembler::Branch::InitializeType(Type initial_type, bool is_r6) { + OffsetBits offset_size_needed = GetOffsetSizeNeeded(location_, target_); + if (is_r6) { + // R6 + switch (initial_type) { + case kLabel: + case kLiteral: + case kLiteralUnsigned: + case kLiteralLong: + CHECK(!IsResolved()); + type_ = initial_type; + break; + case kCall: + InitShortOrLong(offset_size_needed, kCall, kLongCall); + break; + case kCondBranch: + switch (condition_) { + case kUncond: + InitShortOrLong(offset_size_needed, kUncondBranch, kLongUncondBranch); + break; + case kCondEQZ: + case kCondNEZ: + // Special case for beqzc/bnezc with longer offset than in other b<cond>c instructions. + type_ = (offset_size_needed <= kOffset23) ? kCondBranch : kLongCondBranch; + break; + default: + InitShortOrLong(offset_size_needed, kCondBranch, kLongCondBranch); + break; + } + break; + case kBareCall: + type_ = kBareCall; + CHECK_LE(offset_size_needed, GetOffsetSize()); + break; + case kBareCondBranch: + type_ = (condition_ == kUncond) ? kBareUncondBranch : kBareCondBranch; + CHECK_LE(offset_size_needed, GetOffsetSize()); + break; + default: + LOG(FATAL) << "Unexpected branch type " << initial_type; + UNREACHABLE(); + } + } else { + // R2 + CHECK_EQ(initial_type, kBareCondBranch); + switch (condition_) { + case kCondLTZ: + case kCondGEZ: + case kCondLEZ: + case kCondGTZ: + case kCondEQ: + case kCondNE: + case kCondEQZ: + case kCondNEZ: + break; + default: + LOG(FATAL) << "Unexpected R2 branch condition " << condition_; + UNREACHABLE(); + } + type_ = kR2BareCondBranch; + CHECK_LE(offset_size_needed, GetOffsetSize()); } old_type_ = type_; } @@ -2076,21 +2183,25 @@ bool Mips64Assembler::Branch::IsUncond(BranchCondition condition, } } -Mips64Assembler::Branch::Branch(uint32_t location, uint32_t target, bool is_call) +Mips64Assembler::Branch::Branch(uint32_t location, uint32_t target, bool is_call, bool is_bare) : old_location_(location), location_(location), target_(target), lhs_reg_(ZERO), rhs_reg_(ZERO), condition_(kUncond) { - InitializeType(is_call ? kCall : kCondBranch); + InitializeType( + (is_call ? (is_bare ? kBareCall : kCall) : (is_bare ? kBareCondBranch : kCondBranch)), + /* is_r6 */ true); } -Mips64Assembler::Branch::Branch(uint32_t location, +Mips64Assembler::Branch::Branch(bool is_r6, + uint32_t location, uint32_t target, Mips64Assembler::BranchCondition condition, GpuRegister lhs_reg, - GpuRegister rhs_reg) + GpuRegister rhs_reg, + bool is_bare) : old_location_(location), location_(location), target_(target), @@ -2131,7 +2242,7 @@ Mips64Assembler::Branch::Branch(uint32_t location, // Branch condition is always true, make the branch unconditional. condition_ = kUncond; } - InitializeType(kCondBranch); + InitializeType((is_bare ? kBareCondBranch : kCondBranch), is_r6); } Mips64Assembler::Branch::Branch(uint32_t location, GpuRegister dest_reg, Type label_or_literal_type) @@ -2142,7 +2253,7 @@ Mips64Assembler::Branch::Branch(uint32_t location, GpuRegister dest_reg, Type la rhs_reg_(ZERO), condition_(kUncond) { CHECK_NE(dest_reg, ZERO); - InitializeType(label_or_literal_type); + InitializeType(label_or_literal_type, /* is_r6 */ true); } Mips64Assembler::BranchCondition Mips64Assembler::Branch::OppositeCondition( @@ -2238,12 +2349,32 @@ uint32_t Mips64Assembler::Branch::GetOldEndLocation() const { return GetOldLocation() + GetOldSize(); } +bool Mips64Assembler::Branch::IsBare() const { + switch (type_) { + // R6 short branches (can't be promoted to long), forbidden/delay slots filled manually. + case kBareUncondBranch: + case kBareCondBranch: + case kBareCall: + // R2 short branches (can't be promoted to long), delay slots filled manually. + case kR2BareCondBranch: + return true; + default: + return false; + } +} + bool Mips64Assembler::Branch::IsLong() const { switch (type_) { - // Short branches. + // R6 short branches (can be promoted to long). case kUncondBranch: case kCondBranch: case kCall: + // R6 short branches (can't be promoted to long), forbidden/delay slots filled manually. + case kBareUncondBranch: + case kBareCondBranch: + case kBareCall: + // R2 short branches (can't be promoted to long), delay slots filled manually. + case kR2BareCondBranch: // Near label. case kLabel: // Near literals. @@ -2271,8 +2402,9 @@ bool Mips64Assembler::Branch::IsResolved() const { } Mips64Assembler::Branch::OffsetBits Mips64Assembler::Branch::GetOffsetSize() const { + bool r6_cond_branch = (type_ == kCondBranch || type_ == kBareCondBranch); OffsetBits offset_size = - (type_ == kCondBranch && (condition_ == kCondEQZ || condition_ == kCondNEZ)) + (r6_cond_branch && (condition_ == kCondEQZ || condition_ == kCondNEZ)) ? kOffset23 : branch_info_[type_].offset_size; return offset_size; @@ -2318,8 +2450,9 @@ void Mips64Assembler::Branch::Relocate(uint32_t expand_location, uint32_t delta) } void Mips64Assembler::Branch::PromoteToLong() { + CHECK(!IsBare()); // Bare branches do not promote. switch (type_) { - // Short branches. + // R6 short branches (can be promoted to long). case kUncondBranch: type_ = kLongUncondBranch; break; @@ -2366,7 +2499,7 @@ uint32_t Mips64Assembler::Branch::PromoteIfNeeded(uint32_t max_short_distance) { } // The following logic is for debugging/testing purposes. // Promote some short branches to long when it's not really required. - if (UNLIKELY(max_short_distance != std::numeric_limits<uint32_t>::max())) { + if (UNLIKELY(max_short_distance != std::numeric_limits<uint32_t>::max() && !IsBare())) { int64_t distance = static_cast<int64_t>(target_) - location_; distance = (distance >= 0) ? distance : -distance; if (distance >= max_short_distance) { @@ -2498,13 +2631,15 @@ void Mips64Assembler::FinalizeLabeledBranch(Mips64Label* label) { } } -void Mips64Assembler::Buncond(Mips64Label* label) { +void Mips64Assembler::Buncond(Mips64Label* label, bool is_bare) { uint32_t target = label->IsBound() ? GetLabelLocation(label) : Branch::kUnresolved; - branches_.emplace_back(buffer_.Size(), target, /* is_call */ false); + branches_.emplace_back(buffer_.Size(), target, /* is_call */ false, is_bare); FinalizeLabeledBranch(label); } void Mips64Assembler::Bcond(Mips64Label* label, + bool is_r6, + bool is_bare, BranchCondition condition, GpuRegister lhs, GpuRegister rhs) { @@ -2513,13 +2648,13 @@ void Mips64Assembler::Bcond(Mips64Label* label, return; } uint32_t target = label->IsBound() ? GetLabelLocation(label) : Branch::kUnresolved; - branches_.emplace_back(buffer_.Size(), target, condition, lhs, rhs); + branches_.emplace_back(is_r6, buffer_.Size(), target, condition, lhs, rhs, is_bare); FinalizeLabeledBranch(label); } -void Mips64Assembler::Call(Mips64Label* label) { +void Mips64Assembler::Call(Mips64Label* label, bool is_bare) { uint32_t target = label->IsBound() ? GetLabelLocation(label) : Branch::kUnresolved; - branches_.emplace_back(buffer_.Size(), target, /* is_call */ true); + branches_.emplace_back(buffer_.Size(), target, /* is_call */ true, is_bare); FinalizeLabeledBranch(label); } @@ -2730,11 +2865,18 @@ void Mips64Assembler::PromoteBranches() { // Note: make sure branch_info_[] and EmitBranch() are kept synchronized. const Mips64Assembler::Branch::BranchInfo Mips64Assembler::Branch::branch_info_[] = { - // Short branches. + // R6 short branches (can be promoted to long). { 1, 0, 1, Mips64Assembler::Branch::kOffset28, 2 }, // kUncondBranch { 2, 0, 1, Mips64Assembler::Branch::kOffset18, 2 }, // kCondBranch // Exception: kOffset23 for beqzc/bnezc { 1, 0, 1, Mips64Assembler::Branch::kOffset28, 2 }, // kCall + // R6 short branches (can't be promoted to long), forbidden/delay slots filled manually. + { 1, 0, 1, Mips64Assembler::Branch::kOffset28, 2 }, // kBareUncondBranch + { 1, 0, 1, Mips64Assembler::Branch::kOffset18, 2 }, // kBareCondBranch + // Exception: kOffset23 for beqzc/bnezc + { 1, 0, 1, Mips64Assembler::Branch::kOffset28, 2 }, // kBareCall + // R2 short branches (can't be promoted to long), delay slots filled manually. + { 1, 0, 1, Mips64Assembler::Branch::kOffset18, 2 }, // kR2BareCondBranch // Near label. { 1, 0, 0, Mips64Assembler::Branch::kOffset21, 2 }, // kLabel // Near literals. @@ -2769,13 +2911,29 @@ void Mips64Assembler::EmitBranch(Mips64Assembler::Branch* branch) { break; case Branch::kCondBranch: CHECK_EQ(overwrite_location_, branch->GetOffsetLocation()); - EmitBcondc(condition, lhs, rhs, offset); + EmitBcondR6(condition, lhs, rhs, offset); Nop(); // TODO: improve by filling the forbidden/delay slot. break; case Branch::kCall: CHECK_EQ(overwrite_location_, branch->GetOffsetLocation()); Balc(offset); break; + case Branch::kBareUncondBranch: + CHECK_EQ(overwrite_location_, branch->GetOffsetLocation()); + Bc(offset); + break; + case Branch::kBareCondBranch: + CHECK_EQ(overwrite_location_, branch->GetOffsetLocation()); + EmitBcondR6(condition, lhs, rhs, offset); + break; + case Branch::kBareCall: + CHECK_EQ(overwrite_location_, branch->GetOffsetLocation()); + Balc(offset); + break; + case Branch::kR2BareCondBranch: + CHECK_EQ(overwrite_location_, branch->GetOffsetLocation()); + EmitBcondR2(condition, lhs, rhs, offset); + break; // Near label. case Branch::kLabel: @@ -2804,7 +2962,7 @@ void Mips64Assembler::EmitBranch(Mips64Assembler::Branch* branch) { Jic(AT, Low16Bits(offset)); break; case Branch::kLongCondBranch: - EmitBcondc(Branch::OppositeCondition(condition), lhs, rhs, 2); + EmitBcondR6(Branch::OppositeCondition(condition), lhs, rhs, 2); offset += (offset & 0x8000) << 1; // Account for sign extension in jic. CHECK_EQ(overwrite_location_, branch->GetOffsetLocation()); Auipc(AT, High16Bits(offset)); @@ -2848,68 +3006,108 @@ void Mips64Assembler::EmitBranch(Mips64Assembler::Branch* branch) { CHECK_LT(branch->GetSize(), static_cast<uint32_t>(Branch::kMaxBranchSize)); } -void Mips64Assembler::Bc(Mips64Label* label) { - Buncond(label); +void Mips64Assembler::Bc(Mips64Label* label, bool is_bare) { + Buncond(label, is_bare); +} + +void Mips64Assembler::Balc(Mips64Label* label, bool is_bare) { + Call(label, is_bare); +} + +void Mips64Assembler::Bltc(GpuRegister rs, GpuRegister rt, Mips64Label* label, bool is_bare) { + Bcond(label, /* is_r6 */ true, is_bare, kCondLT, rs, rt); +} + +void Mips64Assembler::Bltzc(GpuRegister rt, Mips64Label* label, bool is_bare) { + Bcond(label, /* is_r6 */ true, is_bare, kCondLTZ, rt); +} + +void Mips64Assembler::Bgtzc(GpuRegister rt, Mips64Label* label, bool is_bare) { + Bcond(label, /* is_r6 */ true, is_bare, kCondGTZ, rt); +} + +void Mips64Assembler::Bgec(GpuRegister rs, GpuRegister rt, Mips64Label* label, bool is_bare) { + Bcond(label, /* is_r6 */ true, is_bare, kCondGE, rs, rt); +} + +void Mips64Assembler::Bgezc(GpuRegister rt, Mips64Label* label, bool is_bare) { + Bcond(label, /* is_r6 */ true, is_bare, kCondGEZ, rt); +} + +void Mips64Assembler::Blezc(GpuRegister rt, Mips64Label* label, bool is_bare) { + Bcond(label, /* is_r6 */ true, is_bare, kCondLEZ, rt); +} + +void Mips64Assembler::Bltuc(GpuRegister rs, GpuRegister rt, Mips64Label* label, bool is_bare) { + Bcond(label, /* is_r6 */ true, is_bare, kCondLTU, rs, rt); } -void Mips64Assembler::Balc(Mips64Label* label) { - Call(label); +void Mips64Assembler::Bgeuc(GpuRegister rs, GpuRegister rt, Mips64Label* label, bool is_bare) { + Bcond(label, /* is_r6 */ true, is_bare, kCondGEU, rs, rt); } -void Mips64Assembler::Bltc(GpuRegister rs, GpuRegister rt, Mips64Label* label) { - Bcond(label, kCondLT, rs, rt); +void Mips64Assembler::Beqc(GpuRegister rs, GpuRegister rt, Mips64Label* label, bool is_bare) { + Bcond(label, /* is_r6 */ true, is_bare, kCondEQ, rs, rt); } -void Mips64Assembler::Bltzc(GpuRegister rt, Mips64Label* label) { - Bcond(label, kCondLTZ, rt); +void Mips64Assembler::Bnec(GpuRegister rs, GpuRegister rt, Mips64Label* label, bool is_bare) { + Bcond(label, /* is_r6 */ true, is_bare, kCondNE, rs, rt); } -void Mips64Assembler::Bgtzc(GpuRegister rt, Mips64Label* label) { - Bcond(label, kCondGTZ, rt); +void Mips64Assembler::Beqzc(GpuRegister rs, Mips64Label* label, bool is_bare) { + Bcond(label, /* is_r6 */ true, is_bare, kCondEQZ, rs); } -void Mips64Assembler::Bgec(GpuRegister rs, GpuRegister rt, Mips64Label* label) { - Bcond(label, kCondGE, rs, rt); +void Mips64Assembler::Bnezc(GpuRegister rs, Mips64Label* label, bool is_bare) { + Bcond(label, /* is_r6 */ true, is_bare, kCondNEZ, rs); } -void Mips64Assembler::Bgezc(GpuRegister rt, Mips64Label* label) { - Bcond(label, kCondGEZ, rt); +void Mips64Assembler::Bc1eqz(FpuRegister ft, Mips64Label* label, bool is_bare) { + Bcond(label, /* is_r6 */ true, is_bare, kCondF, static_cast<GpuRegister>(ft), ZERO); } -void Mips64Assembler::Blezc(GpuRegister rt, Mips64Label* label) { - Bcond(label, kCondLEZ, rt); +void Mips64Assembler::Bc1nez(FpuRegister ft, Mips64Label* label, bool is_bare) { + Bcond(label, /* is_r6 */ true, is_bare, kCondT, static_cast<GpuRegister>(ft), ZERO); } -void Mips64Assembler::Bltuc(GpuRegister rs, GpuRegister rt, Mips64Label* label) { - Bcond(label, kCondLTU, rs, rt); +void Mips64Assembler::Bltz(GpuRegister rt, Mips64Label* label, bool is_bare) { + CHECK(is_bare); + Bcond(label, /* is_r6 */ false, is_bare, kCondLTZ, rt); } -void Mips64Assembler::Bgeuc(GpuRegister rs, GpuRegister rt, Mips64Label* label) { - Bcond(label, kCondGEU, rs, rt); +void Mips64Assembler::Bgtz(GpuRegister rt, Mips64Label* label, bool is_bare) { + CHECK(is_bare); + Bcond(label, /* is_r6 */ false, is_bare, kCondGTZ, rt); } -void Mips64Assembler::Beqc(GpuRegister rs, GpuRegister rt, Mips64Label* label) { - Bcond(label, kCondEQ, rs, rt); +void Mips64Assembler::Bgez(GpuRegister rt, Mips64Label* label, bool is_bare) { + CHECK(is_bare); + Bcond(label, /* is_r6 */ false, is_bare, kCondGEZ, rt); } -void Mips64Assembler::Bnec(GpuRegister rs, GpuRegister rt, Mips64Label* label) { - Bcond(label, kCondNE, rs, rt); +void Mips64Assembler::Blez(GpuRegister rt, Mips64Label* label, bool is_bare) { + CHECK(is_bare); + Bcond(label, /* is_r6 */ false, is_bare, kCondLEZ, rt); } -void Mips64Assembler::Beqzc(GpuRegister rs, Mips64Label* label) { - Bcond(label, kCondEQZ, rs); +void Mips64Assembler::Beq(GpuRegister rs, GpuRegister rt, Mips64Label* label, bool is_bare) { + CHECK(is_bare); + Bcond(label, /* is_r6 */ false, is_bare, kCondEQ, rs, rt); } -void Mips64Assembler::Bnezc(GpuRegister rs, Mips64Label* label) { - Bcond(label, kCondNEZ, rs); +void Mips64Assembler::Bne(GpuRegister rs, GpuRegister rt, Mips64Label* label, bool is_bare) { + CHECK(is_bare); + Bcond(label, /* is_r6 */ false, is_bare, kCondNE, rs, rt); } -void Mips64Assembler::Bc1eqz(FpuRegister ft, Mips64Label* label) { - Bcond(label, kCondF, static_cast<GpuRegister>(ft), ZERO); +void Mips64Assembler::Beqz(GpuRegister rs, Mips64Label* label, bool is_bare) { + CHECK(is_bare); + Bcond(label, /* is_r6 */ false, is_bare, kCondEQZ, rs); } -void Mips64Assembler::Bc1nez(FpuRegister ft, Mips64Label* label) { - Bcond(label, kCondT, static_cast<GpuRegister>(ft), ZERO); +void Mips64Assembler::Bnez(GpuRegister rs, Mips64Label* label, bool is_bare) { + CHECK(is_bare); + Bcond(label, /* is_r6 */ false, is_bare, kCondNEZ, rs); } void Mips64Assembler::AdjustBaseAndOffset(GpuRegister& base, diff --git a/compiler/utils/mips64/assembler_mips64.h b/compiler/utils/mips64/assembler_mips64.h index c39d120bce..023bcd681d 100644 --- a/compiler/utils/mips64/assembler_mips64.h +++ b/compiler/utils/mips64/assembler_mips64.h @@ -563,7 +563,14 @@ class Mips64Assembler FINAL : public Assembler, public JNIMacroAssembler<Pointer void Bnezc(GpuRegister rs, uint32_t imm21); void Bc1eqz(FpuRegister ft, uint16_t imm16); void Bc1nez(FpuRegister ft, uint16_t imm16); - void Beqz(GpuRegister rt, uint16_t imm16); + void Beq(GpuRegister rs, GpuRegister rt, uint16_t imm16); // R2 + void Bne(GpuRegister rs, GpuRegister rt, uint16_t imm16); // R2 + void Beqz(GpuRegister rt, uint16_t imm16); // R2 + void Bnez(GpuRegister rt, uint16_t imm16); // R2 + void Bltz(GpuRegister rt, uint16_t imm16); // R2 + void Bgez(GpuRegister rt, uint16_t imm16); // R2 + void Blez(GpuRegister rt, uint16_t imm16); // R2 + void Bgtz(GpuRegister rt, uint16_t imm16); // R2 void AddS(FpuRegister fd, FpuRegister fs, FpuRegister ft); void SubS(FpuRegister fd, FpuRegister fs, FpuRegister ft); @@ -922,22 +929,57 @@ class Mips64Assembler FINAL : public Assembler, public JNIMacroAssembler<Pointer // the table data) and should be loaded using LoadLabelAddress(). JumpTable* CreateJumpTable(std::vector<Mips64Label*>&& labels); - void Bc(Mips64Label* label); - void Balc(Mips64Label* label); - void Bltc(GpuRegister rs, GpuRegister rt, Mips64Label* label); - void Bltzc(GpuRegister rt, Mips64Label* label); - void Bgtzc(GpuRegister rt, Mips64Label* label); - void Bgec(GpuRegister rs, GpuRegister rt, Mips64Label* label); - void Bgezc(GpuRegister rt, Mips64Label* label); - void Blezc(GpuRegister rt, Mips64Label* label); - void Bltuc(GpuRegister rs, GpuRegister rt, Mips64Label* label); - void Bgeuc(GpuRegister rs, GpuRegister rt, Mips64Label* label); - void Beqc(GpuRegister rs, GpuRegister rt, Mips64Label* label); - void Bnec(GpuRegister rs, GpuRegister rt, Mips64Label* label); - void Beqzc(GpuRegister rs, Mips64Label* label); - void Bnezc(GpuRegister rs, Mips64Label* label); - void Bc1eqz(FpuRegister ft, Mips64Label* label); - void Bc1nez(FpuRegister ft, Mips64Label* label); + // When `is_bare` is false, the branches will promote to long (if the range + // of the individual branch instruction is insufficient) and the delay/ + // forbidden slots will be taken care of. + // Use `is_bare = false` when the branch target may be out of reach of the + // individual branch instruction. IOW, this is for general purpose use. + // + // When `is_bare` is true, just the branch instructions will be generated + // leaving delay/forbidden slot filling up to the caller and the branches + // won't promote to long if the range is insufficient (you'll get a + // compilation error when the range is exceeded). + // Use `is_bare = true` when the branch target is known to be within reach + // of the individual branch instruction. This is intended for small local + // optimizations around delay/forbidden slots. + // Also prefer using `is_bare = true` if the code near the branch is to be + // patched or analyzed at run time (e.g. introspection) to + // - show the intent and + // - fail during compilation rather than during patching/execution if the + // bare branch range is insufficent but the code size and layout are + // expected to remain unchanged + // + // R6 compact branches without delay/forbidden slots. + void Bc(Mips64Label* label, bool is_bare = false); + void Balc(Mips64Label* label, bool is_bare = false); + // R6 compact branches with forbidden slots. + void Bltc(GpuRegister rs, GpuRegister rt, Mips64Label* label, bool is_bare = false); + void Bltzc(GpuRegister rt, Mips64Label* label, bool is_bare = false); + void Bgtzc(GpuRegister rt, Mips64Label* label, bool is_bare = false); + void Bgec(GpuRegister rs, GpuRegister rt, Mips64Label* label, bool is_bare = false); + void Bgezc(GpuRegister rt, Mips64Label* label, bool is_bare = false); + void Blezc(GpuRegister rt, Mips64Label* label, bool is_bare = false); + void Bltuc(GpuRegister rs, GpuRegister rt, Mips64Label* label, bool is_bare = false); + void Bgeuc(GpuRegister rs, GpuRegister rt, Mips64Label* label, bool is_bare = false); + void Beqc(GpuRegister rs, GpuRegister rt, Mips64Label* label, bool is_bare = false); + void Bnec(GpuRegister rs, GpuRegister rt, Mips64Label* label, bool is_bare = false); + void Beqzc(GpuRegister rs, Mips64Label* label, bool is_bare = false); + void Bnezc(GpuRegister rs, Mips64Label* label, bool is_bare = false); + // R6 branches with delay slots. + void Bc1eqz(FpuRegister ft, Mips64Label* label, bool is_bare = false); + void Bc1nez(FpuRegister ft, Mips64Label* label, bool is_bare = false); + // R2 branches with delay slots that are also available on R6. + // The `is_bare` parameter exists and is checked in these branches only to + // prevent programming mistakes. These branches never promote to long, not + // even if `is_bare` is false. + void Bltz(GpuRegister rt, Mips64Label* label, bool is_bare = false); // R2 + void Bgtz(GpuRegister rt, Mips64Label* label, bool is_bare = false); // R2 + void Bgez(GpuRegister rt, Mips64Label* label, bool is_bare = false); // R2 + void Blez(GpuRegister rt, Mips64Label* label, bool is_bare = false); // R2 + void Beq(GpuRegister rs, GpuRegister rt, Mips64Label* label, bool is_bare = false); // R2 + void Bne(GpuRegister rs, GpuRegister rt, Mips64Label* label, bool is_bare = false); // R2 + void Beqz(GpuRegister rs, Mips64Label* label, bool is_bare = false); // R2 + void Bnez(GpuRegister rs, Mips64Label* label, bool is_bare = false); // R2 void EmitLoad(ManagedRegister m_dst, GpuRegister src_register, int32_t src_offset, size_t size); void AdjustBaseAndOffset(GpuRegister& base, int32_t& offset, bool is_doubleword); @@ -1379,10 +1421,16 @@ class Mips64Assembler FINAL : public Assembler, public JNIMacroAssembler<Pointer class Branch { public: enum Type { - // Short branches. + // R6 short branches (can be promoted to long). kUncondBranch, kCondBranch, kCall, + // R6 short branches (can't be promoted to long), forbidden/delay slots filled manually. + kBareUncondBranch, + kBareCondBranch, + kBareCall, + // R2 short branches (can't be promoted to long), delay slots filled manually. + kR2BareCondBranch, // Near label. kLabel, // Near literals. @@ -1425,8 +1473,8 @@ class Mips64Assembler FINAL : public Assembler, public JNIMacroAssembler<Pointer // different origins, e.g. to PC or PC+4. Encode the origin distance (as a number of 4-byte // instructions) from the instruction containing the offset. uint32_t pc_org; - // How large (in bits) a PC-relative offset can be for a given type of branch (kCondBranch is - // an exception: use kOffset23 for beqzc/bnezc). + // How large (in bits) a PC-relative offset can be for a given type of branch (kCondBranch + // and kBareCondBranch are an exception: use kOffset23 for beqzc/bnezc). OffsetBits offset_size; // Some MIPS instructions with PC-relative offsets shift the offset by 2. Encode the shift // count. @@ -1435,13 +1483,15 @@ class Mips64Assembler FINAL : public Assembler, public JNIMacroAssembler<Pointer static const BranchInfo branch_info_[/* Type */]; // Unconditional branch or call. - Branch(uint32_t location, uint32_t target, bool is_call); + Branch(uint32_t location, uint32_t target, bool is_call, bool is_bare); // Conditional branch. - Branch(uint32_t location, + Branch(bool is_r6, + uint32_t location, uint32_t target, BranchCondition condition, GpuRegister lhs_reg, - GpuRegister rhs_reg); + GpuRegister rhs_reg, + bool is_bare); // Label address (in literal area) or literal. Branch(uint32_t location, GpuRegister dest_reg, Type label_or_literal_type); @@ -1467,6 +1517,7 @@ class Mips64Assembler FINAL : public Assembler, public JNIMacroAssembler<Pointer uint32_t GetOldSize() const; uint32_t GetEndLocation() const; uint32_t GetOldEndLocation() const; + bool IsBare() const; bool IsLong() const; bool IsResolved() const; @@ -1527,7 +1578,7 @@ class Mips64Assembler FINAL : public Assembler, public JNIMacroAssembler<Pointer private: // Completes branch construction by determining and recording its type. - void InitializeType(Type initial_type); + void InitializeType(Type initial_type, bool is_r6); // Helper for the above. void InitShortOrLong(OffsetBits ofs_size, Type short_type, Type long_type); @@ -1554,7 +1605,8 @@ class Mips64Assembler FINAL : public Assembler, public JNIMacroAssembler<Pointer void EmitI26(int opcode, uint32_t imm26); void EmitFR(int opcode, int fmt, FpuRegister ft, FpuRegister fs, FpuRegister fd, int funct); void EmitFI(int opcode, int fmt, FpuRegister rt, uint16_t imm); - void EmitBcondc(BranchCondition cond, GpuRegister rs, GpuRegister rt, uint32_t imm16_21); + void EmitBcondR6(BranchCondition cond, GpuRegister rs, GpuRegister rt, uint32_t imm16_21); + void EmitBcondR2(BranchCondition cond, GpuRegister rs, GpuRegister rt, uint16_t imm16); void EmitMsa3R(int operation, int df, VectorRegister wt, @@ -1568,12 +1620,14 @@ class Mips64Assembler FINAL : public Assembler, public JNIMacroAssembler<Pointer void EmitMsa2R(int operation, int df, VectorRegister ws, VectorRegister wd, int minor_opcode); void EmitMsa2RF(int operation, int df, VectorRegister ws, VectorRegister wd, int minor_opcode); - void Buncond(Mips64Label* label); + void Buncond(Mips64Label* label, bool is_bare); void Bcond(Mips64Label* label, + bool is_r6, + bool is_bare, BranchCondition condition, GpuRegister lhs, GpuRegister rhs = ZERO); - void Call(Mips64Label* label); + void Call(Mips64Label* label, bool is_bare); void FinalizeLabeledBranch(Mips64Label* label); Branch* GetBranch(uint32_t branch_id); diff --git a/compiler/utils/mips64/assembler_mips64_test.cc b/compiler/utils/mips64/assembler_mips64_test.cc index 021e335697..1541780e2f 100644 --- a/compiler/utils/mips64/assembler_mips64_test.cc +++ b/compiler/utils/mips64/assembler_mips64_test.cc @@ -257,11 +257,46 @@ class AssemblerMIPS64Test : public AssemblerTest<mips64::Mips64Assembler, return result; } + void BranchHelper(void (mips64::Mips64Assembler::*f)(mips64::Mips64Label*, + bool), + const std::string& instr_name, + bool is_bare = false) { + mips64::Mips64Label label1, label2; + (Base::GetAssembler()->*f)(&label1, is_bare); + constexpr size_t kAdduCount1 = 63; + for (size_t i = 0; i != kAdduCount1; ++i) { + __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO); + } + __ Bind(&label1); + (Base::GetAssembler()->*f)(&label2, is_bare); + constexpr size_t kAdduCount2 = 64; + for (size_t i = 0; i != kAdduCount2; ++i) { + __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO); + } + __ Bind(&label2); + (Base::GetAssembler()->*f)(&label1, is_bare); + __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO); + + std::string expected = + ".set noreorder\n" + + instr_name + " 1f\n" + + RepeatInsn(kAdduCount1, "addu $zero, $zero, $zero\n") + + "1:\n" + + instr_name + " 2f\n" + + RepeatInsn(kAdduCount2, "addu $zero, $zero, $zero\n") + + "2:\n" + + instr_name + " 1b\n" + + "addu $zero, $zero, $zero\n"; + DriverStr(expected, instr_name); + } + void BranchCondOneRegHelper(void (mips64::Mips64Assembler::*f)(mips64::GpuRegister, - mips64::Mips64Label*), - const std::string& instr_name) { + mips64::Mips64Label*, + bool), + const std::string& instr_name, + bool is_bare = false) { mips64::Mips64Label label; - (Base::GetAssembler()->*f)(mips64::A0, &label); + (Base::GetAssembler()->*f)(mips64::A0, &label, is_bare); constexpr size_t kAdduCount1 = 63; for (size_t i = 0; i != kAdduCount1; ++i) { __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO); @@ -271,26 +306,30 @@ class AssemblerMIPS64Test : public AssemblerTest<mips64::Mips64Assembler, for (size_t i = 0; i != kAdduCount2; ++i) { __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO); } - (Base::GetAssembler()->*f)(mips64::A1, &label); + (Base::GetAssembler()->*f)(mips64::A1, &label, is_bare); + __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO); std::string expected = ".set noreorder\n" + - instr_name + " $a0, 1f\n" - "nop\n" + + instr_name + " $a0, 1f\n" + + (is_bare ? "" : "nop\n") + RepeatInsn(kAdduCount1, "addu $zero, $zero, $zero\n") + "1:\n" + RepeatInsn(kAdduCount2, "addu $zero, $zero, $zero\n") + - instr_name + " $a1, 1b\n" - "nop\n"; + instr_name + " $a1, 1b\n" + + (is_bare ? "" : "nop\n") + + "addu $zero, $zero, $zero\n"; DriverStr(expected, instr_name); } void BranchCondTwoRegsHelper(void (mips64::Mips64Assembler::*f)(mips64::GpuRegister, mips64::GpuRegister, - mips64::Mips64Label*), - const std::string& instr_name) { + mips64::Mips64Label*, + bool), + const std::string& instr_name, + bool is_bare = false) { mips64::Mips64Label label; - (Base::GetAssembler()->*f)(mips64::A0, mips64::A1, &label); + (Base::GetAssembler()->*f)(mips64::A0, mips64::A1, &label, is_bare); constexpr size_t kAdduCount1 = 63; for (size_t i = 0; i != kAdduCount1; ++i) { __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO); @@ -300,17 +339,51 @@ class AssemblerMIPS64Test : public AssemblerTest<mips64::Mips64Assembler, for (size_t i = 0; i != kAdduCount2; ++i) { __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO); } - (Base::GetAssembler()->*f)(mips64::A2, mips64::A3, &label); + (Base::GetAssembler()->*f)(mips64::A2, mips64::A3, &label, is_bare); + __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO); std::string expected = ".set noreorder\n" + - instr_name + " $a0, $a1, 1f\n" - "nop\n" + + instr_name + " $a0, $a1, 1f\n" + + (is_bare ? "" : "nop\n") + RepeatInsn(kAdduCount1, "addu $zero, $zero, $zero\n") + "1:\n" + RepeatInsn(kAdduCount2, "addu $zero, $zero, $zero\n") + - instr_name + " $a2, $a3, 1b\n" - "nop\n"; + instr_name + " $a2, $a3, 1b\n" + + (is_bare ? "" : "nop\n") + + "addu $zero, $zero, $zero\n"; + DriverStr(expected, instr_name); + } + + void BranchFpuCondHelper(void (mips64::Mips64Assembler::*f)(mips64::FpuRegister, + mips64::Mips64Label*, + bool), + const std::string& instr_name, + bool is_bare = false) { + mips64::Mips64Label label; + (Base::GetAssembler()->*f)(mips64::F0, &label, is_bare); + constexpr size_t kAdduCount1 = 63; + for (size_t i = 0; i != kAdduCount1; ++i) { + __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO); + } + __ Bind(&label); + constexpr size_t kAdduCount2 = 64; + for (size_t i = 0; i != kAdduCount2; ++i) { + __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO); + } + (Base::GetAssembler()->*f)(mips64::F31, &label, is_bare); + __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO); + + std::string expected = + ".set noreorder\n" + + instr_name + " $f0, 1f\n" + + (is_bare ? "" : "nop\n") + + RepeatInsn(kAdduCount1, "addu $zero, $zero, $zero\n") + + "1:\n" + + RepeatInsn(kAdduCount2, "addu $zero, $zero, $zero\n") + + instr_name + " $f31, 1b\n" + + (is_bare ? "" : "nop\n") + + "addu $zero, $zero, $zero\n"; DriverStr(expected, instr_name); } @@ -668,120 +741,21 @@ TEST_F(AssemblerMIPS64Test, Sdc1) { "sdc1"); } -//////////////// -// CALL / JMP // -//////////////// +////////////// +// BRANCHES // +////////////// TEST_F(AssemblerMIPS64Test, Jalr) { DriverStr(".set noreorder\n" + RepeatRRNoDupes(&mips64::Mips64Assembler::Jalr, "jalr ${reg1}, ${reg2}"), "jalr"); } -TEST_F(AssemblerMIPS64Test, Balc) { - mips64::Mips64Label label1, label2; - __ Balc(&label1); - constexpr size_t kAdduCount1 = 63; - for (size_t i = 0; i != kAdduCount1; ++i) { - __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO); - } - __ Bind(&label1); - __ Balc(&label2); - constexpr size_t kAdduCount2 = 64; - for (size_t i = 0; i != kAdduCount2; ++i) { - __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO); - } - __ Bind(&label2); - __ Balc(&label1); - - std::string expected = - ".set noreorder\n" - "balc 1f\n" + - RepeatInsn(kAdduCount1, "addu $zero, $zero, $zero\n") + - "1:\n" - "balc 2f\n" + - RepeatInsn(kAdduCount2, "addu $zero, $zero, $zero\n") + - "2:\n" - "balc 1b\n"; - DriverStr(expected, "Balc"); -} - -TEST_F(AssemblerMIPS64Test, LongBalc) { - constexpr uint32_t kNopCount1 = (1u << 25) + 1; - constexpr uint32_t kNopCount2 = (1u << 25) + 1; - constexpr uint32_t kRequiredCapacity = (kNopCount1 + kNopCount2 + 6u) * 4u; - ASSERT_LT(__ GetBuffer()->Capacity(), kRequiredCapacity); - __ GetBuffer()->ExtendCapacity(kRequiredCapacity); - mips64::Mips64Label label1, label2; - __ Balc(&label1); - for (uint32_t i = 0; i != kNopCount1; ++i) { - __ Nop(); - } - __ Bind(&label1); - __ Balc(&label2); - for (uint32_t i = 0; i != kNopCount2; ++i) { - __ Nop(); - } - __ Bind(&label2); - __ Balc(&label1); - - uint32_t offset_forward1 = 2 + kNopCount1; // 2: account for auipc and jialc. - offset_forward1 <<= 2; - offset_forward1 += (offset_forward1 & 0x8000) << 1; // Account for sign extension in jialc. - - uint32_t offset_forward2 = 2 + kNopCount2; // 2: account for auipc and jialc. - offset_forward2 <<= 2; - offset_forward2 += (offset_forward2 & 0x8000) << 1; // Account for sign extension in jialc. - - uint32_t offset_back = -(2 + kNopCount2); // 2: account for auipc and jialc. - offset_back <<= 2; - offset_back += (offset_back & 0x8000) << 1; // Account for sign extension in jialc. - - // Note, we're using the ".fill" directive to tell the assembler to generate many NOPs - // instead of generating them ourselves in the source code. This saves a few minutes - // of test time. - std::ostringstream oss; - oss << - ".set noreorder\n" - "auipc $at, 0x" << std::hex << High16Bits(offset_forward1) << "\n" - "jialc $at, 0x" << std::hex << Low16Bits(offset_forward1) << "\n" - ".fill 0x" << std::hex << kNopCount1 << " , 4, 0\n" - "1:\n" - "auipc $at, 0x" << std::hex << High16Bits(offset_forward2) << "\n" - "jialc $at, 0x" << std::hex << Low16Bits(offset_forward2) << "\n" - ".fill 0x" << std::hex << kNopCount2 << " , 4, 0\n" - "2:\n" - "auipc $at, 0x" << std::hex << High16Bits(offset_back) << "\n" - "jialc $at, 0x" << std::hex << Low16Bits(offset_back) << "\n"; - std::string expected = oss.str(); - DriverStr(expected, "LongBalc"); -} - TEST_F(AssemblerMIPS64Test, Bc) { - mips64::Mips64Label label1, label2; - __ Bc(&label1); - constexpr size_t kAdduCount1 = 63; - for (size_t i = 0; i != kAdduCount1; ++i) { - __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO); - } - __ Bind(&label1); - __ Bc(&label2); - constexpr size_t kAdduCount2 = 64; - for (size_t i = 0; i != kAdduCount2; ++i) { - __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO); - } - __ Bind(&label2); - __ Bc(&label1); + BranchHelper(&mips64::Mips64Assembler::Bc, "Bc"); +} - std::string expected = - ".set noreorder\n" - "bc 1f\n" + - RepeatInsn(kAdduCount1, "addu $zero, $zero, $zero\n") + - "1:\n" - "bc 2f\n" + - RepeatInsn(kAdduCount2, "addu $zero, $zero, $zero\n") + - "2:\n" - "bc 1b\n"; - DriverStr(expected, "Bc"); +TEST_F(AssemblerMIPS64Test, Balc) { + BranchHelper(&mips64::Mips64Assembler::Balc, "Balc"); } TEST_F(AssemblerMIPS64Test, Beqzc) { @@ -833,55 +807,107 @@ TEST_F(AssemblerMIPS64Test, Bgeuc) { } TEST_F(AssemblerMIPS64Test, Bc1eqz) { - mips64::Mips64Label label; - __ Bc1eqz(mips64::F0, &label); - constexpr size_t kAdduCount1 = 63; - for (size_t i = 0; i != kAdduCount1; ++i) { - __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO); - } - __ Bind(&label); - constexpr size_t kAdduCount2 = 64; - for (size_t i = 0; i != kAdduCount2; ++i) { - __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO); - } - __ Bc1eqz(mips64::F31, &label); - - std::string expected = - ".set noreorder\n" - "bc1eqz $f0, 1f\n" - "nop\n" + - RepeatInsn(kAdduCount1, "addu $zero, $zero, $zero\n") + - "1:\n" + - RepeatInsn(kAdduCount2, "addu $zero, $zero, $zero\n") + - "bc1eqz $f31, 1b\n" - "nop\n"; - DriverStr(expected, "Bc1eqz"); + BranchFpuCondHelper(&mips64::Mips64Assembler::Bc1eqz, "Bc1eqz"); } TEST_F(AssemblerMIPS64Test, Bc1nez) { - mips64::Mips64Label label; - __ Bc1nez(mips64::F0, &label); - constexpr size_t kAdduCount1 = 63; - for (size_t i = 0; i != kAdduCount1; ++i) { - __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO); - } - __ Bind(&label); - constexpr size_t kAdduCount2 = 64; - for (size_t i = 0; i != kAdduCount2; ++i) { - __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO); - } - __ Bc1nez(mips64::F31, &label); + BranchFpuCondHelper(&mips64::Mips64Assembler::Bc1nez, "Bc1nez"); +} - std::string expected = - ".set noreorder\n" - "bc1nez $f0, 1f\n" - "nop\n" + - RepeatInsn(kAdduCount1, "addu $zero, $zero, $zero\n") + - "1:\n" + - RepeatInsn(kAdduCount2, "addu $zero, $zero, $zero\n") + - "bc1nez $f31, 1b\n" - "nop\n"; - DriverStr(expected, "Bc1nez"); +TEST_F(AssemblerMIPS64Test, BareBc) { + BranchHelper(&mips64::Mips64Assembler::Bc, "Bc", /* is_bare */ true); +} + +TEST_F(AssemblerMIPS64Test, BareBalc) { + BranchHelper(&mips64::Mips64Assembler::Balc, "Balc", /* is_bare */ true); +} + +TEST_F(AssemblerMIPS64Test, BareBeqzc) { + BranchCondOneRegHelper(&mips64::Mips64Assembler::Beqzc, "Beqzc", /* is_bare */ true); +} + +TEST_F(AssemblerMIPS64Test, BareBnezc) { + BranchCondOneRegHelper(&mips64::Mips64Assembler::Bnezc, "Bnezc", /* is_bare */ true); +} + +TEST_F(AssemblerMIPS64Test, BareBltzc) { + BranchCondOneRegHelper(&mips64::Mips64Assembler::Bltzc, "Bltzc", /* is_bare */ true); +} + +TEST_F(AssemblerMIPS64Test, BareBgezc) { + BranchCondOneRegHelper(&mips64::Mips64Assembler::Bgezc, "Bgezc", /* is_bare */ true); +} + +TEST_F(AssemblerMIPS64Test, BareBlezc) { + BranchCondOneRegHelper(&mips64::Mips64Assembler::Blezc, "Blezc", /* is_bare */ true); +} + +TEST_F(AssemblerMIPS64Test, BareBgtzc) { + BranchCondOneRegHelper(&mips64::Mips64Assembler::Bgtzc, "Bgtzc", /* is_bare */ true); +} + +TEST_F(AssemblerMIPS64Test, BareBeqc) { + BranchCondTwoRegsHelper(&mips64::Mips64Assembler::Beqc, "Beqc", /* is_bare */ true); +} + +TEST_F(AssemblerMIPS64Test, BareBnec) { + BranchCondTwoRegsHelper(&mips64::Mips64Assembler::Bnec, "Bnec", /* is_bare */ true); +} + +TEST_F(AssemblerMIPS64Test, BareBltc) { + BranchCondTwoRegsHelper(&mips64::Mips64Assembler::Bltc, "Bltc", /* is_bare */ true); +} + +TEST_F(AssemblerMIPS64Test, BareBgec) { + BranchCondTwoRegsHelper(&mips64::Mips64Assembler::Bgec, "Bgec", /* is_bare */ true); +} + +TEST_F(AssemblerMIPS64Test, BareBltuc) { + BranchCondTwoRegsHelper(&mips64::Mips64Assembler::Bltuc, "Bltuc", /* is_bare */ true); +} + +TEST_F(AssemblerMIPS64Test, BareBgeuc) { + BranchCondTwoRegsHelper(&mips64::Mips64Assembler::Bgeuc, "Bgeuc", /* is_bare */ true); +} + +TEST_F(AssemblerMIPS64Test, BareBc1eqz) { + BranchFpuCondHelper(&mips64::Mips64Assembler::Bc1eqz, "Bc1eqz", /* is_bare */ true); +} + +TEST_F(AssemblerMIPS64Test, BareBc1nez) { + BranchFpuCondHelper(&mips64::Mips64Assembler::Bc1nez, "Bc1nez", /* is_bare */ true); +} + +TEST_F(AssemblerMIPS64Test, BareBeqz) { + BranchCondOneRegHelper(&mips64::Mips64Assembler::Beqz, "Beqz", /* is_bare */ true); +} + +TEST_F(AssemblerMIPS64Test, BareBnez) { + BranchCondOneRegHelper(&mips64::Mips64Assembler::Bnez, "Bnez", /* is_bare */ true); +} + +TEST_F(AssemblerMIPS64Test, BareBltz) { + BranchCondOneRegHelper(&mips64::Mips64Assembler::Bltz, "Bltz", /* is_bare */ true); +} + +TEST_F(AssemblerMIPS64Test, BareBgez) { + BranchCondOneRegHelper(&mips64::Mips64Assembler::Bgez, "Bgez", /* is_bare */ true); +} + +TEST_F(AssemblerMIPS64Test, BareBlez) { + BranchCondOneRegHelper(&mips64::Mips64Assembler::Blez, "Blez", /* is_bare */ true); +} + +TEST_F(AssemblerMIPS64Test, BareBgtz) { + BranchCondOneRegHelper(&mips64::Mips64Assembler::Bgtz, "Bgtz", /* is_bare */ true); +} + +TEST_F(AssemblerMIPS64Test, BareBeq) { + BranchCondTwoRegsHelper(&mips64::Mips64Assembler::Beq, "Beq", /* is_bare */ true); +} + +TEST_F(AssemblerMIPS64Test, BareBne) { + BranchCondTwoRegsHelper(&mips64::Mips64Assembler::Bne, "Bne", /* is_bare */ true); } TEST_F(AssemblerMIPS64Test, LongBeqc) { @@ -924,6 +950,102 @@ TEST_F(AssemblerMIPS64Test, LongBeqc) { DriverStr(expected, "LongBeqc"); } +TEST_F(AssemblerMIPS64Test, LongBeqzc) { + constexpr uint32_t kNopCount1 = (1u << 20) + 1; + constexpr uint32_t kNopCount2 = (1u << 20) + 1; + constexpr uint32_t kRequiredCapacity = (kNopCount1 + kNopCount2 + 6u) * 4u; + ASSERT_LT(__ GetBuffer()->Capacity(), kRequiredCapacity); + __ GetBuffer()->ExtendCapacity(kRequiredCapacity); + mips64::Mips64Label label; + __ Beqzc(mips64::A0, &label); + for (uint32_t i = 0; i != kNopCount1; ++i) { + __ Nop(); + } + __ Bind(&label); + for (uint32_t i = 0; i != kNopCount2; ++i) { + __ Nop(); + } + __ Beqzc(mips64::A2, &label); + + uint32_t offset_forward = 2 + kNopCount1; // 2: account for auipc and jic. + offset_forward <<= 2; + offset_forward += (offset_forward & 0x8000) << 1; // Account for sign extension in jic. + + uint32_t offset_back = -(kNopCount2 + 1); // 1: account for bnezc. + offset_back <<= 2; + offset_back += (offset_back & 0x8000) << 1; // Account for sign extension in jic. + + // Note, we're using the ".fill" directive to tell the assembler to generate many NOPs + // instead of generating them ourselves in the source code. This saves test time. + std::ostringstream oss; + oss << + ".set noreorder\n" + "bnezc $a0, 1f\n" + "auipc $at, 0x" << std::hex << High16Bits(offset_forward) << "\n" + "jic $at, 0x" << std::hex << Low16Bits(offset_forward) << "\n" + "1:\n" << + ".fill 0x" << std::hex << kNopCount1 << " , 4, 0\n" + "2:\n" << + ".fill 0x" << std::hex << kNopCount2 << " , 4, 0\n" + "bnezc $a2, 3f\n" + "auipc $at, 0x" << std::hex << High16Bits(offset_back) << "\n" + "jic $at, 0x" << std::hex << Low16Bits(offset_back) << "\n" + "3:\n"; + std::string expected = oss.str(); + DriverStr(expected, "LongBeqzc"); +} + +TEST_F(AssemblerMIPS64Test, LongBalc) { + constexpr uint32_t kNopCount1 = (1u << 25) + 1; + constexpr uint32_t kNopCount2 = (1u << 25) + 1; + constexpr uint32_t kRequiredCapacity = (kNopCount1 + kNopCount2 + 6u) * 4u; + ASSERT_LT(__ GetBuffer()->Capacity(), kRequiredCapacity); + __ GetBuffer()->ExtendCapacity(kRequiredCapacity); + mips64::Mips64Label label1, label2; + __ Balc(&label1); + for (uint32_t i = 0; i != kNopCount1; ++i) { + __ Nop(); + } + __ Bind(&label1); + __ Balc(&label2); + for (uint32_t i = 0; i != kNopCount2; ++i) { + __ Nop(); + } + __ Bind(&label2); + __ Balc(&label1); + + uint32_t offset_forward1 = 2 + kNopCount1; // 2: account for auipc and jialc. + offset_forward1 <<= 2; + offset_forward1 += (offset_forward1 & 0x8000) << 1; // Account for sign extension in jialc. + + uint32_t offset_forward2 = 2 + kNopCount2; // 2: account for auipc and jialc. + offset_forward2 <<= 2; + offset_forward2 += (offset_forward2 & 0x8000) << 1; // Account for sign extension in jialc. + + uint32_t offset_back = -(2 + kNopCount2); // 2: account for auipc and jialc. + offset_back <<= 2; + offset_back += (offset_back & 0x8000) << 1; // Account for sign extension in jialc. + + // Note, we're using the ".fill" directive to tell the assembler to generate many NOPs + // instead of generating them ourselves in the source code. This saves a few minutes + // of test time. + std::ostringstream oss; + oss << + ".set noreorder\n" + "auipc $at, 0x" << std::hex << High16Bits(offset_forward1) << "\n" + "jialc $at, 0x" << std::hex << Low16Bits(offset_forward1) << "\n" + ".fill 0x" << std::hex << kNopCount1 << " , 4, 0\n" + "1:\n" + "auipc $at, 0x" << std::hex << High16Bits(offset_forward2) << "\n" + "jialc $at, 0x" << std::hex << Low16Bits(offset_forward2) << "\n" + ".fill 0x" << std::hex << kNopCount2 << " , 4, 0\n" + "2:\n" + "auipc $at, 0x" << std::hex << High16Bits(offset_back) << "\n" + "jialc $at, 0x" << std::hex << Low16Bits(offset_back) << "\n"; + std::string expected = oss.str(); + DriverStr(expected, "LongBalc"); +} + ////////// // MISC // ////////// @@ -961,235 +1083,6 @@ TEST_F(AssemblerMIPS64Test, Addiupc) { DriverStr(RepeatRIb(&mips64::Mips64Assembler::Addiupc, 19, code), "Addiupc"); } -TEST_F(AssemblerMIPS64Test, LoadFarthestNearLabelAddress) { - mips64::Mips64Label label; - __ LoadLabelAddress(mips64::V0, &label); - constexpr uint32_t kAdduCount = 0x3FFDE; - for (uint32_t i = 0; i != kAdduCount; ++i) { - __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO); - } - __ Bind(&label); - - std::string expected = - "lapc $v0, 1f\n" + - RepeatInsn(kAdduCount, "addu $zero, $zero, $zero\n") + - "1:\n"; - DriverStr(expected, "LoadFarthestNearLabelAddress"); - EXPECT_EQ(__ GetLabelLocation(&label), (1 + kAdduCount) * 4); -} - -TEST_F(AssemblerMIPS64Test, LoadNearestFarLabelAddress) { - mips64::Mips64Label label; - __ LoadLabelAddress(mips64::V0, &label); - constexpr uint32_t kAdduCount = 0x3FFDF; - for (uint32_t i = 0; i != kAdduCount; ++i) { - __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO); - } - __ Bind(&label); - - std::string expected = - "1:\n" - "auipc $at, %hi(2f - 1b)\n" - "daddiu $v0, $at, %lo(2f - 1b)\n" + - RepeatInsn(kAdduCount, "addu $zero, $zero, $zero\n") + - "2:\n"; - DriverStr(expected, "LoadNearestFarLabelAddress"); - EXPECT_EQ(__ GetLabelLocation(&label), (2 + kAdduCount) * 4); -} - -TEST_F(AssemblerMIPS64Test, LoadFarthestNearLiteral) { - mips64::Literal* literal = __ NewLiteral<uint32_t>(0x12345678); - __ LoadLiteral(mips64::V0, mips64::kLoadWord, literal); - constexpr uint32_t kAdduCount = 0x3FFDE; - for (uint32_t i = 0; i != kAdduCount; ++i) { - __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO); - } - - std::string expected = - "lwpc $v0, 1f\n" + - RepeatInsn(kAdduCount, "addu $zero, $zero, $zero\n") + - "1:\n" - ".word 0x12345678\n"; - DriverStr(expected, "LoadFarthestNearLiteral"); - EXPECT_EQ(__ GetLabelLocation(literal->GetLabel()), (1 + kAdduCount) * 4); -} - -TEST_F(AssemblerMIPS64Test, LoadNearestFarLiteral) { - mips64::Literal* literal = __ NewLiteral<uint32_t>(0x12345678); - __ LoadLiteral(mips64::V0, mips64::kLoadWord, literal); - constexpr uint32_t kAdduCount = 0x3FFDF; - for (uint32_t i = 0; i != kAdduCount; ++i) { - __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO); - } - - std::string expected = - "1:\n" - "auipc $at, %hi(2f - 1b)\n" - "lw $v0, %lo(2f - 1b)($at)\n" + - RepeatInsn(kAdduCount, "addu $zero, $zero, $zero\n") + - "2:\n" - ".word 0x12345678\n"; - DriverStr(expected, "LoadNearestFarLiteral"); - EXPECT_EQ(__ GetLabelLocation(literal->GetLabel()), (2 + kAdduCount) * 4); -} - -TEST_F(AssemblerMIPS64Test, LoadFarthestNearLiteralUnsigned) { - mips64::Literal* literal = __ NewLiteral<uint32_t>(0x12345678); - __ LoadLiteral(mips64::V0, mips64::kLoadUnsignedWord, literal); - constexpr uint32_t kAdduCount = 0x3FFDE; - for (uint32_t i = 0; i != kAdduCount; ++i) { - __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO); - } - - std::string expected = - "lwupc $v0, 1f\n" + - RepeatInsn(kAdduCount, "addu $zero, $zero, $zero\n") + - "1:\n" - ".word 0x12345678\n"; - DriverStr(expected, "LoadFarthestNearLiteralUnsigned"); - EXPECT_EQ(__ GetLabelLocation(literal->GetLabel()), (1 + kAdduCount) * 4); -} - -TEST_F(AssemblerMIPS64Test, LoadNearestFarLiteralUnsigned) { - mips64::Literal* literal = __ NewLiteral<uint32_t>(0x12345678); - __ LoadLiteral(mips64::V0, mips64::kLoadUnsignedWord, literal); - constexpr uint32_t kAdduCount = 0x3FFDF; - for (uint32_t i = 0; i != kAdduCount; ++i) { - __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO); - } - - std::string expected = - "1:\n" - "auipc $at, %hi(2f - 1b)\n" - "lwu $v0, %lo(2f - 1b)($at)\n" + - RepeatInsn(kAdduCount, "addu $zero, $zero, $zero\n") + - "2:\n" - ".word 0x12345678\n"; - DriverStr(expected, "LoadNearestFarLiteralUnsigned"); - EXPECT_EQ(__ GetLabelLocation(literal->GetLabel()), (2 + kAdduCount) * 4); -} - -TEST_F(AssemblerMIPS64Test, LoadFarthestNearLiteralLong) { - mips64::Literal* literal = __ NewLiteral<uint64_t>(UINT64_C(0x0123456789ABCDEF)); - __ LoadLiteral(mips64::V0, mips64::kLoadDoubleword, literal); - constexpr uint32_t kAdduCount = 0x3FFDD; - for (uint32_t i = 0; i != kAdduCount; ++i) { - __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO); - } - - std::string expected = - "ldpc $v0, 1f\n" + - RepeatInsn(kAdduCount, "addu $zero, $zero, $zero\n") + - "1:\n" - ".dword 0x0123456789ABCDEF\n"; - DriverStr(expected, "LoadFarthestNearLiteralLong"); - EXPECT_EQ(__ GetLabelLocation(literal->GetLabel()), (1 + kAdduCount) * 4); -} - -TEST_F(AssemblerMIPS64Test, LoadNearestFarLiteralLong) { - mips64::Literal* literal = __ NewLiteral<uint64_t>(UINT64_C(0x0123456789ABCDEF)); - __ LoadLiteral(mips64::V0, mips64::kLoadDoubleword, literal); - constexpr uint32_t kAdduCount = 0x3FFDE; - for (uint32_t i = 0; i != kAdduCount; ++i) { - __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO); - } - - std::string expected = - "1:\n" - "auipc $at, %hi(2f - 1b)\n" - "ld $v0, %lo(2f - 1b)($at)\n" + - RepeatInsn(kAdduCount, "addu $zero, $zero, $zero\n") + - "2:\n" - ".dword 0x0123456789ABCDEF\n"; - DriverStr(expected, "LoadNearestFarLiteralLong"); - EXPECT_EQ(__ GetLabelLocation(literal->GetLabel()), (2 + kAdduCount) * 4); -} - -TEST_F(AssemblerMIPS64Test, LongLiteralAlignmentNop) { - mips64::Literal* literal1 = __ NewLiteral<uint64_t>(UINT64_C(0x0123456789ABCDEF)); - mips64::Literal* literal2 = __ NewLiteral<uint64_t>(UINT64_C(0x5555555555555555)); - mips64::Literal* literal3 = __ NewLiteral<uint64_t>(UINT64_C(0xAAAAAAAAAAAAAAAA)); - __ LoadLiteral(mips64::A1, mips64::kLoadDoubleword, literal1); - __ LoadLiteral(mips64::A2, mips64::kLoadDoubleword, literal2); - __ LoadLiteral(mips64::A3, mips64::kLoadDoubleword, literal3); - __ LoadLabelAddress(mips64::V0, literal1->GetLabel()); - __ LoadLabelAddress(mips64::V1, literal2->GetLabel()); - // A nop will be inserted here before the 64-bit literals. - - std::string expected = - "ldpc $a1, 1f\n" - // The GNU assembler incorrectly requires the ldpc instruction to be located - // at an address that's a multiple of 8. TODO: Remove this workaround if/when - // the assembler is fixed. - // "ldpc $a2, 2f\n" - ".word 0xECD80004\n" - "ldpc $a3, 3f\n" - "lapc $v0, 1f\n" - "lapc $v1, 2f\n" - "nop\n" - "1:\n" - ".dword 0x0123456789ABCDEF\n" - "2:\n" - ".dword 0x5555555555555555\n" - "3:\n" - ".dword 0xAAAAAAAAAAAAAAAA\n"; - DriverStr(expected, "LongLiteralAlignmentNop"); - EXPECT_EQ(__ GetLabelLocation(literal1->GetLabel()), 6 * 4u); - EXPECT_EQ(__ GetLabelLocation(literal2->GetLabel()), 8 * 4u); - EXPECT_EQ(__ GetLabelLocation(literal3->GetLabel()), 10 * 4u); -} - -TEST_F(AssemblerMIPS64Test, LongLiteralAlignmentNoNop) { - mips64::Literal* literal1 = __ NewLiteral<uint64_t>(UINT64_C(0x0123456789ABCDEF)); - mips64::Literal* literal2 = __ NewLiteral<uint64_t>(UINT64_C(0x5555555555555555)); - __ LoadLiteral(mips64::A1, mips64::kLoadDoubleword, literal1); - __ LoadLiteral(mips64::A2, mips64::kLoadDoubleword, literal2); - __ LoadLabelAddress(mips64::V0, literal1->GetLabel()); - __ LoadLabelAddress(mips64::V1, literal2->GetLabel()); - - std::string expected = - "ldpc $a1, 1f\n" - // The GNU assembler incorrectly requires the ldpc instruction to be located - // at an address that's a multiple of 8. TODO: Remove this workaround if/when - // the assembler is fixed. - // "ldpc $a2, 2f\n" - ".word 0xECD80003\n" - "lapc $v0, 1f\n" - "lapc $v1, 2f\n" - "1:\n" - ".dword 0x0123456789ABCDEF\n" - "2:\n" - ".dword 0x5555555555555555\n"; - DriverStr(expected, "LongLiteralAlignmentNoNop"); - EXPECT_EQ(__ GetLabelLocation(literal1->GetLabel()), 4 * 4u); - EXPECT_EQ(__ GetLabelLocation(literal2->GetLabel()), 6 * 4u); -} - -TEST_F(AssemblerMIPS64Test, FarLongLiteralAlignmentNop) { - mips64::Literal* literal = __ NewLiteral<uint64_t>(UINT64_C(0x0123456789ABCDEF)); - __ LoadLiteral(mips64::V0, mips64::kLoadDoubleword, literal); - __ LoadLabelAddress(mips64::V1, literal->GetLabel()); - constexpr uint32_t kAdduCount = 0x3FFDF; - for (uint32_t i = 0; i != kAdduCount; ++i) { - __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO); - } - // A nop will be inserted here before the 64-bit literal. - - std::string expected = - "1:\n" - "auipc $at, %hi(3f - 1b)\n" - "ld $v0, %lo(3f - 1b)($at)\n" - "2:\n" - "auipc $at, %hi(3f - 2b)\n" - "daddiu $v1, $at, %lo(3f - 2b)\n" + - RepeatInsn(kAdduCount, "addu $zero, $zero, $zero\n") + - "nop\n" - "3:\n" - ".dword 0x0123456789ABCDEF\n"; - DriverStr(expected, "FarLongLiteralAlignmentNop"); - EXPECT_EQ(__ GetLabelLocation(literal->GetLabel()), (5 + kAdduCount) * 4); -} - TEST_F(AssemblerMIPS64Test, Addu) { DriverStr(RepeatRRR(&mips64::Mips64Assembler::Addu, "addu ${reg1}, ${reg2}, ${reg3}"), "addu"); } @@ -2740,6 +2633,235 @@ TEST_F(AssemblerMIPS64Test, LoadConst64) { EXPECT_EQ(tester.GetPathsCovered(), art::mips64::kLoadConst64PathAllPaths); } +TEST_F(AssemblerMIPS64Test, LoadFarthestNearLabelAddress) { + mips64::Mips64Label label; + __ LoadLabelAddress(mips64::V0, &label); + constexpr uint32_t kAdduCount = 0x3FFDE; + for (uint32_t i = 0; i != kAdduCount; ++i) { + __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO); + } + __ Bind(&label); + + std::string expected = + "lapc $v0, 1f\n" + + RepeatInsn(kAdduCount, "addu $zero, $zero, $zero\n") + + "1:\n"; + DriverStr(expected, "LoadFarthestNearLabelAddress"); + EXPECT_EQ(__ GetLabelLocation(&label), (1 + kAdduCount) * 4); +} + +TEST_F(AssemblerMIPS64Test, LoadNearestFarLabelAddress) { + mips64::Mips64Label label; + __ LoadLabelAddress(mips64::V0, &label); + constexpr uint32_t kAdduCount = 0x3FFDF; + for (uint32_t i = 0; i != kAdduCount; ++i) { + __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO); + } + __ Bind(&label); + + std::string expected = + "1:\n" + "auipc $at, %hi(2f - 1b)\n" + "daddiu $v0, $at, %lo(2f - 1b)\n" + + RepeatInsn(kAdduCount, "addu $zero, $zero, $zero\n") + + "2:\n"; + DriverStr(expected, "LoadNearestFarLabelAddress"); + EXPECT_EQ(__ GetLabelLocation(&label), (2 + kAdduCount) * 4); +} + +TEST_F(AssemblerMIPS64Test, LoadFarthestNearLiteral) { + mips64::Literal* literal = __ NewLiteral<uint32_t>(0x12345678); + __ LoadLiteral(mips64::V0, mips64::kLoadWord, literal); + constexpr uint32_t kAdduCount = 0x3FFDE; + for (uint32_t i = 0; i != kAdduCount; ++i) { + __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO); + } + + std::string expected = + "lwpc $v0, 1f\n" + + RepeatInsn(kAdduCount, "addu $zero, $zero, $zero\n") + + "1:\n" + ".word 0x12345678\n"; + DriverStr(expected, "LoadFarthestNearLiteral"); + EXPECT_EQ(__ GetLabelLocation(literal->GetLabel()), (1 + kAdduCount) * 4); +} + +TEST_F(AssemblerMIPS64Test, LoadNearestFarLiteral) { + mips64::Literal* literal = __ NewLiteral<uint32_t>(0x12345678); + __ LoadLiteral(mips64::V0, mips64::kLoadWord, literal); + constexpr uint32_t kAdduCount = 0x3FFDF; + for (uint32_t i = 0; i != kAdduCount; ++i) { + __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO); + } + + std::string expected = + "1:\n" + "auipc $at, %hi(2f - 1b)\n" + "lw $v0, %lo(2f - 1b)($at)\n" + + RepeatInsn(kAdduCount, "addu $zero, $zero, $zero\n") + + "2:\n" + ".word 0x12345678\n"; + DriverStr(expected, "LoadNearestFarLiteral"); + EXPECT_EQ(__ GetLabelLocation(literal->GetLabel()), (2 + kAdduCount) * 4); +} + +TEST_F(AssemblerMIPS64Test, LoadFarthestNearLiteralUnsigned) { + mips64::Literal* literal = __ NewLiteral<uint32_t>(0x12345678); + __ LoadLiteral(mips64::V0, mips64::kLoadUnsignedWord, literal); + constexpr uint32_t kAdduCount = 0x3FFDE; + for (uint32_t i = 0; i != kAdduCount; ++i) { + __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO); + } + + std::string expected = + "lwupc $v0, 1f\n" + + RepeatInsn(kAdduCount, "addu $zero, $zero, $zero\n") + + "1:\n" + ".word 0x12345678\n"; + DriverStr(expected, "LoadFarthestNearLiteralUnsigned"); + EXPECT_EQ(__ GetLabelLocation(literal->GetLabel()), (1 + kAdduCount) * 4); +} + +TEST_F(AssemblerMIPS64Test, LoadNearestFarLiteralUnsigned) { + mips64::Literal* literal = __ NewLiteral<uint32_t>(0x12345678); + __ LoadLiteral(mips64::V0, mips64::kLoadUnsignedWord, literal); + constexpr uint32_t kAdduCount = 0x3FFDF; + for (uint32_t i = 0; i != kAdduCount; ++i) { + __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO); + } + + std::string expected = + "1:\n" + "auipc $at, %hi(2f - 1b)\n" + "lwu $v0, %lo(2f - 1b)($at)\n" + + RepeatInsn(kAdduCount, "addu $zero, $zero, $zero\n") + + "2:\n" + ".word 0x12345678\n"; + DriverStr(expected, "LoadNearestFarLiteralUnsigned"); + EXPECT_EQ(__ GetLabelLocation(literal->GetLabel()), (2 + kAdduCount) * 4); +} + +TEST_F(AssemblerMIPS64Test, LoadFarthestNearLiteralLong) { + mips64::Literal* literal = __ NewLiteral<uint64_t>(UINT64_C(0x0123456789ABCDEF)); + __ LoadLiteral(mips64::V0, mips64::kLoadDoubleword, literal); + constexpr uint32_t kAdduCount = 0x3FFDD; + for (uint32_t i = 0; i != kAdduCount; ++i) { + __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO); + } + + std::string expected = + "ldpc $v0, 1f\n" + + RepeatInsn(kAdduCount, "addu $zero, $zero, $zero\n") + + "1:\n" + ".dword 0x0123456789ABCDEF\n"; + DriverStr(expected, "LoadFarthestNearLiteralLong"); + EXPECT_EQ(__ GetLabelLocation(literal->GetLabel()), (1 + kAdduCount) * 4); +} + +TEST_F(AssemblerMIPS64Test, LoadNearestFarLiteralLong) { + mips64::Literal* literal = __ NewLiteral<uint64_t>(UINT64_C(0x0123456789ABCDEF)); + __ LoadLiteral(mips64::V0, mips64::kLoadDoubleword, literal); + constexpr uint32_t kAdduCount = 0x3FFDE; + for (uint32_t i = 0; i != kAdduCount; ++i) { + __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO); + } + + std::string expected = + "1:\n" + "auipc $at, %hi(2f - 1b)\n" + "ld $v0, %lo(2f - 1b)($at)\n" + + RepeatInsn(kAdduCount, "addu $zero, $zero, $zero\n") + + "2:\n" + ".dword 0x0123456789ABCDEF\n"; + DriverStr(expected, "LoadNearestFarLiteralLong"); + EXPECT_EQ(__ GetLabelLocation(literal->GetLabel()), (2 + kAdduCount) * 4); +} + +TEST_F(AssemblerMIPS64Test, LongLiteralAlignmentNop) { + mips64::Literal* literal1 = __ NewLiteral<uint64_t>(UINT64_C(0x0123456789ABCDEF)); + mips64::Literal* literal2 = __ NewLiteral<uint64_t>(UINT64_C(0x5555555555555555)); + mips64::Literal* literal3 = __ NewLiteral<uint64_t>(UINT64_C(0xAAAAAAAAAAAAAAAA)); + __ LoadLiteral(mips64::A1, mips64::kLoadDoubleword, literal1); + __ LoadLiteral(mips64::A2, mips64::kLoadDoubleword, literal2); + __ LoadLiteral(mips64::A3, mips64::kLoadDoubleword, literal3); + __ LoadLabelAddress(mips64::V0, literal1->GetLabel()); + __ LoadLabelAddress(mips64::V1, literal2->GetLabel()); + // A nop will be inserted here before the 64-bit literals. + + std::string expected = + "ldpc $a1, 1f\n" + // The GNU assembler incorrectly requires the ldpc instruction to be located + // at an address that's a multiple of 8. TODO: Remove this workaround if/when + // the assembler is fixed. + // "ldpc $a2, 2f\n" + ".word 0xECD80004\n" + "ldpc $a3, 3f\n" + "lapc $v0, 1f\n" + "lapc $v1, 2f\n" + "nop\n" + "1:\n" + ".dword 0x0123456789ABCDEF\n" + "2:\n" + ".dword 0x5555555555555555\n" + "3:\n" + ".dword 0xAAAAAAAAAAAAAAAA\n"; + DriverStr(expected, "LongLiteralAlignmentNop"); + EXPECT_EQ(__ GetLabelLocation(literal1->GetLabel()), 6 * 4u); + EXPECT_EQ(__ GetLabelLocation(literal2->GetLabel()), 8 * 4u); + EXPECT_EQ(__ GetLabelLocation(literal3->GetLabel()), 10 * 4u); +} + +TEST_F(AssemblerMIPS64Test, LongLiteralAlignmentNoNop) { + mips64::Literal* literal1 = __ NewLiteral<uint64_t>(UINT64_C(0x0123456789ABCDEF)); + mips64::Literal* literal2 = __ NewLiteral<uint64_t>(UINT64_C(0x5555555555555555)); + __ LoadLiteral(mips64::A1, mips64::kLoadDoubleword, literal1); + __ LoadLiteral(mips64::A2, mips64::kLoadDoubleword, literal2); + __ LoadLabelAddress(mips64::V0, literal1->GetLabel()); + __ LoadLabelAddress(mips64::V1, literal2->GetLabel()); + + std::string expected = + "ldpc $a1, 1f\n" + // The GNU assembler incorrectly requires the ldpc instruction to be located + // at an address that's a multiple of 8. TODO: Remove this workaround if/when + // the assembler is fixed. + // "ldpc $a2, 2f\n" + ".word 0xECD80003\n" + "lapc $v0, 1f\n" + "lapc $v1, 2f\n" + "1:\n" + ".dword 0x0123456789ABCDEF\n" + "2:\n" + ".dword 0x5555555555555555\n"; + DriverStr(expected, "LongLiteralAlignmentNoNop"); + EXPECT_EQ(__ GetLabelLocation(literal1->GetLabel()), 4 * 4u); + EXPECT_EQ(__ GetLabelLocation(literal2->GetLabel()), 6 * 4u); +} + +TEST_F(AssemblerMIPS64Test, FarLongLiteralAlignmentNop) { + mips64::Literal* literal = __ NewLiteral<uint64_t>(UINT64_C(0x0123456789ABCDEF)); + __ LoadLiteral(mips64::V0, mips64::kLoadDoubleword, literal); + __ LoadLabelAddress(mips64::V1, literal->GetLabel()); + constexpr uint32_t kAdduCount = 0x3FFDF; + for (uint32_t i = 0; i != kAdduCount; ++i) { + __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO); + } + // A nop will be inserted here before the 64-bit literal. + + std::string expected = + "1:\n" + "auipc $at, %hi(3f - 1b)\n" + "ld $v0, %lo(3f - 1b)($at)\n" + "2:\n" + "auipc $at, %hi(3f - 2b)\n" + "daddiu $v1, $at, %lo(3f - 2b)\n" + + RepeatInsn(kAdduCount, "addu $zero, $zero, $zero\n") + + "nop\n" + "3:\n" + ".dword 0x0123456789ABCDEF\n"; + DriverStr(expected, "FarLongLiteralAlignmentNop"); + EXPECT_EQ(__ GetLabelLocation(literal->GetLabel()), (5 + kAdduCount) * 4); +} + // MSA instructions. TEST_F(AssemblerMIPS64Test, AndV) { |