Commit 53340b26 authored by erik.corry@gmail.com's avatar erik.corry@gmail.com

Add movw and movt support for ARMv7. This includes some code from

Zhang Kun.  For now we only emit movw and movt in places where no
relocation is needed.  Small performance boost (around 0.5%).
Also adds support for turning ALU operations (eor etc.) with
large immediates into mvn or movw followed by a register-based
ALU operation.
Review URL: http://codereview.chromium.org/2821014

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@4913 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 6b1d21c6
......@@ -282,6 +282,11 @@ const Instr kBlxRegPattern =
const Instr kMovMvnMask = 0x6d * B21 | 0xf * B16;
const Instr kMovMvnPattern = 0xd * B21;
const Instr kMovMvnFlip = B22;
const Instr kMovLeaveCCMask = 0xdff * B16;
const Instr kMovLeaveCCPattern = 0x1a0 * B16;
const Instr kMovwMask = 0xff * B20;
const Instr kMovwPattern = 0x30 * B20;
const Instr kMovwLeaveCCFlip = 0x5 * B21;
const Instr kCmpCmnMask = 0xdd * B20 | 0xf * B12;
const Instr kCmpCmnPattern = 0x15 * B20;
const Instr kCmpCmnFlip = B21;
......@@ -640,6 +645,12 @@ void Assembler::next(Label* L) {
}
static Instr EncodeMovwImmediate(uint32_t immediate) {
ASSERT(immediate < 0x10000);
return ((immediate & 0xf000) << 4) | (immediate & 0xfff);
}
// Low-level code emission routines depending on the addressing mode.
// If this returns true then you have to use the rotate_imm and immed_8
// that it returns, because it may have already changed the instruction
......@@ -664,6 +675,15 @@ static bool fits_shifter(uint32_t imm32,
if (fits_shifter(~imm32, rotate_imm, immed_8, NULL)) {
*instr ^= kMovMvnFlip;
return true;
} else if ((*instr & kMovLeaveCCMask) == kMovLeaveCCPattern) {
if (CpuFeatures::IsSupported(ARMv7)) {
if (imm32 < 0x10000) {
*instr ^= kMovwLeaveCCFlip;
*instr |= EncodeMovwImmediate(imm32);
*rotate_imm = *immed_8 = 0; // Not used for movw.
return true;
}
}
}
} else if ((*instr & kCmpCmnMask) == kCmpCmnPattern) {
if (fits_shifter(-imm32, rotate_imm, immed_8, NULL)) {
......@@ -695,7 +715,7 @@ static bool fits_shifter(uint32_t imm32,
// if they can be encoded in the ARM's 12 bits of immediate-offset instruction
// space. There is no guarantee that the relocated location can be similarly
// encoded.
static bool MustUseIp(RelocInfo::Mode rmode) {
static bool MustUseConstantPool(RelocInfo::Mode rmode) {
if (rmode == RelocInfo::EXTERNAL_REFERENCE) {
#ifdef DEBUG
if (!Serializer::enabled()) {
......@@ -712,7 +732,7 @@ static bool MustUseIp(RelocInfo::Mode rmode) {
bool Operand::is_single_instruction() const {
if (rm_.is_valid()) return true;
if (MustUseIp(rmode_)) return false;
if (MustUseConstantPool(rmode_)) return false;
uint32_t dummy1, dummy2;
return fits_shifter(imm32_, &dummy1, &dummy2, NULL);
}
......@@ -728,19 +748,34 @@ void Assembler::addrmod1(Instr instr,
// Immediate.
uint32_t rotate_imm;
uint32_t immed_8;
if (MustUseIp(x.rmode_) ||
if (MustUseConstantPool(x.rmode_) ||
!fits_shifter(x.imm32_, &rotate_imm, &immed_8, &instr)) {
// The immediate operand cannot be encoded as a shifter operand, so load
// it first to register ip and change the original instruction to use ip.
// However, if the original instruction is a 'mov rd, x' (not setting the
// condition code), then replace it with a 'ldr rd, [pc]'.
RecordRelocInfo(x.rmode_, x.imm32_);
CHECK(!rn.is(ip)); // rn should never be ip, or will be trashed
Condition cond = static_cast<Condition>(instr & CondMask);
if ((instr & ~CondMask) == 13*B21) { // mov, S not set
ldr(rd, MemOperand(pc, 0), cond);
if (MustUseConstantPool(x.rmode_) ||
!CpuFeatures::IsSupported(ARMv7)) {
RecordRelocInfo(x.rmode_, x.imm32_);
ldr(rd, MemOperand(pc, 0), cond);
} else {
// Will probably use movw, will certainly not use constant pool.
mov(rd, Operand(x.imm32_ & 0xffff), LeaveCC, cond);
movt(rd, static_cast<uint32_t>(x.imm32_) >> 16, cond);
}
} else {
ldr(ip, MemOperand(pc, 0), cond);
// If this is not a mov or mvn instruction we may still be able to avoid
// a constant pool entry by using mvn or movw.
if (!MustUseConstantPool(x.rmode_) &&
(instr & kMovMvnMask) != kMovMvnPattern) {
mov(ip, x, LeaveCC, cond);
} else {
RecordRelocInfo(x.rmode_, x.imm32_);
ldr(ip, MemOperand(pc, 0), cond);
}
addrmod1(instr, rn, rd, Operand(ip));
}
return;
......@@ -1051,6 +1086,17 @@ void Assembler::mov(Register dst, const Operand& src, SBit s, Condition cond) {
}
void Assembler::movw(Register reg, uint32_t immediate, Condition cond) {
ASSERT(immediate < 0x10000);
mov(reg, Operand(immediate), LeaveCC, cond);
}
void Assembler::movt(Register reg, uint32_t immediate, Condition cond) {
emit(cond | 0x34*B20 | reg.code()*B12 | EncodeMovwImmediate(immediate));
}
void Assembler::bic(Register dst, Register src1, const Operand& src2,
SBit s, Condition cond) {
addrmod1(cond | 14*B21 | s, src1, dst, src2);
......@@ -1231,7 +1277,7 @@ void Assembler::msr(SRegisterFieldMask fields, const Operand& src,
// Immediate.
uint32_t rotate_imm;
uint32_t immed_8;
if (MustUseIp(src.rmode_) ||
if (MustUseConstantPool(src.rmode_) ||
!fits_shifter(src.imm32_, &rotate_imm, &immed_8, NULL)) {
// Immediate operand cannot be encoded, load it first to register ip.
RecordRelocInfo(src.rmode_, src.imm32_);
......
......@@ -548,6 +548,12 @@ extern const Instr kMovMvnMask;
extern const Instr kMovMvnPattern;
extern const Instr kMovMvnFlip;
extern const Instr kMovLeaveCCMask;
extern const Instr kMovLeaveCCPattern;
extern const Instr kMovwMask;
extern const Instr kMovwPattern;
extern const Instr kMovwLeaveCCFlip;
extern const Instr kCmpCmnMask;
extern const Instr kCmpCmnPattern;
extern const Instr kCmpCmnFlip;
......@@ -775,6 +781,13 @@ class Assembler : public Malloced {
mov(dst, Operand(src), s, cond);
}
// ARMv7 instructions for loading a 32 bit immediate in two instructions.
// This may actually emit a different mov instruction, but on an ARMv7 it
// is guaranteed to only emit one instruction.
void movw(Register reg, uint32_t immediate, Condition cond = al);
// The constant for movt should be in the range 0-0xffff.
void movt(Register reg, uint32_t immediate, Condition cond = al);
void bic(Register dst, Register src1, const Operand& src2,
SBit s = LeaveCC, Condition cond = al);
......
......@@ -284,6 +284,9 @@ class Instr {
// with immediate
inline int RotateField() const { return Bits(11, 8); }
inline int Immed8Field() const { return Bits(7, 0); }
inline int Immed4Field() const { return Bits(19, 16); }
inline int ImmedMovwMovtField() const {
return Immed4Field() << 12 | Offset12Field(); }
// Fields used in Load/Store instructions
inline int PUField() const { return Bits(24, 23); }
......
......@@ -101,6 +101,7 @@ class Decoder {
void PrintSRegister(int reg);
void PrintDRegister(int reg);
int FormatVFPRegister(Instr* instr, const char* format);
void PrintMovwMovt(Instr* instr);
int FormatVFPinstruction(Instr* instr, const char* format);
void PrintCondition(Instr* instr);
void PrintShiftRm(Instr* instr);
......@@ -375,6 +376,16 @@ int Decoder::FormatVFPinstruction(Instr* instr, const char* format) {
}
// Print the movw or movt instruction.
void Decoder::PrintMovwMovt(Instr* instr) {
int imm = instr->ImmedMovwMovtField();
int rd = instr->RdField();
PrintRegister(rd);
out_buffer_pos_ += v8i::OS::SNPrintF(out_buffer_ + out_buffer_pos_,
", #%d", imm);
}
// FormatOption takes a formatting string and interprets it based on
// the current instructions. The format string points to the first
// character of the option string (the option escape has already been
......@@ -430,7 +441,12 @@ int Decoder::FormatOption(Instr* instr, const char* format) {
return 1;
}
case 'm': {
if (format[1] == 'e') { // 'memop: load/store instructions
if (format[1] == 'w') {
// 'mw: movt/movw instructions.
PrintMovwMovt(instr);
return 2;
}
if (format[1] == 'e') { // 'memop: load/store instructions.
ASSERT(STRING_STARTS_WITH(format, "memop"));
if (instr->HasL()) {
Print("ldr");
......@@ -776,7 +792,7 @@ void Decoder::DecodeType01(Instr* instr) {
if (instr->HasS()) {
Format(instr, "tst'cond 'rn, 'shift_op");
} else {
Unknown(instr); // not used by V8
Format(instr, "movw'cond 'mw");
}
break;
}
......@@ -794,7 +810,7 @@ void Decoder::DecodeType01(Instr* instr) {
if (instr->HasS()) {
Format(instr, "cmp'cond 'rn, 'shift_op");
} else {
Unknown(instr); // not used by V8
Format(instr, "movt'cond 'mw");
}
break;
}
......
......@@ -1859,7 +1859,9 @@ void Simulator::DecodeType01(Instr* instr) {
SetNZFlags(alu_out);
SetCFlag(shifter_carry_out);
} else {
UNIMPLEMENTED();
// Format(instr, "movw'cond 'rd, 'imm").
alu_out = instr->ImmedMovwMovtField();
set_register(rd, alu_out);
}
break;
}
......@@ -1888,7 +1890,10 @@ void Simulator::DecodeType01(Instr* instr) {
SetCFlag(!BorrowFrom(rn_val, shifter_operand));
SetVFlag(OverflowFrom(alu_out, rn_val, shifter_operand, false));
} else {
UNIMPLEMENTED();
// Format(instr, "movt'cond 'rd, 'imm").
alu_out = (get_register(rd) & 0xffff) |
(instr->ImmedMovwMovtField() << 16);
set_register(rd, alu_out);
}
break;
}
......
......@@ -1747,9 +1747,11 @@ bool RegExpNode::EmitQuickCheck(RegExpCompiler* compiler,
if ((mask & char_mask) == char_mask) need_mask = false;
mask &= char_mask;
} else {
// For 2-character preloads in ASCII mode we also use a 16 bit load with
// zero extend.
// For 2-character preloads in ASCII mode or 1-character preloads in
// TWO_BYTE mode we also use a 16 bit load with zero extend.
if (details->characters() == 2 && compiler->ascii()) {
if ((mask & 0x7f7f) == 0x7f7f) need_mask = false;
} else if (details->characters() == 1 && !compiler->ascii()) {
if ((mask & 0xffff) == 0xffff) need_mask = false;
} else {
if (mask == 0xffffffff) need_mask = false;
......
......@@ -269,6 +269,33 @@ TEST(Type0) {
COMPARE(mvn(r6, Operand(-1), LeaveCC, ne),
"13a06000 movne r6, #0");
// mov -> movw.
if (CpuFeatures::IsSupported(ARMv7)) {
COMPARE(mov(r5, Operand(0x01234), LeaveCC, ne),
"13015234 movwne r5, #4660");
// We only disassemble one instruction so the eor instruction is not here.
COMPARE(eor(r5, r4, Operand(0x1234), LeaveCC, ne),
"1301c234 movwne ip, #4660");
// Movw can't do setcc so we don't get that here. Mov immediate with setcc
// is pretty strange anyway.
COMPARE(mov(r5, Operand(0x01234), SetCC, ne),
"159fc000 ldrne ip, [pc, #+0]");
// We only disassemble one instruction so the eor instruction is not here.
// The eor does the setcc so we get a movw here.
COMPARE(eor(r5, r4, Operand(0x1234), SetCC, ne),
"1301c234 movwne ip, #4660");
COMPARE(movt(r5, 0x4321, ne),
"13445321 movtne r5, #17185");
COMPARE(movw(r5, 0xabcd, eq),
"030a5bcd movweq r5, #43981");
}
// Eor doesn't have an eor-negative variant, but we can do an mvn followed by
// an eor to get the same effect.
COMPARE(eor(r5, r4, Operand(0xffffff34), SetCC, ne),
"13e0c0cb mvnne ip, #203");
// and <-> bic.
COMPARE(and_(r3, r5, Operand(0xfc03ffff)),
"e3c537ff bic r3, r5, #66846720");
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment