Merge "MIPS: Eliminate hard-coded offsets in branches"
am: f708c9a392
Change-Id: I921521821f99268f9cf2ab7040e15df1af15eb9a
diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc
index b6eb5c1..2e78af5 100644
--- a/compiler/optimizing/code_generator_mips.cc
+++ b/compiler/optimizing/code_generator_mips.cc
@@ -6573,7 +6573,8 @@
DCHECK(!label_low);
__ AddUpper(base, obj, offset_high);
}
- __ Beqz(T9, (isR6 ? 2 : 4)); // Skip jialc / addiu+jalr+nop.
+ MipsLabel skip_call;
+ __ Beqz(T9, &skip_call, /* is_bare */ true);
if (label_low != nullptr) {
DCHECK(short_offset);
__ Bind(label_low);
@@ -6588,6 +6589,7 @@
__ Jalr(T9);
__ Nop();
}
+ __ Bind(&skip_call);
__ SetReorder(reordering);
} else {
// Note that we do not actually check the value of `GetIsGcMarking()`
@@ -6724,27 +6726,31 @@
__ LoadFromOffset(kLoadWord, T9, TR, entry_point_offset);
Register ref_reg = ref.AsRegister<Register>();
Register base = short_offset ? obj : TMP;
+ MipsLabel skip_call;
if (short_offset) {
if (isR6) {
- __ Beqzc(T9, 2); // Skip jialc.
+ __ Beqzc(T9, &skip_call, /* is_bare */ true);
__ Nop(); // In forbidden slot.
__ Jialc(T9, thunk_disp);
} else {
- __ Beqz(T9, 3); // Skip jalr+nop.
+ __ Beqz(T9, &skip_call, /* is_bare */ true);
__ Addiu(T9, T9, thunk_disp); // In delay slot.
__ Jalr(T9);
__ Nop(); // In delay slot.
}
+ __ Bind(&skip_call);
} else {
if (isR6) {
- __ Beqz(T9, 2); // Skip jialc.
+ __ Beqz(T9, &skip_call, /* is_bare */ true);
__ Aui(base, obj, offset_high); // In delay slot.
__ Jialc(T9, thunk_disp);
+ __ Bind(&skip_call);
} else {
__ Lui(base, offset_high);
- __ Beqz(T9, 2); // Skip jalr.
+ __ Beqz(T9, &skip_call, /* is_bare */ true);
__ Addiu(T9, T9, thunk_disp); // In delay slot.
__ Jalr(T9);
+ __ Bind(&skip_call);
__ Addu(base, base, obj); // In delay slot.
}
}
@@ -6826,15 +6832,18 @@
Register index_reg = index.IsRegisterPair()
? index.AsRegisterPairLow<Register>()
: index.AsRegister<Register>();
+ MipsLabel skip_call;
if (GetInstructionSetFeatures().IsR6()) {
- __ Beqz(T9, 2); // Skip jialc.
+ __ Beqz(T9, &skip_call, /* is_bare */ true);
__ Lsa(TMP, index_reg, obj, scale_factor); // In delay slot.
__ Jialc(T9, thunk_disp);
+ __ Bind(&skip_call);
} else {
__ Sll(TMP, index_reg, scale_factor);
- __ Beqz(T9, 2); // Skip jalr.
+ __ Beqz(T9, &skip_call, /* is_bare */ true);
__ Addiu(T9, T9, thunk_disp); // In delay slot.
__ Jalr(T9);
+ __ Bind(&skip_call);
__ Addu(TMP, TMP, obj); // In delay slot.
}
// /* HeapReference<Object> */ ref = *(obj + data_offset + (index << scale_factor))
diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc
index 3e79f47..1d59694 100644
--- a/compiler/optimizing/code_generator_mips64.cc
+++ b/compiler/optimizing/code_generator_mips64.cc
@@ -4490,7 +4490,8 @@
DCHECK(!label_low);
__ Daui(base, obj, offset_high);
}
- __ Beqz(T9, 2); // Skip jialc.
+ Mips64Label skip_call;
+ __ Beqz(T9, &skip_call, /* is_bare */ true);
if (label_low != nullptr) {
DCHECK(short_offset);
__ Bind(label_low);
@@ -4499,6 +4500,7 @@
__ LoadFromOffset(kLoadUnsignedWord, root_reg, base, offset_low); // Single instruction
// in delay slot.
__ Jialc(T9, thunk_disp);
+ __ Bind(&skip_call);
} else {
// Note that we do not actually check the value of `GetIsGcMarking()`
// to decide whether to mark the loaded GC root or not. Instead, we
@@ -4617,18 +4619,21 @@
// threads are suspended or running a checkpoint.
__ LoadFromOffset(kLoadDoubleword, T9, TR, entry_point_offset);
GpuRegister ref_reg = ref.AsRegister<GpuRegister>();
+ Mips64Label skip_call;
if (short_offset) {
- __ Beqzc(T9, 2); // Skip jialc.
+ __ Beqzc(T9, &skip_call, /* is_bare */ true);
__ Nop(); // In forbidden slot.
__ Jialc(T9, thunk_disp);
+ __ Bind(&skip_call);
// /* HeapReference<Object> */ ref = *(obj + offset)
__ LoadFromOffset(kLoadUnsignedWord, ref_reg, obj, offset); // Single instruction.
} else {
int16_t offset_low = Low16Bits(offset);
int16_t offset_high = High16Bits(offset - offset_low); // Accounts for sign extension in lwu.
- __ Beqz(T9, 2); // Skip jialc.
+ __ Beqz(T9, &skip_call, /* is_bare */ true);
__ Daui(TMP, obj, offset_high); // In delay slot.
__ Jialc(T9, thunk_disp);
+ __ Bind(&skip_call);
// /* HeapReference<Object> */ ref = *(obj + offset)
__ LoadFromOffset(kLoadUnsignedWord, ref_reg, TMP, offset_low); // Single instruction.
}
@@ -4702,11 +4707,13 @@
// Loading the entrypoint does not require a load acquire since it is only changed when
// threads are suspended or running a checkpoint.
__ LoadFromOffset(kLoadDoubleword, T9, TR, entry_point_offset);
- __ Beqz(T9, 2); // Skip jialc.
+ Mips64Label skip_call;
+ __ Beqz(T9, &skip_call, /* is_bare */ true);
GpuRegister ref_reg = ref.AsRegister<GpuRegister>();
GpuRegister index_reg = index.AsRegister<GpuRegister>();
__ Dlsa(TMP, index_reg, obj, scale_factor); // In delay slot.
__ Jialc(T9, thunk_disp);
+ __ Bind(&skip_call);
// /* HeapReference<Object> */ ref = *(obj + data_offset + (index << scale_factor))
DCHECK(IsInt<16>(static_cast<int32_t>(data_offset))) << data_offset;
__ LoadFromOffset(kLoadUnsignedWord, ref_reg, TMP, data_offset); // Single instruction.
diff --git a/compiler/optimizing/optimizing_cfi_test_expected.inc b/compiler/optimizing/optimizing_cfi_test_expected.inc
index 77a63ac..fde55cb 100644
--- a/compiler/optimizing/optimizing_cfi_test_expected.inc
+++ b/compiler/optimizing/optimizing_cfi_test_expected.inc
@@ -148,27 +148,27 @@
0x48, 0x0A, 0x44, 0xDF, 0x44, 0xD1, 0x44, 0xD0, 0x50, 0x0E, 0x00, 0x0B,
0x0E, 0x40,
};
-// 0x00000000: addiu r29, r29, -64
+// 0x00000000: addiu sp, sp, -64
// 0x00000004: .cfi_def_cfa_offset: 64
-// 0x00000004: sw r31, +60(r29)
+// 0x00000004: sw ra, +60(sp)
// 0x00000008: .cfi_offset: r31 at cfa-4
-// 0x00000008: sw r17, +56(r29)
+// 0x00000008: sw s1, +56(sp)
// 0x0000000c: .cfi_offset: r17 at cfa-8
-// 0x0000000c: sw r16, +52(r29)
+// 0x0000000c: sw s0, +52(sp)
// 0x00000010: .cfi_offset: r16 at cfa-12
-// 0x00000010: sdc1 f22, +40(r29)
-// 0x00000014: sdc1 f20, +32(r29)
+// 0x00000010: sdc1 f22, +40(sp)
+// 0x00000014: sdc1 f20, +32(sp)
// 0x00000018: .cfi_remember_state
-// 0x00000018: lw r31, +60(r29)
+// 0x00000018: lw ra, +60(sp)
// 0x0000001c: .cfi_restore: r31
-// 0x0000001c: lw r17, +56(r29)
+// 0x0000001c: lw s1, +56(sp)
// 0x00000020: .cfi_restore: r17
-// 0x00000020: lw r16, +52(r29)
+// 0x00000020: lw s0, +52(sp)
// 0x00000024: .cfi_restore: r16
-// 0x00000024: ldc1 f22, +40(r29)
-// 0x00000028: ldc1 f20, +32(r29)
-// 0x0000002c: jr r31
-// 0x00000030: addiu r29, r29, 64
+// 0x00000024: ldc1 f22, +40(sp)
+// 0x00000028: ldc1 f20, +32(sp)
+// 0x0000002c: jr ra
+// 0x00000030: addiu sp, sp, 64
// 0x00000034: .cfi_def_cfa_offset: 0
// 0x00000034: .cfi_restore_state
// 0x00000034: .cfi_def_cfa_offset: 64
@@ -185,32 +185,32 @@
0x44, 0xB9, 0x08, 0x44, 0xB8, 0x0A, 0x0A, 0x44, 0xDF, 0x44, 0xD1, 0x44,
0xD0, 0x44, 0xF9, 0x44, 0xF8, 0x44, 0x0E, 0x00, 0x44, 0x0B, 0x0E, 0x40,
};
-// 0x00000000: daddiu r29, r29, -64
+// 0x00000000: daddiu sp, sp, -64
// 0x00000004: .cfi_def_cfa_offset: 64
-// 0x00000004: sd r31, +56(r29)
+// 0x00000004: sd ra, +56(sp)
// 0x00000008: .cfi_offset: r31 at cfa-8
-// 0x00000008: sd r17, +48(r29)
+// 0x00000008: sd s1, +48(sp)
// 0x0000000c: .cfi_offset: r17 at cfa-16
-// 0x0000000c: sd r16, +40(r29)
+// 0x0000000c: sd s0, +40(sp)
// 0x00000010: .cfi_offset: r16 at cfa-24
-// 0x00000010: sdc1 f25, +32(r29)
+// 0x00000010: sdc1 f25, +32(sp)
// 0x00000014: .cfi_offset: r57 at cfa-32
-// 0x00000014: sdc1 f24, +24(r29)
+// 0x00000014: sdc1 f24, +24(sp)
// 0x00000018: .cfi_offset: r56 at cfa-40
// 0x00000018: .cfi_remember_state
-// 0x00000018: ld r31, +56(r29)
+// 0x00000018: ld ra, +56(sp)
// 0x0000001c: .cfi_restore: r31
-// 0x0000001c: ld r17, +48(r29)
+// 0x0000001c: ld s1, +48(sp)
// 0x00000020: .cfi_restore: r17
-// 0x00000020: ld r16, +40(r29)
+// 0x00000020: ld s0, +40(sp)
// 0x00000024: .cfi_restore: r16
-// 0x00000024: ldc1 f25, +32(r29)
+// 0x00000024: ldc1 f25, +32(sp)
// 0x00000028: .cfi_restore: r57
-// 0x00000028: ldc1 f24, +24(r29)
+// 0x00000028: ldc1 f24, +24(sp)
// 0x0000002c: .cfi_restore: r56
-// 0x0000002c: daddiu r29, r29, 64
+// 0x0000002c: daddiu sp, sp, 64
// 0x00000030: .cfi_def_cfa_offset: 0
-// 0x00000030: jic r31, 0
+// 0x00000030: jic ra, 0
// 0x00000034: .cfi_restore_state
// 0x00000034: .cfi_def_cfa_offset: 64
@@ -330,7 +330,7 @@
static constexpr uint8_t expected_asm_kMips_adjust_head[] = {
0xC0, 0xFF, 0xBD, 0x27, 0x3C, 0x00, 0xBF, 0xAF, 0x38, 0x00, 0xB1, 0xAF,
0x34, 0x00, 0xB0, 0xAF, 0x28, 0x00, 0xB6, 0xF7, 0x20, 0x00, 0xB4, 0xF7,
- 0x08, 0x00, 0x04, 0x14, 0xFC, 0xFF, 0xBD, 0x27,
+ 0x08, 0x00, 0x80, 0x14, 0xFC, 0xFF, 0xBD, 0x27,
0x00, 0x00, 0xBF, 0xAF, 0x00, 0x00, 0x10, 0x04, 0x02, 0x00, 0x01, 0x3C,
0x18, 0x00, 0x21, 0x34, 0x21, 0x08, 0x3F, 0x00, 0x00, 0x00, 0xBF, 0x8F,
0x09, 0x00, 0x20, 0x00, 0x04, 0x00, 0xBD, 0x27,
@@ -345,42 +345,42 @@
0x50, 0x0E, 0x44, 0x60, 0x0E, 0x40, 0x04, 0x04, 0x00, 0x02, 0x00, 0x0A,
0x44, 0xDF, 0x44, 0xD1, 0x44, 0xD0, 0x50, 0x0E, 0x00, 0x0B, 0x0E, 0x40,
};
-// 0x00000000: addiu r29, r29, -64
+// 0x00000000: addiu sp, sp, -64
// 0x00000004: .cfi_def_cfa_offset: 64
-// 0x00000004: sw r31, +60(r29)
+// 0x00000004: sw ra, +60(sp)
// 0x00000008: .cfi_offset: r31 at cfa-4
-// 0x00000008: sw r17, +56(r29)
+// 0x00000008: sw s1, +56(sp)
// 0x0000000c: .cfi_offset: r17 at cfa-8
-// 0x0000000c: sw r16, +52(r29)
+// 0x0000000c: sw s0, +52(sp)
// 0x00000010: .cfi_offset: r16 at cfa-12
-// 0x00000010: sdc1 f22, +40(r29)
-// 0x00000014: sdc1 f20, +32(r29)
-// 0x00000018: bne r0, r4, 0x00000040 ; +36
-// 0x0000001c: addiu r29, r29, -4
+// 0x00000010: sdc1 f22, +40(sp)
+// 0x00000014: sdc1 f20, +32(sp)
+// 0x00000018: bnez a0, 0x0000003c ; +36
+// 0x0000001c: addiu sp, sp, -4
// 0x00000020: .cfi_def_cfa_offset: 68
-// 0x00000020: sw r31, +0(r29)
-// 0x00000024: bltzal r0, 0x0000002c ; +4
-// 0x00000028: lui r1, 0x20000
-// 0x0000002c: ori r1, r1, 24
-// 0x00000030: addu r1, r1, r31
-// 0x00000034: lw r31, +0(r29)
-// 0x00000038: jr r1
-// 0x0000003c: addiu r29, r29, 4
+// 0x00000020: sw ra, +0(sp)
+// 0x00000024: nal
+// 0x00000028: lui at, 2
+// 0x0000002c: ori at, at, 24
+// 0x00000030: addu at, at, ra
+// 0x00000034: lw ra, +0(sp)
+// 0x00000038: jr at
+// 0x0000003c: addiu sp, sp, 4
// 0x00000040: .cfi_def_cfa_offset: 64
// 0x00000040: nop
// ...
// 0x00020040: nop
// 0x00020044: .cfi_remember_state
-// 0x00020044: lw r31, +60(r29)
+// 0x00020044: lw ra, +60(sp)
// 0x00020048: .cfi_restore: r31
-// 0x00020048: lw r17, +56(r29)
+// 0x00020048: lw s1, +56(sp)
// 0x0002004c: .cfi_restore: r17
-// 0x0002004c: lw r16, +52(r29)
+// 0x0002004c: lw s0, +52(sp)
// 0x00020050: .cfi_restore: r16
-// 0x00020050: ldc1 f22, +40(r29)
-// 0x00020054: ldc1 f20, +32(r29)
-// 0x00020058: jr r31
-// 0x0002005c: addiu r29, r29, 64
+// 0x00020050: ldc1 f22, +40(sp)
+// 0x00020054: ldc1 f20, +32(sp)
+// 0x00020058: jr ra
+// 0x0002005c: addiu sp, sp, 64
// 0x00020060: .cfi_def_cfa_offset: 0
// 0x00020060: .cfi_restore_state
// 0x00020060: .cfi_def_cfa_offset: 64
@@ -401,37 +401,37 @@
0x44, 0xDF, 0x44, 0xD1, 0x44, 0xD0, 0x44, 0xF9, 0x44, 0xF8, 0x44, 0x0E,
0x00, 0x44, 0x0B, 0x0E, 0x40,
};
-// 0x00000000: daddiu r29, r29, -64
+// 0x00000000: daddiu sp, sp, -64
// 0x00000004: .cfi_def_cfa_offset: 64
-// 0x00000004: sd r31, +56(r29)
+// 0x00000004: sd ra, +56(sp)
// 0x00000008: .cfi_offset: r31 at cfa-8
-// 0x00000008: sd r17, +48(r29)
+// 0x00000008: sd s1, +48(sp)
// 0x0000000c: .cfi_offset: r17 at cfa-16
-// 0x0000000c: sd r16, +40(r29)
+// 0x0000000c: sd s0, +40(sp)
// 0x00000010: .cfi_offset: r16 at cfa-24
-// 0x00000010: sdc1 f25, +32(r29)
+// 0x00000010: sdc1 f25, +32(sp)
// 0x00000014: .cfi_offset: r57 at cfa-32
-// 0x00000014: sdc1 f24, +24(r29)
+// 0x00000014: sdc1 f24, +24(sp)
// 0x00000018: .cfi_offset: r56 at cfa-40
-// 0x00000018: bnec r5, r6, 0x00000024 ; +12
-// 0x0000001c: auipc r1, 2
-// 0x00000020: jic r1, 12 ; bc 0x00020028 ; +131080
+// 0x00000018: bnec a1, a2, 0x00000024 ; +12
+// 0x0000001c: auipc at, 2
+// 0x00000020: jic at, 12 ; bc 0x00020028 ; +131080
// 0x00000024: nop
// ...
// 0x00020024: nop
// 0x00020028: .cfi_remember_state
-// 0x00020028: ld r31, +56(r29)
+// 0x00020028: ld ra, +56(sp)
// 0x0002002c: .cfi_restore: r31
-// 0x0002002c: ld r17, +48(r29)
+// 0x0002002c: ld s1, +48(sp)
// 0x00020030: .cfi_restore: r17
-// 0x00020030: ld r16, +40(r29)
+// 0x00020030: ld s0, +40(sp)
// 0x00020034: .cfi_restore: r16
-// 0x00020034: ldc1 f25, +32(r29)
+// 0x00020034: ldc1 f25, +32(sp)
// 0x00020038: .cfi_restore: r57
-// 0x00020038: ldc1 f24, +24(r29)
+// 0x00020038: ldc1 f24, +24(sp)
// 0x0002003c: .cfi_restore: r56
-// 0x0002003c: daddiu r29, r29, 64
+// 0x0002003c: daddiu sp, sp, 64
// 0x00020040: .cfi_def_cfa_offset: 0
-// 0x00020040: jic r31, 0
+// 0x00020040: jic ra, 0
// 0x00020044: .cfi_restore_state
// 0x00020044: .cfi_def_cfa_offset: 64
diff --git a/compiler/utils/mips/assembler_mips.cc b/compiler/utils/mips/assembler_mips.cc
index 2cbabcf..18099d8 100644
--- a/compiler/utils/mips/assembler_mips.cc
+++ b/compiler/utils/mips/assembler_mips.cc
@@ -935,11 +935,11 @@
}
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::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 @@
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 @@
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 @@
}
}
-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 @@
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 @@
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 @@
// 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 @@
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 @@
}
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::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 @@
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 @@
}
// 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::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::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 @@
// 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 @@
{ 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 @@
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 @@
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 @@
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::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::Bc1eqz(FRegister ft, MipsLabel* label) {
- Bcond(label, kCondF, static_cast<Register>(ft), ZERO);
+void MipsAssembler::Bc(MipsLabel* label, bool is_bare) {
+ Buncond(label, /* is_r6 */ true, is_bare);
}
-void MipsAssembler::Bc1nez(FRegister ft, MipsLabel* label) {
- Bcond(label, kCondT, static_cast<Register>(ft), ZERO);
+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, bool is_bare) {
+ Bcond(label, /* is_r6 */ true, is_bare, kCondF, 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 a7ff931..7f9d576 100644
--- a/compiler/utils/mips/assembler_mips.h
+++ b/compiler/utils/mips/assembler_mips.h
@@ -636,29 +636,69 @@
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 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 @@
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 @@
// 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 @@
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 @@
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 @@
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 b72a14e..6e52b17 100644
--- a/compiler/utils/mips/assembler_mips32r6_test.cc
+++ b/compiler/utils/mips/assembler_mips32r6_test.cc
@@ -259,12 +259,52 @@
return result;
}
- void BranchCondTwoRegsHelper(void (mips::MipsAssembler::*f)(mips::Register,
- mips::Register,
- mips::MipsLabel*),
- const std::string& instr_name) {
+ 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, mips::A1, &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);
@@ -274,17 +314,86 @@
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::A1, &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 + " $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 + " $a2, $a3, 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*,
+ 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::F0, &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::F30, &label, is_bare);
+ __ Addu(mips::ZERO, mips::ZERO, mips::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 + " $f30, 1b\n" +
+ (is_bare ? "" : "nop\n") +
+ "addu $zero, $zero, $zero\n";
DriverStr(expected, instr_name);
}
@@ -947,78 +1056,386 @@
DriverStr(expected, "StoreQToOffset");
}
-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");
-}
-
//////////////
// 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;
+ __ 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);
+
+ 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, 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;
+ __ 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);
+
+ 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(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);
+
+ 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.
+
+ 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.
+
+ 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, 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"
+ "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");
+}
+
TEST_F(AssemblerMIPS32r6Test, ImpossibleReordering) {
mips::MipsLabel label;
__ SetReorder(true);
@@ -1154,43 +1571,80 @@
"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
+///////////////////////
+// Loading Constants //
+///////////////////////
-// 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
+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 0917530..d9bf0b8 100644
--- a/compiler/utils/mips/assembler_mips_test.cc
+++ b/compiler/utils/mips/assembler_mips_test.cc
@@ -186,11 +186,51 @@
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,31 @@
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);
+ (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);
@@ -229,17 +274,52 @@
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::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"
- "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 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)(0, &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)(7, &label, is_bare);
+ __ Addu(mips::ZERO, mips::ZERO, mips::ZERO);
+
+ std::string expected =
+ ".set noreorder\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 + " $fcc7, 1b\n" +
+ (is_bare ? "" : "nop\n") +
+ "addu $zero, $zero, $zero\n";
DriverStr(expected, instr_name);
}
@@ -2072,410 +2152,136 @@
DriverStr(expected, "StoreConstToOffset");
}
-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);
+//////////////
+// BRANCHES //
+//////////////
- 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, B) {
+ BranchHelper(&mips::MipsAssembler::B, "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);
-
- 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");
+ BranchFpuCondCodeHelper(&mips::MipsAssembler::Bc1t, "Bc1t");
}
-///////////////////////
-// 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, BareB) {
+ BranchHelper(&mips::MipsAssembler::B, "B", /* 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);
-
- 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, BareBal) {
+ BranchHelper(&mips::MipsAssembler::Bal, "Bal", /* 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);
-
- 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, BareBeq) {
+ BranchCondTwoRegsHelper(&mips::MipsAssembler::Beq, "Beq", /* 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);
- }
-
- 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, BareBne) {
+ BranchCondTwoRegsHelper(&mips::MipsAssembler::Bne, "Bne", /* 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, BareBeqz) {
+ BranchCondOneRegHelper(&mips::MipsAssembler::Beqz, "Beqz", /* 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, BareBnez) {
+ BranchCondOneRegHelper(&mips::MipsAssembler::Bnez, "Bnez", /* is_bare */ true);
+}
+
+TEST_F(AssemblerMIPSTest, BareBltz) {
+ BranchCondOneRegHelper(&mips::MipsAssembler::Bltz, "Bltz", /* is_bare */ true);
+}
+
+TEST_F(AssemblerMIPSTest, BareBgez) {
+ BranchCondOneRegHelper(&mips::MipsAssembler::Bgez, "Bgez", /* is_bare */ true);
+}
+
+TEST_F(AssemblerMIPSTest, BareBlez) {
+ BranchCondOneRegHelper(&mips::MipsAssembler::Blez, "Blez", /* is_bare */ true);
+}
+
+TEST_F(AssemblerMIPSTest, BareBgtz) {
+ BranchCondOneRegHelper(&mips::MipsAssembler::Bgtz, "Bgtz", /* is_bare */ true);
+}
+
+TEST_F(AssemblerMIPSTest, BareBlt) {
+ BranchCondTwoRegsHelper(&mips::MipsAssembler::Blt, "Blt", /* is_bare */ true);
+}
+
+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 @@
"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 @@
"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 @@
"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 @@
".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 @@
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 7a1beb6..3aa09fb 100644
--- a/compiler/utils/mips64/assembler_mips64.cc
+++ b/compiler/utils/mips64/assembler_mips64.cc
@@ -795,14 +795,42 @@
EmitFI(0x11, 0xD, ft, imm16);
}
-void Mips64Assembler::Beqz(GpuRegister rt, uint16_t imm16) {
- EmitI(0x4, ZERO, rt, imm16);
+void Mips64Assembler::Beq(GpuRegister rs, GpuRegister rt, uint16_t imm16) {
+ EmitI(0x4, rs, rt, imm16);
}
-void Mips64Assembler::EmitBcondc(BranchCondition cond,
- GpuRegister rs,
- GpuRegister rt,
- uint32_t imm16_21) {
+void Mips64Assembler::Bne(GpuRegister rs, GpuRegister rt, uint16_t imm16) {
+ EmitI(0x5, rs, rt, imm16);
+}
+
+void Mips64Assembler::Beqz(GpuRegister rt, uint16_t 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::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::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 @@
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 @@
}
}
-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 @@
// 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 @@
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 @@
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 @@
}
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::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 @@
}
// 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::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 @@
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 @@
// 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 @@
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 @@
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 @@
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) {
- Call(label);
+void Mips64Assembler::Balc(Mips64Label* label, bool is_bare) {
+ Call(label, is_bare);
}
-void Mips64Assembler::Bltc(GpuRegister rs, GpuRegister rt, Mips64Label* label) {
- Bcond(label, kCondLT, rs, rt);
+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) {
- Bcond(label, kCondLTZ, 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) {
- Bcond(label, kCondGTZ, 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) {
- Bcond(label, kCondGE, rs, 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) {
- Bcond(label, kCondGEZ, 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) {
- Bcond(label, kCondLEZ, 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) {
- Bcond(label, kCondLTU, rs, 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::Bgeuc(GpuRegister rs, GpuRegister rt, Mips64Label* label) {
- Bcond(label, kCondGEU, rs, rt);
+void Mips64Assembler::Bgeuc(GpuRegister rs, GpuRegister rt, Mips64Label* label, bool is_bare) {
+ Bcond(label, /* is_r6 */ true, is_bare, kCondGEU, rs, rt);
}
-void Mips64Assembler::Beqc(GpuRegister rs, GpuRegister rt, Mips64Label* label) {
- Bcond(label, kCondEQ, 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::Bnec(GpuRegister rs, GpuRegister rt, Mips64Label* label) {
- Bcond(label, kCondNE, rs, 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::Beqzc(GpuRegister rs, Mips64Label* label) {
- Bcond(label, kCondEQZ, rs);
+void Mips64Assembler::Beqzc(GpuRegister rs, Mips64Label* label, bool is_bare) {
+ Bcond(label, /* is_r6 */ true, is_bare, kCondEQZ, rs);
}
-void Mips64Assembler::Bnezc(GpuRegister rs, Mips64Label* label) {
- Bcond(label, kCondNEZ, rs);
+void Mips64Assembler::Bnezc(GpuRegister rs, Mips64Label* label, bool is_bare) {
+ Bcond(label, /* is_r6 */ true, is_bare, kCondNEZ, rs);
}
-void Mips64Assembler::Bc1eqz(FpuRegister ft, Mips64Label* label) {
- Bcond(label, kCondF, static_cast<GpuRegister>(ft), ZERO);
+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::Bc1nez(FpuRegister ft, Mips64Label* label) {
- Bcond(label, kCondT, static_cast<GpuRegister>(ft), ZERO);
+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::Bltz(GpuRegister rt, Mips64Label* label, bool is_bare) {
+ CHECK(is_bare);
+ Bcond(label, /* is_r6 */ false, is_bare, kCondLTZ, 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::Bgez(GpuRegister rt, Mips64Label* label, bool is_bare) {
+ CHECK(is_bare);
+ Bcond(label, /* is_r6 */ false, is_bare, kCondGEZ, 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::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::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::Beqz(GpuRegister rs, Mips64Label* label, bool is_bare) {
+ CHECK(is_bare);
+ Bcond(label, /* is_r6 */ false, is_bare, kCondEQZ, rs);
+}
+
+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 c39d120..023bcd6 100644
--- a/compiler/utils/mips64/assembler_mips64.h
+++ b/compiler/utils/mips64/assembler_mips64.h
@@ -563,7 +563,14 @@
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 @@
// 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 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 @@
// 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 @@
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 @@
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 @@
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 @@
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 @@
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 021e335..1541780 100644
--- a/compiler/utils/mips64/assembler_mips64_test.cc
+++ b/compiler/utils/mips64/assembler_mips64_test.cc
@@ -257,11 +257,46 @@
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 @@
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 @@
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,41 +741,258 @@
"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);
+TEST_F(AssemblerMIPS64Test, Bc) {
+ BranchHelper(&mips64::Mips64Assembler::Bc, "Bc");
+}
- std::string expected =
+TEST_F(AssemblerMIPS64Test, Balc) {
+ BranchHelper(&mips64::Mips64Assembler::Balc, "Balc");
+}
+
+TEST_F(AssemblerMIPS64Test, Beqzc) {
+ BranchCondOneRegHelper(&mips64::Mips64Assembler::Beqzc, "Beqzc");
+}
+
+TEST_F(AssemblerMIPS64Test, Bnezc) {
+ BranchCondOneRegHelper(&mips64::Mips64Assembler::Bnezc, "Bnezc");
+}
+
+TEST_F(AssemblerMIPS64Test, Bltzc) {
+ BranchCondOneRegHelper(&mips64::Mips64Assembler::Bltzc, "Bltzc");
+}
+
+TEST_F(AssemblerMIPS64Test, Bgezc) {
+ BranchCondOneRegHelper(&mips64::Mips64Assembler::Bgezc, "Bgezc");
+}
+
+TEST_F(AssemblerMIPS64Test, Blezc) {
+ BranchCondOneRegHelper(&mips64::Mips64Assembler::Blezc, "Blezc");
+}
+
+TEST_F(AssemblerMIPS64Test, Bgtzc) {
+ BranchCondOneRegHelper(&mips64::Mips64Assembler::Bgtzc, "Bgtzc");
+}
+
+TEST_F(AssemblerMIPS64Test, Beqc) {
+ BranchCondTwoRegsHelper(&mips64::Mips64Assembler::Beqc, "Beqc");
+}
+
+TEST_F(AssemblerMIPS64Test, Bnec) {
+ BranchCondTwoRegsHelper(&mips64::Mips64Assembler::Bnec, "Bnec");
+}
+
+TEST_F(AssemblerMIPS64Test, Bltc) {
+ BranchCondTwoRegsHelper(&mips64::Mips64Assembler::Bltc, "Bltc");
+}
+
+TEST_F(AssemblerMIPS64Test, Bgec) {
+ BranchCondTwoRegsHelper(&mips64::Mips64Assembler::Bgec, "Bgec");
+}
+
+TEST_F(AssemblerMIPS64Test, Bltuc) {
+ BranchCondTwoRegsHelper(&mips64::Mips64Assembler::Bltuc, "Bltuc");
+}
+
+TEST_F(AssemblerMIPS64Test, Bgeuc) {
+ BranchCondTwoRegsHelper(&mips64::Mips64Assembler::Bgeuc, "Bgeuc");
+}
+
+TEST_F(AssemblerMIPS64Test, Bc1eqz) {
+ BranchFpuCondHelper(&mips64::Mips64Assembler::Bc1eqz, "Bc1eqz");
+}
+
+TEST_F(AssemblerMIPS64Test, Bc1nez) {
+ BranchFpuCondHelper(&mips64::Mips64Assembler::Bc1nez, "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) {
+ mips64::Mips64Label label;
+ __ Beqc(mips64::A0, mips64::A1, &label);
+ constexpr uint32_t kAdduCount1 = (1u << 15) + 1;
+ for (uint32_t i = 0; i != kAdduCount1; ++i) {
+ __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO);
+ }
+ __ Bind(&label);
+ constexpr uint32_t kAdduCount2 = (1u << 15) + 1;
+ for (uint32_t i = 0; i != kAdduCount2; ++i) {
+ __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO);
+ }
+ __ Beqc(mips64::A2, mips64::A3, &label);
+
+ 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"
- "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");
+ "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(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) {
@@ -756,174 +1046,6 @@
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);
-
- 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, Beqzc) {
- BranchCondOneRegHelper(&mips64::Mips64Assembler::Beqzc, "Beqzc");
-}
-
-TEST_F(AssemblerMIPS64Test, Bnezc) {
- BranchCondOneRegHelper(&mips64::Mips64Assembler::Bnezc, "Bnezc");
-}
-
-TEST_F(AssemblerMIPS64Test, Bltzc) {
- BranchCondOneRegHelper(&mips64::Mips64Assembler::Bltzc, "Bltzc");
-}
-
-TEST_F(AssemblerMIPS64Test, Bgezc) {
- BranchCondOneRegHelper(&mips64::Mips64Assembler::Bgezc, "Bgezc");
-}
-
-TEST_F(AssemblerMIPS64Test, Blezc) {
- BranchCondOneRegHelper(&mips64::Mips64Assembler::Blezc, "Blezc");
-}
-
-TEST_F(AssemblerMIPS64Test, Bgtzc) {
- BranchCondOneRegHelper(&mips64::Mips64Assembler::Bgtzc, "Bgtzc");
-}
-
-TEST_F(AssemblerMIPS64Test, Beqc) {
- BranchCondTwoRegsHelper(&mips64::Mips64Assembler::Beqc, "Beqc");
-}
-
-TEST_F(AssemblerMIPS64Test, Bnec) {
- BranchCondTwoRegsHelper(&mips64::Mips64Assembler::Bnec, "Bnec");
-}
-
-TEST_F(AssemblerMIPS64Test, Bltc) {
- BranchCondTwoRegsHelper(&mips64::Mips64Assembler::Bltc, "Bltc");
-}
-
-TEST_F(AssemblerMIPS64Test, Bgec) {
- BranchCondTwoRegsHelper(&mips64::Mips64Assembler::Bgec, "Bgec");
-}
-
-TEST_F(AssemblerMIPS64Test, Bltuc) {
- BranchCondTwoRegsHelper(&mips64::Mips64Assembler::Bltuc, "Bltuc");
-}
-
-TEST_F(AssemblerMIPS64Test, Bgeuc) {
- BranchCondTwoRegsHelper(&mips64::Mips64Assembler::Bgeuc, "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");
-}
-
-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);
-
- 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, LongBeqc) {
- mips64::Mips64Label label;
- __ Beqc(mips64::A0, mips64::A1, &label);
- constexpr uint32_t kAdduCount1 = (1u << 15) + 1;
- for (uint32_t i = 0; i != kAdduCount1; ++i) {
- __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO);
- }
- __ Bind(&label);
- constexpr uint32_t kAdduCount2 = (1u << 15) + 1;
- for (uint32_t i = 0; i != kAdduCount2; ++i) {
- __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO);
- }
- __ Beqc(mips64::A2, mips64::A3, &label);
-
- 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");
-}
-
//////////
// MISC //
//////////
@@ -961,235 +1083,6 @@
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 @@
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) {
diff --git a/disassembler/disassembler_mips.cc b/disassembler/disassembler_mips.cc
index 1a395a4..938ea5d 100644
--- a/disassembler/disassembler_mips.cc
+++ b/disassembler/disassembler_mips.cc
@@ -112,6 +112,8 @@
{ kRTypeMask, 34, "sub", "DST", },
{ kRTypeMask, 35, "subu", "DST", },
{ kRTypeMask, 36, "and", "DST", },
+ { kRTypeMask | (0x1f << 16), 37 | (0 << 16), "move", "DS" },
+ { kRTypeMask | (0x1f << 21), 37 | (0 << 21), "move", "DT" },
{ kRTypeMask, 37, "or", "DST", },
{ kRTypeMask, 38, "xor", "DST", },
{ kRTypeMask, 39, "nor", "DST", },
@@ -214,13 +216,19 @@
{ kJTypeMask, 3 << kOpcodeShift, "jal", "L" },
// I-type instructions.
+ { kITypeMask | (0x3ff << 16), 4 << kOpcodeShift, "b", "B" },
+ { kITypeMask | (0x1f << 16), 4 << kOpcodeShift | (0 << 16), "beqz", "SB" },
+ { kITypeMask | (0x1f << 21), 4 << kOpcodeShift | (0 << 21), "beqz", "TB" },
{ kITypeMask, 4 << kOpcodeShift, "beq", "STB" },
+ { kITypeMask | (0x1f << 16), 5 << kOpcodeShift | (0 << 16), "bnez", "SB" },
+ { kITypeMask | (0x1f << 21), 5 << kOpcodeShift | (0 << 21), "bnez", "TB" },
{ kITypeMask, 5 << kOpcodeShift, "bne", "STB" },
{ kITypeMask | (0x1f << 16), 1 << kOpcodeShift | (1 << 16), "bgez", "SB" },
{ kITypeMask | (0x1f << 16), 1 << kOpcodeShift | (0 << 16), "bltz", "SB" },
- { kITypeMask | (0x1f << 16), 1 << kOpcodeShift | (2 << 16), "bltzl", "SB" },
+ { kITypeMask | (0x3ff << 16), 1 << kOpcodeShift | (16 << 16), "nal", "" },
{ kITypeMask | (0x1f << 16), 1 << kOpcodeShift | (16 << 16), "bltzal", "SB" },
- { kITypeMask | (0x1f << 16), 1 << kOpcodeShift | (18 << 16), "bltzall", "SB" },
+ { kITypeMask | (0x3ff << 16), 1 << kOpcodeShift | (17 << 16), "bal", "B" },
+ { kITypeMask | (0x1f << 16), 1 << kOpcodeShift | (17 << 16), "bgezal", "SB" },
{ kITypeMask | (0x1f << 16), 6 << kOpcodeShift | (0 << 16), "blez", "SB" },
{ kITypeMask, 6 << kOpcodeShift, "bgeuc", "STB" },
{ kITypeMask | (0x1f << 16), 7 << kOpcodeShift | (0 << 16), "bgtz", "SB" },
@@ -228,18 +236,16 @@
{ kITypeMask | (0x1f << 16), 1 << kOpcodeShift | (6 << 16), "dahi", "Si", },
{ kITypeMask | (0x1f << 16), 1 << kOpcodeShift | (30 << 16), "dati", "Si", },
- { 0xffff0000, (4 << kOpcodeShift), "b", "B" },
- { 0xffff0000, (1 << kOpcodeShift) | (17 << 16), "bal", "B" },
-
{ kITypeMask, 8 << kOpcodeShift, "beqc", "STB" },
- { kITypeMask, 8 << kOpcodeShift, "addi", "TSi", },
+ { kITypeMask | (0x1f << 21), 9 << kOpcodeShift | (0 << 21), "li", "Ti" },
{ kITypeMask, 9 << kOpcodeShift, "addiu", "TSi", },
{ kITypeMask, 10 << kOpcodeShift, "slti", "TSi", },
{ kITypeMask, 11 << kOpcodeShift, "sltiu", "TSi", },
- { kITypeMask, 12 << kOpcodeShift, "andi", "TSi", },
- { kITypeMask, 13 << kOpcodeShift, "ori", "TSi", },
- { kITypeMask, 14 << kOpcodeShift, "xori", "TSi", },
+ { kITypeMask, 12 << kOpcodeShift, "andi", "TSI", },
+ { kITypeMask | (0x1f << 21), 13 << kOpcodeShift | (0 << 21), "li", "TI" },
+ { kITypeMask, 13 << kOpcodeShift, "ori", "TSI", },
+ { kITypeMask, 14 << kOpcodeShift, "xori", "TSI", },
{ kITypeMask | (0x1f << 21), 15 << kOpcodeShift, "lui", "Ti", },
{ kITypeMask, 15 << kOpcodeShift, "aui", "TSi", },
@@ -324,6 +330,7 @@
{ kITypeMask, 24 << kOpcodeShift, "bnec", "STB" },
+ { kITypeMask | (0x1f << 21), 25 << kOpcodeShift | (0 << 21), "dli", "Ti" },
{ kITypeMask, 25 << kOpcodeShift, "daddiu", "TSi", },
{ kITypeMask, 29 << kOpcodeShift, "daui", "TSi", },
@@ -561,6 +568,9 @@
}
continue; // No ", ".
}
+ case 'I': // Unsigned lower 16-bit immediate.
+ args << (instruction & 0xffff);
+ break;
case 'i': // Sign-extended lower 16-bit immediate.
args << static_cast<int16_t>(instruction & 0xffff);
break;