bool Thumb2Assembler::Is32BitDataProcessing()

in compiler/utils/arm/assembler_thumb2.cc [1183:1343]


bool Thumb2Assembler::Is32BitDataProcessing(Condition cond,
                                            Opcode opcode,
                                            SetCc set_cc,
                                            Register rn,
                                            Register rd,
                                            const ShifterOperand& so) {
  if (force_32bit_) {
    return true;
  }

  // Check special case for SP relative ADD and SUB immediate.
  if ((opcode == ADD || opcode == SUB) && rn == SP && so.IsImmediate() && set_cc != kCcSet) {
    // If the immediate is in range, use 16 bit.
    if (rd == SP) {
      if (so.GetImmediate() < (1 << 9)) {    // 9 bit immediate.
        return false;
      }
    } else if (!IsHighRegister(rd) && opcode == ADD) {
      if (so.GetImmediate() < (1 << 10)) {    // 10 bit immediate.
        return false;
      }
    }
  }

  bool can_contain_high_register =
      (opcode == CMP) ||
      (opcode == MOV && set_cc != kCcSet) ||
      ((opcode == ADD) && (rn == rd) && set_cc != kCcSet);

  if (IsHighRegister(rd) || IsHighRegister(rn)) {
    if (!can_contain_high_register) {
      return true;
    }

    // There are high register instructions available for this opcode.
    // However, there is no actual shift available, neither for ADD nor for MOV (ASR/LSR/LSL/ROR).
    if (so.IsShift() && (so.GetShift() == RRX || so.GetImmediate() != 0u)) {
      return true;
    }

    // The ADD and MOV instructions that work with high registers don't have 16-bit
    // immediate variants.
    if (so.IsImmediate()) {
      return true;
    }
  }

  if (so.IsRegister() && IsHighRegister(so.GetRegister()) && !can_contain_high_register) {
    return true;
  }

  bool rn_is_valid = true;

  // Check for single operand instructions and ADD/SUB.
  switch (opcode) {
    case CMP:
    case MOV:
    case TST:
    case MVN:
      rn_is_valid = false;      // There is no Rn for these instructions.
      break;
    case TEQ:
    case ORN:
      return true;
    case ADD:
    case SUB:
      break;
    default:
      if (so.IsRegister() && rd != rn) {
        return true;
      }
  }

  if (so.IsImmediate()) {
    if (opcode == RSB) {
      DCHECK(rn_is_valid);
      if (so.GetImmediate() != 0u) {
        return true;
      }
    } else if (rn_is_valid && rn != rd) {
      // The only thumb1 instructions with a register and an immediate are ADD and SUB
      // with a 3-bit immediate, and RSB with zero immediate.
      if (opcode == ADD || opcode == SUB) {
        if ((cond == AL) ? set_cc == kCcKeep : set_cc == kCcSet) {
          return true;  // Cannot match "setflags".
        }
        if (!IsUint<3>(so.GetImmediate()) && !IsUint<3>(-so.GetImmediate())) {
          return true;
        }
      } else {
        return true;
      }
    } else {
      // ADD, SUB, CMP and MOV may be thumb1 only if the immediate is 8 bits.
      if (!(opcode == ADD || opcode == SUB || opcode == MOV || opcode == CMP)) {
        return true;
      } else if (opcode != CMP && ((cond == AL) ? set_cc == kCcKeep : set_cc == kCcSet)) {
        return true;  // Cannot match "setflags" for ADD, SUB or MOV.
      } else {
        // For ADD and SUB allow also negative 8-bit immediate as we will emit the oposite opcode.
        if (!IsUint<8>(so.GetImmediate()) &&
            (opcode == MOV || opcode == CMP || !IsUint<8>(-so.GetImmediate()))) {
          return true;
        }
      }
    }
  } else {
    DCHECK(so.IsRegister());
    if (so.IsShift()) {
      // Shift operand - check if it is a MOV convertible to a 16-bit shift instruction.
      if (opcode != MOV) {
        return true;
      }
      // Check for MOV with an ROR/RRX. There is no 16-bit ROR immediate and no 16-bit RRX.
      if (so.GetShift() == ROR || so.GetShift() == RRX) {
        return true;
      }
      // 16-bit shifts set condition codes if and only if outside IT block,
      // i.e. if and only if cond == AL.
      if ((cond == AL) ? set_cc == kCcKeep : set_cc == kCcSet) {
        return true;
      }
    } else {
      // Register operand without shift.
      switch (opcode) {
        case ADD:
          // The 16-bit ADD that cannot contain high registers can set condition codes
          // if and only if outside IT block, i.e. if and only if cond == AL.
          if (!can_contain_high_register &&
              ((cond == AL) ? set_cc == kCcKeep : set_cc == kCcSet)) {
            return true;
          }
          break;
        case AND:
        case BIC:
        case EOR:
        case ORR:
        case MVN:
        case ADC:
        case SUB:
        case SBC:
          // These 16-bit opcodes set condition codes if and only if outside IT block,
          // i.e. if and only if cond == AL.
          if ((cond == AL) ? set_cc == kCcKeep : set_cc == kCcSet) {
            return true;
          }
          break;
        case RSB:
        case RSC:
          // No 16-bit RSB/RSC Rd, Rm, Rn. It would be equivalent to SUB/SBC Rd, Rn, Rm.
          return true;
        case CMP:
        default:
          break;
      }
    }
  }

  // The instruction can be encoded in 16 bits.
  return false;
}