Commit 67c0ec6f authored by erik.corry@gmail.com's avatar erik.corry@gmail.com

Update and improve support for ARMv7 bitfield instructions.

This is a commit of http://codereview.chromium.org/2124022
for Rodolph Perfetta.  I changed the test in
test-assembler-arm.cc so it only runs if ARMv7 is supported.


git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@4744 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent fc7c5f78
......@@ -903,20 +903,6 @@ void Assembler::bx(Register target, Condition cond) { // v5 and above, plus v4t
// Data-processing instructions.
// UBFX <Rd>,<Rn>,#<lsb>,#<width - 1>
// Instruction details available in ARM DDI 0406A, A8-464.
// cond(31-28) | 01111(27-23)| 1(22) | 1(21) | widthm1(20-16) |
// Rd(15-12) | lsb(11-7) | 101(6-4) | Rn(3-0)
void Assembler::ubfx(Register dst, Register src1, const Operand& src2,
const Operand& src3, Condition cond) {
ASSERT(!src2.rm_.is_valid() && !src3.rm_.is_valid());
ASSERT(static_cast<uint32_t>(src2.imm32_) <= 0x1f);
ASSERT(static_cast<uint32_t>(src3.imm32_) <= 0x1f);
emit(cond | 0x3F*B21 | src3.imm32_*B16 |
dst.code()*B12 | src2.imm32_*B7 | 0x5*B4 | src1.code());
}
void Assembler::and_(Register dst, Register src1, const Operand& src2,
SBit s, Condition cond) {
addrmod1(cond | 0*B21 | s, src1, dst, src2);
......@@ -1106,6 +1092,82 @@ void Assembler::clz(Register dst, Register src, Condition cond) {
}
// Bitfield manipulation instructions.
// Unsigned bit field extract.
// Extracts #width adjacent bits from position #lsb in a register, and
// writes them to the low bits of a destination register.
// ubfx dst, src, #lsb, #width
void Assembler::ubfx(Register dst,
Register src,
int lsb,
int width,
Condition cond) {
// v7 and above.
ASSERT(CpuFeatures::IsSupported(ARMv7));
ASSERT(!dst.is(pc) && !src.is(pc));
ASSERT((lsb >= 0) && (lsb <= 31));
ASSERT((width >= 1) && (width <= (32 - lsb)));
emit(cond | 0xf*B23 | B22 | B21 | (width - 1)*B16 | dst.code()*B12 |
lsb*B7 | B6 | B4 | src.code());
}
// Signed bit field extract.
// Extracts #width adjacent bits from position #lsb in a register, and
// writes them to the low bits of a destination register. The extracted
// value is sign extended to fill the destination register.
// sbfx dst, src, #lsb, #width
void Assembler::sbfx(Register dst,
Register src,
int lsb,
int width,
Condition cond) {
// v7 and above.
ASSERT(CpuFeatures::IsSupported(ARMv7));
ASSERT(!dst.is(pc) && !src.is(pc));
ASSERT((lsb >= 0) && (lsb <= 31));
ASSERT((width >= 1) && (width <= (32 - lsb)));
emit(cond | 0xf*B23 | B21 | (width - 1)*B16 | dst.code()*B12 |
lsb*B7 | B6 | B4 | src.code());
}
// Bit field clear.
// Sets #width adjacent bits at position #lsb in the destination register
// to zero, preserving the value of the other bits.
// bfc dst, #lsb, #width
void Assembler::bfc(Register dst, int lsb, int width, Condition cond) {
// v7 and above.
ASSERT(CpuFeatures::IsSupported(ARMv7));
ASSERT(!dst.is(pc));
ASSERT((lsb >= 0) && (lsb <= 31));
ASSERT((width >= 1) && (width <= (32 - lsb)));
int msb = lsb + width - 1;
emit(cond | 0x1f*B22 | msb*B16 | dst.code()*B12 | lsb*B7 | B4 | 0xf);
}
// Bit field insert.
// Inserts #width adjacent bits from the low bits of the source register
// into position #lsb of the destination register.
// bfi dst, src, #lsb, #width
void Assembler::bfi(Register dst,
Register src,
int lsb,
int width,
Condition cond) {
// v7 and above.
ASSERT(CpuFeatures::IsSupported(ARMv7));
ASSERT(!dst.is(pc) && !src.is(pc));
ASSERT((lsb >= 0) && (lsb <= 31));
ASSERT((width >= 1) && (width <= (32 - lsb)));
int msb = lsb + width - 1;
emit(cond | 0x1f*B22 | msb*B16 | dst.code()*B12 | lsb*B7 | B4 |
src.code());
}
// Status register access instructions.
void Assembler::mrs(Register dst, SRegister s, Condition cond) {
ASSERT(!dst.is(pc));
......
......@@ -671,8 +671,6 @@ class Assembler : public Malloced {
void blx(Label* L) { blx(branch_offset(L, false)); } // v5 and above
// Data-processing instructions
void ubfx(Register dst, Register src1, const Operand& src2,
const Operand& src3, Condition cond = al);
void and_(Register dst, Register src1, const Operand& src2,
SBit s = LeaveCC, Condition cond = al);
......@@ -759,6 +757,19 @@ class Assembler : public Malloced {
void clz(Register dst, Register src, Condition cond = al); // v5 and above
// Bitfield manipulation instructions. v7 and above.
void ubfx(Register dst, Register src, int lsb, int width,
Condition cond = al);
void sbfx(Register dst, Register src, int lsb, int width,
Condition cond = al);
void bfc(Register dst, int lsb, int width, Condition cond = al);
void bfi(Register dst, Register src, int lsb, int width,
Condition cond = al);
// Status register access instructions
void mrs(Register dst, SRegister s, Condition cond = al);
......
......@@ -401,6 +401,20 @@ int Decoder::FormatOption(Instr* instr, const char* format) {
PrintCondition(instr);
return 4;
}
case 'f': { // 'f: bitfield instructions - v7 and above.
uint32_t lsbit = instr->Bits(11, 7);
uint32_t width = instr->Bits(20, 16) + 1;
if (instr->Bit(21) == 0) {
// BFC/BFI:
// Bits 20-16 represent most-significant bit. Covert to width.
width -= lsbit;
ASSERT(width > 0);
}
ASSERT((width + lsbit) <= 32);
out_buffer_pos_ += v8i::OS::SNPrintF(out_buffer_ + out_buffer_pos_,
"#%d, #%d", lsbit, width);
return 1;
}
case 'h': { // 'h: halfword operation for extra loads and stores
if (instr->HasH()) {
Print("h");
......@@ -446,16 +460,6 @@ int Decoder::FormatOption(Instr* instr, const char* format) {
out_buffer_pos_ += v8i::OS::SNPrintF(out_buffer_ + out_buffer_pos_,
"%d", instr->Offset12Field());
return 5;
} else if ((format[3] == '1') && (format[4] == '6')) {
ASSERT(STRING_STARTS_WITH(format, "off16to20"));
out_buffer_pos_ += v8i::OS::SNPrintF(out_buffer_ + out_buffer_pos_,
"%d", instr->Bits(20, 16) +1);
return 9;
} else if (format[3] == '7') {
ASSERT(STRING_STARTS_WITH(format, "off7to11"));
out_buffer_pos_ += v8i::OS::SNPrintF(out_buffer_ + out_buffer_pos_,
"%d", instr->ShiftAmountField());
return 8;
} else if (format[3] == '0') {
// 'off0to3and8to19 16-bit immediate encoded in bits 19-8 and 3-0.
ASSERT(STRING_STARTS_WITH(format, "off0to3and8to19"));
......@@ -882,10 +886,26 @@ void Decoder::DecodeType3(Instr* instr) {
case 3: {
if (instr->HasW() && (instr->Bits(6, 4) == 0x5)) {
uint32_t widthminus1 = static_cast<uint32_t>(instr->Bits(20, 16));
uint32_t lsbit = static_cast<uint32_t>(instr->ShiftAmountField());
uint32_t lsbit = static_cast<uint32_t>(instr->Bits(11, 7));
uint32_t msbit = widthminus1 + lsbit;
if (msbit <= 31) {
Format(instr, "ubfx'cond 'rd, 'rm, #'off7to11, #'off16to20");
if (instr->Bit(22)) {
Format(instr, "ubfx'cond 'rd, 'rm, 'f");
} else {
Format(instr, "sbfx'cond 'rd, 'rm, 'f");
}
} else {
UNREACHABLE();
}
} else if (!instr->HasW() && (instr->Bits(6, 4) == 0x1)) {
uint32_t lsbit = static_cast<uint32_t>(instr->Bits(11, 7));
uint32_t msbit = static_cast<uint32_t>(instr->Bits(20, 16));
if (msbit >= lsbit) {
if (instr->RmField() == 15) {
Format(instr, "bfc'cond 'rd, 'f");
} else {
Format(instr, "bfi'cond 'rd, 'rm, 'f");
}
} else {
UNREACHABLE();
}
......
......@@ -1287,7 +1287,7 @@ void MacroAssembler::GetLeastBitsFromSmi(Register dst,
Register src,
int num_least_bits) {
if (CpuFeatures::IsSupported(ARMv7)) {
ubfx(dst, src, Operand(kSmiTagSize), Operand(num_least_bits - 1));
ubfx(dst, src, kSmiTagSize, num_least_bits);
} else {
mov(dst, Operand(src, ASR, kSmiTagSize));
and_(dst, dst, Operand((1 << num_least_bits) - 1));
......
......@@ -2031,7 +2031,6 @@ void Simulator::DecodeType2(Instr* instr) {
void Simulator::DecodeType3(Instr* instr) {
ASSERT(instr->Bits(6, 4) == 0x5 || instr->Bit(4) == 0);
int rd = instr->RdField();
int rn = instr->RnField();
int32_t rn_val = get_register(rn);
......@@ -2058,17 +2057,47 @@ void Simulator::DecodeType3(Instr* instr) {
break;
}
case 3: {
// UBFX.
if (instr->HasW() && (instr->Bits(6, 4) == 0x5)) {
uint32_t widthminus1 = static_cast<uint32_t>(instr->Bits(20, 16));
uint32_t lsbit = static_cast<uint32_t>(instr->ShiftAmountField());
uint32_t lsbit = static_cast<uint32_t>(instr->Bits(11, 7));
uint32_t msbit = widthminus1 + lsbit;
if (msbit <= 31) {
uint32_t rm_val =
static_cast<uint32_t>(get_register(instr->RmField()));
uint32_t extr_val = rm_val << (31 - msbit);
extr_val = extr_val >> (31 - widthminus1);
set_register(instr->RdField(), extr_val);
if (instr->Bit(22)) {
// ubfx - unsigned bitfield extract.
uint32_t rm_val =
static_cast<uint32_t>(get_register(instr->RmField()));
uint32_t extr_val = rm_val << (31 - msbit);
extr_val = extr_val >> (31 - widthminus1);
set_register(instr->RdField(), extr_val);
} else {
// sbfx - signed bitfield extract.
int32_t rm_val = get_register(instr->RmField());
int32_t extr_val = rm_val << (31 - msbit);
extr_val = extr_val >> (31 - widthminus1);
set_register(instr->RdField(), extr_val);
}
} else {
UNREACHABLE();
}
return;
} else if (!instr->HasW() && (instr->Bits(6, 4) == 0x1)) {
uint32_t lsbit = static_cast<uint32_t>(instr->Bits(11, 7));
uint32_t msbit = static_cast<uint32_t>(instr->Bits(20, 16));
if (msbit >= lsbit) {
// bfc or bfi - bitfield clear/insert.
uint32_t rd_val =
static_cast<uint32_t>(get_register(instr->RdField()));
uint32_t bitcount = msbit - lsbit + 1;
uint32_t mask = (1 << bitcount) - 1;
rd_val &= ~(mask << lsbit);
if (instr->RmField() != 15) {
// bfi - bitfield insert.
uint32_t rm_val =
static_cast<uint32_t>(get_register(instr->RmField()));
rm_val &= mask;
rd_val |= rm_val << lsbit;
}
set_register(instr->RdField(), rd_val);
} else {
UNREACHABLE();
}
......
......@@ -280,4 +280,40 @@ TEST(4) {
}
}
TEST(5) {
// Test the ARMv7 bitfield instructions.
InitializeVM();
v8::HandleScope scope;
Assembler assm(NULL, 0);
if (CpuFeatures::IsSupported(ARMv7)) {
CpuFeatures::Scope scope(ARMv7);
// On entry, r0 = 0xAAAAAAAA = 0b10..10101010.
__ ubfx(r0, r0, 1, 12); // 0b00..010101010101 = 0x555
__ sbfx(r0, r0, 0, 5); // 0b11..111111110101 = -11
__ bfc(r0, 1, 3); // 0b11..111111110001 = -15
__ mov(r1, Operand(7));
__ bfi(r0, r1, 3, 3); // 0b11..111111111001 = -7
__ mov(pc, Operand(lr));
CodeDesc desc;
assm.GetCode(&desc);
Object* code = Heap::CreateCode(desc,
NULL,
Code::ComputeFlags(Code::STUB),
Handle<Object>(Heap::undefined_value()));
CHECK(code->IsCode());
#ifdef DEBUG
Code::cast(code)->Print();
#endif
F1 f = FUNCTION_CAST<F1>(Code::cast(code)->entry());
int res = reinterpret_cast<int>(
CALL_GENERATED_CODE(f, 0xAAAAAAAA, 0, 0, 0, 0));
::printf("f() = %d\n", res);
CHECK_EQ(-7, res);
}
}
#undef __
......@@ -289,3 +289,49 @@ TEST(Type1) {
VERIFY_RUN();
}
TEST(Type3) {
SETUP();
if (CpuFeatures::IsSupported(ARMv7)) {
COMPARE(ubfx(r0, r1, 5, 10),
"e7e902d1 ubfx r0, r1, #5, #10");
COMPARE(ubfx(r1, r0, 5, 10),
"e7e912d0 ubfx r1, r0, #5, #10");
COMPARE(ubfx(r0, r1, 31, 1),
"e7e00fd1 ubfx r0, r1, #31, #1");
COMPARE(ubfx(r1, r0, 31, 1),
"e7e01fd0 ubfx r1, r0, #31, #1");
COMPARE(sbfx(r0, r1, 5, 10),
"e7a902d1 sbfx r0, r1, #5, #10");
COMPARE(sbfx(r1, r0, 5, 10),
"e7a912d0 sbfx r1, r0, #5, #10");
COMPARE(sbfx(r0, r1, 31, 1),
"e7a00fd1 sbfx r0, r1, #31, #1");
COMPARE(sbfx(r1, r0, 31, 1),
"e7a01fd0 sbfx r1, r0, #31, #1");
COMPARE(bfc(r0, 5, 10),
"e7ce029f bfc r0, #5, #10");
COMPARE(bfc(r1, 5, 10),
"e7ce129f bfc r1, #5, #10");
COMPARE(bfc(r0, 31, 1),
"e7df0f9f bfc r0, #31, #1");
COMPARE(bfc(r1, 31, 1),
"e7df1f9f bfc r1, #31, #1");
COMPARE(bfi(r0, r1, 5, 10),
"e7ce0291 bfi r0, r1, #5, #10");
COMPARE(bfi(r1, r0, 5, 10),
"e7ce1290 bfi r1, r0, #5, #10");
COMPARE(bfi(r0, r1, 31, 1),
"e7df0f91 bfi r0, r1, #31, #1");
COMPARE(bfi(r1, r0, 31, 1),
"e7df1f90 bfi r1, r0, #31, #1");
}
VERIFY_RUN();
}
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