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