in compiler/utils/arm/assembler_thumb2.cc [2322:2482]
void Thumb2Assembler::EmitLoadStore(Condition cond,
bool load,
bool byte,
bool half,
bool is_signed,
Register rd,
const Address& ad) {
CHECK_NE(rd, kNoRegister);
CheckCondition(cond);
bool must_be_32bit = force_32bit_;
if (IsHighRegister(rd)) {
must_be_32bit = true;
}
Register rn = ad.GetRegister();
if (IsHighRegister(rn) && rn != SP && rn != PC) {
must_be_32bit = true;
}
if (is_signed || ad.GetOffset() < 0 || ad.GetMode() != Address::Offset) {
must_be_32bit = true;
}
if (ad.IsImmediate()) {
// Immediate offset
int32_t offset = ad.GetOffset();
// The 16 bit SP relative instruction can only have a 10 bit offset.
if (rn == SP && offset >= (1 << 10)) {
must_be_32bit = true;
}
if (byte) {
// 5 bit offset, no shift.
if (offset >= (1 << 5)) {
must_be_32bit = true;
}
} else if (half) {
// 6 bit offset, shifted by 1.
if (offset >= (1 << 6)) {
must_be_32bit = true;
}
} else {
// 7 bit offset, shifted by 2.
if (offset >= (1 << 7)) {
must_be_32bit = true;
}
}
if (must_be_32bit) {
int32_t encoding = B31 | B30 | B29 | B28 | B27 |
(load ? B20 : 0) |
(is_signed ? B24 : 0) |
static_cast<uint32_t>(rd) << 12 |
ad.encodingThumb(true) |
(byte ? 0 : half ? B21 : B22);
Emit32(encoding);
} else {
// 16 bit thumb1.
uint8_t opA = 0;
bool sp_relative = false;
if (byte) {
opA = 7U /* 0b0111 */;
} else if (half) {
opA = 8U /* 0b1000 */;
} else {
if (rn == SP) {
opA = 9U /* 0b1001 */;
sp_relative = true;
} else {
opA = 6U /* 0b0110 */;
}
}
int16_t encoding = opA << 12 |
(load ? B11 : 0);
CHECK_GE(offset, 0);
if (sp_relative) {
// SP relative, 10 bit offset.
CHECK_LT(offset, (1 << 10));
CHECK_ALIGNED(offset, 4);
encoding |= rd << 8 | offset >> 2;
} else {
// No SP relative. The offset is shifted right depending on
// the size of the load/store.
encoding |= static_cast<uint32_t>(rd);
if (byte) {
// 5 bit offset, no shift.
CHECK_LT(offset, (1 << 5));
} else if (half) {
// 6 bit offset, shifted by 1.
CHECK_LT(offset, (1 << 6));
CHECK_ALIGNED(offset, 2);
offset >>= 1;
} else {
// 7 bit offset, shifted by 2.
CHECK_LT(offset, (1 << 7));
CHECK_ALIGNED(offset, 4);
offset >>= 2;
}
encoding |= rn << 3 | offset << 6;
}
Emit16(encoding);
}
} else {
// Register shift.
if (ad.GetRegister() == PC) {
// PC relative literal encoding.
int32_t offset = ad.GetOffset();
if (must_be_32bit || offset < 0 || offset >= (1 << 10) || !load) {
int32_t up = B23;
if (offset < 0) {
offset = -offset;
up = 0;
}
CHECK_LT(offset, (1 << 12));
int32_t encoding = 0x1f << 27 | 0xf << 16 | B22 | (load ? B20 : 0) |
offset | up |
static_cast<uint32_t>(rd) << 12;
Emit32(encoding);
} else {
// 16 bit literal load.
CHECK_GE(offset, 0);
CHECK_LT(offset, (1 << 10));
int32_t encoding = B14 | (load ? B11 : 0) | static_cast<uint32_t>(rd) << 8 | offset >> 2;
Emit16(encoding);
}
} else {
if (ad.GetShiftCount() != 0) {
// If there is a shift count this must be 32 bit.
must_be_32bit = true;
} else if (IsHighRegister(ad.GetRegisterOffset())) {
must_be_32bit = true;
}
if (must_be_32bit) {
int32_t encoding = 0x1f << 27 | (load ? B20 : 0) | static_cast<uint32_t>(rd) << 12 |
ad.encodingThumb(true);
if (half) {
encoding |= B21;
} else if (!byte) {
encoding |= B22;
}
Emit32(encoding);
} else {
// 16 bit register offset.
int32_t encoding = B14 | B12 | (load ? B11 : 0) | static_cast<uint32_t>(rd) |
ad.encodingThumb(false);
if (byte) {
encoding |= B10;
} else if (half) {
encoding |= B9;
}
Emit16(encoding);
}
}
}
}