diff options
-rw-r--r-- | compiler/optimizing/code_generator_mips.cc | 228 | ||||
-rw-r--r-- | compiler/utils/mips/assembler_mips.cc | 74 | ||||
-rw-r--r-- | compiler/utils/mips/assembler_mips.h | 2 | ||||
-rw-r--r-- | compiler/utils/mips/assembler_mips_test.cc | 38 | ||||
-rw-r--r-- | disassembler/disassembler_mips.cc | 7 |
5 files changed, 282 insertions, 67 deletions
diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc index 5dc101b199..84fa0e6322 100644 --- a/compiler/optimizing/code_generator_mips.cc +++ b/compiler/optimizing/code_generator_mips.cc @@ -1191,17 +1191,16 @@ void CodeGeneratorMIPS::InvokeRuntime(int32_t entry_point_offset, uint32_t dex_pc, SlowPathCode* slow_path, bool is_direct_entrypoint) { + __ LoadFromOffset(kLoadWord, T9, TR, entry_point_offset); + __ Jalr(T9); if (is_direct_entrypoint) { // Reserve argument space on stack (for $a0-$a3) for // entrypoints that directly reference native implementations. // Called function may use this space to store $a0-$a3 regs. - __ IncreaseFrameSize(kMipsDirectEntrypointRuntimeOffset); - } - __ LoadFromOffset(kLoadWord, T9, TR, entry_point_offset); - __ Jalr(T9); - __ Nop(); - if (is_direct_entrypoint) { + __ IncreaseFrameSize(kMipsDirectEntrypointRuntimeOffset); // Single instruction in delay slot. __ DecreaseFrameSize(kMipsDirectEntrypointRuntimeOffset); + } else { + __ Nop(); // In delay slot. } RecordPcInfo(instruction, dex_pc, slow_path); } @@ -1275,15 +1274,9 @@ void LocationsBuilderMIPS::HandleBinaryOp(HBinaryOperation* instruction) { } case Primitive::kPrimLong: { - // TODO: can 2nd param be const? locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RequiresRegister()); - if (instruction->IsAdd() || instruction->IsSub()) { - locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); - } else { - DCHECK(instruction->IsAnd() || instruction->IsOr() || instruction->IsXor()); - locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); - } + locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1))); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); break; } @@ -1350,34 +1343,142 @@ void InstructionCodeGeneratorMIPS::HandleBinaryOp(HBinaryOperation* instruction) } case Primitive::kPrimLong: { - // TODO: can 2nd param be const? Register dst_high = locations->Out().AsRegisterPairHigh<Register>(); Register dst_low = locations->Out().AsRegisterPairLow<Register>(); Register lhs_high = locations->InAt(0).AsRegisterPairHigh<Register>(); Register lhs_low = locations->InAt(0).AsRegisterPairLow<Register>(); - Register rhs_high = locations->InAt(1).AsRegisterPairHigh<Register>(); - Register rhs_low = locations->InAt(1).AsRegisterPairLow<Register>(); - - if (instruction->IsAnd()) { - __ And(dst_low, lhs_low, rhs_low); - __ And(dst_high, lhs_high, rhs_high); - } else if (instruction->IsOr()) { - __ Or(dst_low, lhs_low, rhs_low); - __ Or(dst_high, lhs_high, rhs_high); - } else if (instruction->IsXor()) { - __ Xor(dst_low, lhs_low, rhs_low); - __ Xor(dst_high, lhs_high, rhs_high); - } else if (instruction->IsAdd()) { - __ Addu(dst_low, lhs_low, rhs_low); - __ Sltu(TMP, dst_low, lhs_low); - __ Addu(dst_high, lhs_high, rhs_high); - __ Addu(dst_high, dst_high, TMP); + Location rhs_location = locations->InAt(1); + bool use_imm = rhs_location.IsConstant(); + if (!use_imm) { + Register rhs_high = rhs_location.AsRegisterPairHigh<Register>(); + Register rhs_low = rhs_location.AsRegisterPairLow<Register>(); + if (instruction->IsAnd()) { + __ And(dst_low, lhs_low, rhs_low); + __ And(dst_high, lhs_high, rhs_high); + } else if (instruction->IsOr()) { + __ Or(dst_low, lhs_low, rhs_low); + __ Or(dst_high, lhs_high, rhs_high); + } else if (instruction->IsXor()) { + __ Xor(dst_low, lhs_low, rhs_low); + __ Xor(dst_high, lhs_high, rhs_high); + } else if (instruction->IsAdd()) { + if (lhs_low == rhs_low) { + // Special case for lhs = rhs and the sum potentially overwriting both lhs and rhs. + __ Slt(TMP, lhs_low, ZERO); + __ Addu(dst_low, lhs_low, rhs_low); + } else { + __ Addu(dst_low, lhs_low, rhs_low); + // If the sum overwrites rhs, lhs remains unchanged, otherwise rhs remains unchanged. + __ Sltu(TMP, dst_low, (dst_low == rhs_low) ? lhs_low : rhs_low); + } + __ Addu(dst_high, lhs_high, rhs_high); + __ Addu(dst_high, dst_high, TMP); + } else { + DCHECK(instruction->IsSub()); + __ Sltu(TMP, lhs_low, rhs_low); + __ Subu(dst_low, lhs_low, rhs_low); + __ Subu(dst_high, lhs_high, rhs_high); + __ Subu(dst_high, dst_high, TMP); + } } else { - DCHECK(instruction->IsSub()); - __ Subu(dst_low, lhs_low, rhs_low); - __ Sltu(TMP, lhs_low, dst_low); - __ Subu(dst_high, lhs_high, rhs_high); - __ Subu(dst_high, dst_high, TMP); + int64_t value = CodeGenerator::GetInt64ValueOf(rhs_location.GetConstant()->AsConstant()); + if (instruction->IsOr()) { + uint32_t low = Low32Bits(value); + uint32_t high = High32Bits(value); + if (IsUint<16>(low)) { + if (dst_low != lhs_low || low != 0) { + __ Ori(dst_low, lhs_low, low); + } + } else { + __ LoadConst32(TMP, low); + __ Or(dst_low, lhs_low, TMP); + } + if (IsUint<16>(high)) { + if (dst_high != lhs_high || high != 0) { + __ Ori(dst_high, lhs_high, high); + } + } else { + if (high != low) { + __ LoadConst32(TMP, high); + } + __ Or(dst_high, lhs_high, TMP); + } + } else if (instruction->IsXor()) { + uint32_t low = Low32Bits(value); + uint32_t high = High32Bits(value); + if (IsUint<16>(low)) { + if (dst_low != lhs_low || low != 0) { + __ Xori(dst_low, lhs_low, low); + } + } else { + __ LoadConst32(TMP, low); + __ Xor(dst_low, lhs_low, TMP); + } + if (IsUint<16>(high)) { + if (dst_high != lhs_high || high != 0) { + __ Xori(dst_high, lhs_high, high); + } + } else { + if (high != low) { + __ LoadConst32(TMP, high); + } + __ Xor(dst_high, lhs_high, TMP); + } + } else if (instruction->IsAnd()) { + uint32_t low = Low32Bits(value); + uint32_t high = High32Bits(value); + if (IsUint<16>(low)) { + __ Andi(dst_low, lhs_low, low); + } else if (low != 0xFFFFFFFF) { + __ LoadConst32(TMP, low); + __ And(dst_low, lhs_low, TMP); + } else if (dst_low != lhs_low) { + __ Move(dst_low, lhs_low); + } + if (IsUint<16>(high)) { + __ Andi(dst_high, lhs_high, high); + } else if (high != 0xFFFFFFFF) { + if (high != low) { + __ LoadConst32(TMP, high); + } + __ And(dst_high, lhs_high, TMP); + } else if (dst_high != lhs_high) { + __ Move(dst_high, lhs_high); + } + } else { + if (instruction->IsSub()) { + value = -value; + } else { + DCHECK(instruction->IsAdd()); + } + int32_t low = Low32Bits(value); + int32_t high = High32Bits(value); + if (IsInt<16>(low)) { + if (dst_low != lhs_low || low != 0) { + __ Addiu(dst_low, lhs_low, low); + } + if (low != 0) { + __ Sltiu(AT, dst_low, low); + } + } else { + __ LoadConst32(TMP, low); + __ Addu(dst_low, lhs_low, TMP); + __ Sltu(AT, dst_low, TMP); + } + if (IsInt<16>(high)) { + if (dst_high != lhs_high || high != 0) { + __ Addiu(dst_high, lhs_high, high); + } + } else { + if (high != low) { + __ LoadConst32(TMP, high); + } + __ Addu(dst_high, lhs_high, TMP); + } + if (low != 0) { + __ Addu(dst_high, dst_high, AT); + } + } } break; } @@ -1416,12 +1517,15 @@ void LocationsBuilderMIPS::HandleShift(HBinaryOperation* instr) { Primitive::Type type = instr->GetResultType(); switch (type) { case Primitive::kPrimInt: - case Primitive::kPrimLong: { + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RegisterOrConstant(instr->InputAt(1))); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); + break; + case Primitive::kPrimLong: locations->SetInAt(0, Location::RequiresRegister()); locations->SetInAt(1, Location::RegisterOrConstant(instr->InputAt(1))); locations->SetOut(Location::RequiresRegister()); break; - } default: LOG(FATAL) << "Unexpected shift type " << type; } @@ -1440,6 +1544,8 @@ void InstructionCodeGeneratorMIPS::HandleShift(HBinaryOperation* instr) { int64_t rhs_imm = use_imm ? CodeGenerator::GetInt64ValueOf(rhs_location.GetConstant()) : 0; uint32_t shift_mask = (type == Primitive::kPrimInt) ? kMaxIntShiftValue : kMaxLongShiftValue; uint32_t shift_value = rhs_imm & shift_mask; + // Is the INS (Insert Bit Field) instruction supported? + bool has_ins = codegen_->GetInstructionSetFeatures().IsMipsIsaRevGreaterThanEqual2(); switch (type) { case Primitive::kPrimInt: { @@ -1474,21 +1580,37 @@ void InstructionCodeGeneratorMIPS::HandleShift(HBinaryOperation* instr) { if (shift_value == 0) { codegen_->Move64(locations->Out(), locations->InAt(0)); } else if (shift_value < kMipsBitsPerWord) { - if (instr->IsShl()) { - __ Sll(dst_low, lhs_low, shift_value); - __ Srl(TMP, lhs_low, kMipsBitsPerWord - shift_value); - __ Sll(dst_high, lhs_high, shift_value); - __ Or(dst_high, dst_high, TMP); - } else if (instr->IsShr()) { - __ Sra(dst_high, lhs_high, shift_value); - __ Sll(TMP, lhs_high, kMipsBitsPerWord - shift_value); - __ Srl(dst_low, lhs_low, shift_value); - __ Or(dst_low, dst_low, TMP); + if (has_ins) { + if (instr->IsShl()) { + __ Srl(dst_high, lhs_low, kMipsBitsPerWord - shift_value); + __ Ins(dst_high, lhs_high, shift_value, kMipsBitsPerWord - shift_value); + __ Sll(dst_low, lhs_low, shift_value); + } else if (instr->IsShr()) { + __ Srl(dst_low, lhs_low, shift_value); + __ Ins(dst_low, lhs_high, kMipsBitsPerWord - shift_value, shift_value); + __ Sra(dst_high, lhs_high, shift_value); + } else { + __ Srl(dst_low, lhs_low, shift_value); + __ Ins(dst_low, lhs_high, kMipsBitsPerWord - shift_value, shift_value); + __ Srl(dst_high, lhs_high, shift_value); + } } else { - __ Srl(dst_high, lhs_high, shift_value); - __ Sll(TMP, lhs_high, kMipsBitsPerWord - shift_value); - __ Srl(dst_low, lhs_low, shift_value); - __ Or(dst_low, dst_low, TMP); + if (instr->IsShl()) { + __ Sll(dst_low, lhs_low, shift_value); + __ Srl(TMP, lhs_low, kMipsBitsPerWord - shift_value); + __ Sll(dst_high, lhs_high, shift_value); + __ Or(dst_high, dst_high, TMP); + } else if (instr->IsShr()) { + __ Sra(dst_high, lhs_high, shift_value); + __ Sll(TMP, lhs_high, kMipsBitsPerWord - shift_value); + __ Srl(dst_low, lhs_low, shift_value); + __ Or(dst_low, dst_low, TMP); + } else { + __ Srl(dst_high, lhs_high, shift_value); + __ Sll(TMP, lhs_high, kMipsBitsPerWord - shift_value); + __ Srl(dst_low, lhs_low, shift_value); + __ Or(dst_low, dst_low, TMP); + } } } else { shift_value -= kMipsBitsPerWord; diff --git a/compiler/utils/mips/assembler_mips.cc b/compiler/utils/mips/assembler_mips.cc index afca8adcbb..0dc307c9ac 100644 --- a/compiler/utils/mips/assembler_mips.cc +++ b/compiler/utils/mips/assembler_mips.cc @@ -400,6 +400,20 @@ void MipsAssembler::Srav(Register rd, Register rt, Register rs) { EmitR(0, rs, rt, rd, 0, 0x07); } +void MipsAssembler::Ext(Register rd, Register rt, int pos, int size) { + CHECK(IsUint<5>(pos)) << pos; + CHECK(0 < size && size <= 32) << size; + CHECK(0 < pos + size && pos + size <= 32) << pos << " + " << size; + EmitR(0x1f, rt, rd, static_cast<Register>(size - 1), pos, 0x00); +} + +void MipsAssembler::Ins(Register rd, Register rt, int pos, int size) { + CHECK(IsUint<5>(pos)) << pos; + CHECK(0 < size && size <= 32) << size; + CHECK(0 < pos + size && pos + size <= 32) << pos << " + " << size; + EmitR(0x1f, rt, rd, static_cast<Register>(pos + size - 1), pos, 0x04); +} + void MipsAssembler::Lb(Register rt, Register rs, uint16_t imm16) { EmitI(0x20, rs, rt, imm16); } @@ -1121,8 +1135,14 @@ void MipsAssembler::LoadConst32(Register rd, int32_t value) { } void MipsAssembler::LoadConst64(Register reg_hi, Register reg_lo, int64_t value) { - LoadConst32(reg_lo, Low32Bits(value)); - LoadConst32(reg_hi, High32Bits(value)); + uint32_t low = Low32Bits(value); + uint32_t high = High32Bits(value); + LoadConst32(reg_lo, low); + if (high != low) { + LoadConst32(reg_hi, high); + } else { + Move(reg_hi, reg_lo); + } } void MipsAssembler::StoreConst32ToOffset(int32_t value, @@ -1136,7 +1156,11 @@ void MipsAssembler::StoreConst32ToOffset(int32_t value, base = AT; offset = 0; } - LoadConst32(temp, value); + if (value == 0) { + temp = ZERO; + } else { + LoadConst32(temp, value); + } Sw(temp, base, offset); } @@ -1152,22 +1176,48 @@ void MipsAssembler::StoreConst64ToOffset(int64_t value, base = AT; offset = 0; } - LoadConst32(temp, Low32Bits(value)); - Sw(temp, base, offset); - LoadConst32(temp, High32Bits(value)); - Sw(temp, base, offset + kMipsWordSize); + uint32_t low = Low32Bits(value); + uint32_t high = High32Bits(value); + if (low == 0) { + Sw(ZERO, base, offset); + } else { + LoadConst32(temp, low); + Sw(temp, base, offset); + } + if (high == 0) { + Sw(ZERO, base, offset + kMipsWordSize); + } else { + if (high != low) { + LoadConst32(temp, high); + } + Sw(temp, base, offset + kMipsWordSize); + } } void MipsAssembler::LoadSConst32(FRegister r, int32_t value, Register temp) { - LoadConst32(temp, value); + if (value == 0) { + temp = ZERO; + } else { + LoadConst32(temp, value); + } Mtc1(temp, r); } void MipsAssembler::LoadDConst64(FRegister rd, int64_t value, Register temp) { - LoadConst32(temp, Low32Bits(value)); - Mtc1(temp, rd); - LoadConst32(temp, High32Bits(value)); - Mthc1(temp, rd); + uint32_t low = Low32Bits(value); + uint32_t high = High32Bits(value); + if (low == 0) { + Mtc1(ZERO, rd); + } else { + LoadConst32(temp, low); + Mtc1(temp, rd); + } + if (high == 0) { + Mthc1(ZERO, rd); + } else { + LoadConst32(temp, high); + Mthc1(temp, rd); + } } void MipsAssembler::Addiu32(Register rt, Register rs, int32_t value, Register temp) { diff --git a/compiler/utils/mips/assembler_mips.h b/compiler/utils/mips/assembler_mips.h index f569aa858c..066e7b0014 100644 --- a/compiler/utils/mips/assembler_mips.h +++ b/compiler/utils/mips/assembler_mips.h @@ -156,6 +156,8 @@ class MipsAssembler FINAL : public Assembler { void Srlv(Register rd, Register rt, Register rs); void Rotrv(Register rd, Register rt, Register rs); // R2+ void Srav(Register rd, Register rt, Register rs); + void Ext(Register rd, Register rt, int pos, int size); // R2+ + void Ins(Register rd, Register rt, int pos, int size); // R2+ void Lb(Register rt, Register rs, uint16_t imm16); void Lh(Register rt, Register rs, uint16_t imm16); diff --git a/compiler/utils/mips/assembler_mips_test.cc b/compiler/utils/mips/assembler_mips_test.cc index 6f8b3e8c57..4361843c54 100644 --- a/compiler/utils/mips/assembler_mips_test.cc +++ b/compiler/utils/mips/assembler_mips_test.cc @@ -367,6 +367,44 @@ TEST_F(AssemblerMIPSTest, Srav) { DriverStr(RepeatRRR(&mips::MipsAssembler::Srav, "srav ${reg1}, ${reg2}, ${reg3}"), "Srav"); } +TEST_F(AssemblerMIPSTest, Ins) { + std::vector<mips::Register*> regs = GetRegisters(); + WarnOnCombinations(regs.size() * regs.size() * 33 * 16); + std::string expected; + for (mips::Register* reg1 : regs) { + for (mips::Register* reg2 : regs) { + for (int32_t pos = 0; pos < 32; pos++) { + for (int32_t size = 1; pos + size <= 32; size++) { + __ Ins(*reg1, *reg2, pos, size); + std::ostringstream instr; + instr << "ins $" << *reg1 << ", $" << *reg2 << ", " << pos << ", " << size << "\n"; + expected += instr.str(); + } + } + } + } + DriverStr(expected, "Ins"); +} + +TEST_F(AssemblerMIPSTest, Ext) { + std::vector<mips::Register*> regs = GetRegisters(); + WarnOnCombinations(regs.size() * regs.size() * 33 * 16); + std::string expected; + for (mips::Register* reg1 : regs) { + for (mips::Register* reg2 : regs) { + for (int32_t pos = 0; pos < 32; pos++) { + for (int32_t size = 1; pos + size <= 32; size++) { + __ Ext(*reg1, *reg2, pos, size); + std::ostringstream instr; + instr << "ext $" << *reg1 << ", $" << *reg2 << ", " << pos << ", " << size << "\n"; + expected += instr.str(); + } + } + } + } + DriverStr(expected, "Ext"); +} + TEST_F(AssemblerMIPSTest, Lb) { DriverStr(RepeatRRIb(&mips::MipsAssembler::Lb, -16, "lb ${reg1}, {imm}(${reg2})"), "Lb"); } diff --git a/disassembler/disassembler_mips.cc b/disassembler/disassembler_mips.cc index cd64a4f926..ee7b21ced7 100644 --- a/disassembler/disassembler_mips.cc +++ b/disassembler/disassembler_mips.cc @@ -150,7 +150,9 @@ static const MipsInstruction gMipsInstructions[] = { { kSpecial2Mask | 0x3f, (28 << kOpcodeShift) | 0x3f, "sdbbp", "" }, // TODO: code // SPECIAL3 + { kSpecial3Mask | 0x3f, (31 << kOpcodeShift), "ext", "TSAZ", }, { kSpecial3Mask | 0x3f, (31 << kOpcodeShift) | 3, "dext", "TSAZ", }, + { kSpecial3Mask | 0x3f, (31 << kOpcodeShift) | 4, "ins", "TSAz", }, { kSpecial3Mask | (0x1f << 21) | (0x1f << 6) | 0x3f, (31 << kOpcodeShift) | (16 << 6) | 32, "seb", @@ -421,7 +423,7 @@ size_t DisassemblerMips::Dump(std::ostream& os, const uint8_t* instr_ptr) { opcode = gMipsInstructions[i].name; for (const char* args_fmt = gMipsInstructions[i].args_fmt; *args_fmt; ++args_fmt) { switch (*args_fmt) { - case 'A': // sa (shift amount or [d]ext position). + case 'A': // sa (shift amount or [d]ins/[d]ext position). args << sa; break; case 'B': // Branch offset. @@ -519,7 +521,8 @@ size_t DisassemblerMips::Dump(std::ostream& os, const uint8_t* instr_ptr) { case 's': args << 'f' << rs; break; case 'T': args << 'r' << rt; break; case 't': args << 'f' << rt; break; - case 'Z': args << rd; break; // sz ([d]ext size). + case 'Z': args << (rd + 1); break; // sz ([d]ext size). + case 'z': args << (rd - sa + 1); break; // sz ([d]ins size). } if (*(args_fmt + 1)) { args << ", "; |