void InstructionCodeGeneratorMIPS::HandleShift()

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;
  }
}