in compiler/utils/arm/assembler_thumb2.cc [1433:1620]
void Thumb2Assembler::Emit16BitDataProcessing(Condition cond,
Opcode opcode,
SetCc set_cc,
Register rn,
Register rd,
const ShifterOperand& so) {
if (opcode == ADD || opcode == SUB) {
Emit16BitAddSub(cond, opcode, set_cc, rn, rd, so);
return;
}
uint8_t thumb_opcode = 255U /* 0b11111111 */;
// Thumb1.
uint8_t dp_opcode = 1U /* 0b01 */;
uint8_t opcode_shift = 6;
uint8_t rd_shift = 0;
uint8_t rn_shift = 3;
uint8_t immediate_shift = 0;
bool use_immediate = false;
uint8_t immediate = 0;
if (opcode == MOV && so.IsRegister() && so.IsShift()) {
// Convert shifted mov operand2 into 16 bit opcodes.
dp_opcode = 0;
opcode_shift = 11;
use_immediate = true;
immediate = so.GetImmediate();
immediate_shift = 6;
rn = so.GetRegister();
switch (so.GetShift()) {
case LSL:
DCHECK_LE(immediate, 31u);
thumb_opcode = 0U /* 0b00 */;
break;
case LSR:
DCHECK(1 <= immediate && immediate <= 32);
immediate &= 31; // 32 is encoded as 0.
thumb_opcode = 1U /* 0b01 */;
break;
case ASR:
DCHECK(1 <= immediate && immediate <= 32);
immediate &= 31; // 32 is encoded as 0.
thumb_opcode = 2U /* 0b10 */;
break;
case ROR: // No 16-bit ROR immediate.
case RRX: // No 16-bit RRX.
default:
LOG(FATAL) << "Unexpected shift: " << so.GetShift();
UNREACHABLE();
}
} else {
if (so.IsImmediate()) {
use_immediate = true;
immediate = so.GetImmediate();
} else {
CHECK(!(so.IsRegister() && so.IsShift() && so.GetSecondRegister() != kNoRegister))
<< "No register-shifted register instruction available in thumb";
// Adjust rn and rd: only two registers will be emitted.
switch (opcode) {
case AND:
case ORR:
case EOR:
case RSB:
case ADC:
case SBC:
case BIC: {
// Sets condition codes if and only if outside IT block,
// check that it complies with set_cc.
DCHECK((cond == AL) ? set_cc != kCcKeep : set_cc != kCcSet);
if (rn == rd) {
rn = so.GetRegister();
} else {
CHECK_EQ(rd, so.GetRegister());
}
break;
}
case CMP:
case CMN: {
CHECK_EQ(rd, 0);
rd = rn;
rn = so.GetRegister();
break;
}
case MVN: {
// Sets condition codes if and only if outside IT block,
// check that it complies with set_cc.
DCHECK((cond == AL) ? set_cc != kCcKeep : set_cc != kCcSet);
CHECK_EQ(rn, 0);
rn = so.GetRegister();
break;
}
case TST:
case TEQ: {
DCHECK(set_cc == kCcSet);
CHECK_EQ(rn, 0);
rn = so.GetRegister();
break;
}
default:
break;
}
}
switch (opcode) {
case AND: thumb_opcode = 0U /* 0b0000 */; break;
case ORR: thumb_opcode = 12U /* 0b1100 */; break;
case EOR: thumb_opcode = 1U /* 0b0001 */; break;
case RSB: thumb_opcode = 9U /* 0b1001 */; break;
case ADC: thumb_opcode = 5U /* 0b0101 */; break;
case SBC: thumb_opcode = 6U /* 0b0110 */; break;
case BIC: thumb_opcode = 14U /* 0b1110 */; break;
case TST: thumb_opcode = 8U /* 0b1000 */; CHECK(!use_immediate); break;
case MVN: thumb_opcode = 15U /* 0b1111 */; CHECK(!use_immediate); break;
case CMP: {
DCHECK(set_cc == kCcSet);
if (use_immediate) {
// T2 encoding.
dp_opcode = 0;
opcode_shift = 11;
thumb_opcode = 5U /* 0b101 */;
rd_shift = 8;
rn_shift = 8;
} else if (IsHighRegister(rd) || IsHighRegister(rn)) {
// Special cmp for high registers.
dp_opcode = 1U /* 0b01 */;
opcode_shift = 7;
// Put the top bit of rd into the bottom bit of the opcode.
thumb_opcode = 10U /* 0b0001010 */ | static_cast<uint32_t>(rd) >> 3;
rd = static_cast<Register>(static_cast<uint32_t>(rd) & 7U /* 0b111 */);
} else {
thumb_opcode = 10U /* 0b1010 */;
}
break;
}
case CMN: {
CHECK(!use_immediate);
thumb_opcode = 11U /* 0b1011 */;
break;
}
case MOV:
dp_opcode = 0;
if (use_immediate) {
// T2 encoding.
opcode_shift = 11;
thumb_opcode = 4U /* 0b100 */;
rd_shift = 8;
rn_shift = 8;
} else {
rn = so.GetRegister();
if (set_cc != kCcSet) {
// Special mov for high registers.
dp_opcode = 1U /* 0b01 */;
opcode_shift = 7;
// Put the top bit of rd into the bottom bit of the opcode.
thumb_opcode = 12U /* 0b0001100 */ | static_cast<uint32_t>(rd) >> 3;
rd = static_cast<Register>(static_cast<uint32_t>(rd) & 7U /* 0b111 */);
} else {
DCHECK(!IsHighRegister(rn));
DCHECK(!IsHighRegister(rd));
thumb_opcode = 0;
}
}
break;
case TEQ:
case RSC:
default:
LOG(FATAL) << "Invalid thumb1 opcode " << opcode;
break;
}
}
if (thumb_opcode == 255U /* 0b11111111 */) {
LOG(FATAL) << "Invalid thumb1 opcode " << opcode;
UNREACHABLE();
}
int16_t encoding = dp_opcode << 14 |
(thumb_opcode << opcode_shift) |
rd << rd_shift |
rn << rn_shift |
(use_immediate ? (immediate << immediate_shift) : 0);
Emit16(encoding);
}