in compiler/optimizing/code_generator_mips.cc [1424:1634]
void InstructionCodeGeneratorMIPS::HandleShift(HBinaryOperation* instr) {
DCHECK(instr->IsShl() || instr->IsShr() || instr->IsUShr() || instr->IsRor());
LocationSummary* locations = instr->GetLocations();
Primitive::Type type = instr->GetType();
Location rhs_location = locations->InAt(1);
bool use_imm = rhs_location.IsConstant();
Register rhs_reg = use_imm ? ZERO : rhs_location.AsRegister<Register>();
int64_t rhs_imm = use_imm ? CodeGenerator::GetInt64ValueOf(rhs_location.GetConstant()) : 0;
const uint32_t shift_mask =
(type == Primitive::kPrimInt) ? kMaxIntShiftDistance : kMaxLongShiftDistance;
const uint32_t shift_value = rhs_imm & shift_mask;
// Are the INS (Insert Bit Field) and ROTR instructions supported?
bool has_ins_rotr = codegen_->GetInstructionSetFeatures().IsMipsIsaRevGreaterThanEqual2();
switch (type) {
case Primitive::kPrimInt: {
Register dst = locations->Out().AsRegister<Register>();
Register lhs = locations->InAt(0).AsRegister<Register>();
if (use_imm) {
if (shift_value == 0) {
if (dst != lhs) {
__ Move(dst, lhs);
}
} else if (instr->IsShl()) {
__ Sll(dst, lhs, shift_value);
} else if (instr->IsShr()) {
__ Sra(dst, lhs, shift_value);
} else if (instr->IsUShr()) {
__ Srl(dst, lhs, shift_value);
} else {
if (has_ins_rotr) {
__ Rotr(dst, lhs, shift_value);
} else {
__ Sll(TMP, lhs, (kMipsBitsPerWord - shift_value) & shift_mask);
__ Srl(dst, lhs, shift_value);
__ Or(dst, dst, TMP);
}
}
} else {
if (instr->IsShl()) {
__ Sllv(dst, lhs, rhs_reg);
} else if (instr->IsShr()) {
__ Srav(dst, lhs, rhs_reg);
} else if (instr->IsUShr()) {
__ Srlv(dst, lhs, rhs_reg);
} else {
if (has_ins_rotr) {
__ Rotrv(dst, lhs, rhs_reg);
} else {
__ Subu(TMP, ZERO, rhs_reg);
// 32-bit shift instructions use the 5 least significant bits of the shift count, so
// shifting by `-rhs_reg` is equivalent to shifting by `(32 - rhs_reg) & 31`. The case
// when `rhs_reg & 31 == 0` is OK even though we don't shift `lhs` left all the way out
// by 32, because the result in this case is computed as `(lhs >> 0) | (lhs << 0)`,
// IOW, the OR'd values are equal.
__ Sllv(TMP, lhs, TMP);
__ Srlv(dst, lhs, rhs_reg);
__ Or(dst, dst, TMP);
}
}
}
break;
}
case Primitive::kPrimLong: {
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>();
if (use_imm) {
if (shift_value == 0) {
codegen_->Move64(locations->Out(), locations->InAt(0));
} else if (shift_value < kMipsBitsPerWord) {
if (has_ins_rotr) {
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 if (instr->IsUShr()) {
__ 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_low, lhs_low, shift_value);
__ Ins(dst_low, lhs_high, kMipsBitsPerWord - shift_value, shift_value);
__ Srl(dst_high, lhs_high, shift_value);
__ Ins(dst_high, lhs_low, kMipsBitsPerWord - shift_value, shift_value);
}
} else {
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 if (instr->IsUShr()) {
__ 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 {
__ Srl(TMP, lhs_low, shift_value);
__ Sll(dst_low, lhs_high, kMipsBitsPerWord - shift_value);
__ Or(dst_low, dst_low, TMP);
__ Srl(TMP, lhs_high, shift_value);
__ Sll(dst_high, lhs_low, kMipsBitsPerWord - shift_value);
__ Or(dst_high, dst_high, TMP);
}
}
} else {
const uint32_t shift_value_high = shift_value - kMipsBitsPerWord;
if (instr->IsShl()) {
__ Sll(dst_high, lhs_low, shift_value_high);
__ Move(dst_low, ZERO);
} else if (instr->IsShr()) {
__ Sra(dst_low, lhs_high, shift_value_high);
__ Sra(dst_high, dst_low, kMipsBitsPerWord - 1);
} else if (instr->IsUShr()) {
__ Srl(dst_low, lhs_high, shift_value_high);
__ Move(dst_high, ZERO);
} else {
if (shift_value == kMipsBitsPerWord) {
// 64-bit rotation by 32 is just a swap.
__ Move(dst_low, lhs_high);
__ Move(dst_high, lhs_low);
} else {
if (has_ins_rotr) {
__ Srl(dst_low, lhs_high, shift_value_high);
__ Ins(dst_low, lhs_low, kMipsBitsPerWord - shift_value_high, shift_value_high);
__ Srl(dst_high, lhs_low, shift_value_high);
__ Ins(dst_high, lhs_high, kMipsBitsPerWord - shift_value_high, shift_value_high);
} else {
__ Sll(TMP, lhs_low, kMipsBitsPerWord - shift_value_high);
__ Srl(dst_low, lhs_high, shift_value_high);
__ Or(dst_low, dst_low, TMP);
__ Sll(TMP, lhs_high, kMipsBitsPerWord - shift_value_high);
__ Srl(dst_high, lhs_low, shift_value_high);
__ Or(dst_high, dst_high, TMP);
}
}
}
}
} else {
MipsLabel done;
if (instr->IsShl()) {
__ Sllv(dst_low, lhs_low, rhs_reg);
__ Nor(AT, ZERO, rhs_reg);
__ Srl(TMP, lhs_low, 1);
__ Srlv(TMP, TMP, AT);
__ Sllv(dst_high, lhs_high, rhs_reg);
__ Or(dst_high, dst_high, TMP);
__ Andi(TMP, rhs_reg, kMipsBitsPerWord);
__ Beqz(TMP, &done);
__ Move(dst_high, dst_low);
__ Move(dst_low, ZERO);
} else if (instr->IsShr()) {
__ Srav(dst_high, lhs_high, rhs_reg);
__ Nor(AT, ZERO, rhs_reg);
__ Sll(TMP, lhs_high, 1);
__ Sllv(TMP, TMP, AT);
__ Srlv(dst_low, lhs_low, rhs_reg);
__ Or(dst_low, dst_low, TMP);
__ Andi(TMP, rhs_reg, kMipsBitsPerWord);
__ Beqz(TMP, &done);
__ Move(dst_low, dst_high);
__ Sra(dst_high, dst_high, 31);
} else if (instr->IsUShr()) {
__ Srlv(dst_high, lhs_high, rhs_reg);
__ Nor(AT, ZERO, rhs_reg);
__ Sll(TMP, lhs_high, 1);
__ Sllv(TMP, TMP, AT);
__ Srlv(dst_low, lhs_low, rhs_reg);
__ Or(dst_low, dst_low, TMP);
__ Andi(TMP, rhs_reg, kMipsBitsPerWord);
__ Beqz(TMP, &done);
__ Move(dst_low, dst_high);
__ Move(dst_high, ZERO);
} else {
__ Nor(AT, ZERO, rhs_reg);
__ Srlv(TMP, lhs_low, rhs_reg);
__ Sll(dst_low, lhs_high, 1);
__ Sllv(dst_low, dst_low, AT);
__ Or(dst_low, dst_low, TMP);
__ Srlv(TMP, lhs_high, rhs_reg);
__ Sll(dst_high, lhs_low, 1);
__ Sllv(dst_high, dst_high, AT);
__ Or(dst_high, dst_high, TMP);
__ Andi(TMP, rhs_reg, kMipsBitsPerWord);
__ Beqz(TMP, &done);
__ Move(TMP, dst_high);
__ Move(dst_high, dst_low);
__ Move(dst_low, TMP);
}
__ Bind(&done);
}
break;
}
default:
LOG(FATAL) << "Unexpected shift operation type " << type;
}
}