Landing for Rodolph Perfetta.
Add support for saturation instruction (ARMv6 or above). The byte array clamping code has been updated accordingly. Review URL: http://codereview.chromium.org/3036008/show git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@5106 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
c9c7f8834e
commit
74f9789f61
@ -1192,6 +1192,30 @@ void Assembler::clz(Register dst, Register src, Condition cond) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Saturating instructions.
|
||||||
|
|
||||||
|
// Unsigned saturate.
|
||||||
|
void Assembler::usat(Register dst,
|
||||||
|
int satpos,
|
||||||
|
const Operand& src,
|
||||||
|
Condition cond) {
|
||||||
|
// v6 and above.
|
||||||
|
ASSERT(CpuFeatures::IsSupported(ARMv7));
|
||||||
|
ASSERT(!dst.is(pc) && !src.rm_.is(pc));
|
||||||
|
ASSERT((satpos >= 0) && (satpos <= 31));
|
||||||
|
ASSERT((src.shift_op_ == ASR) || (src.shift_op_ == LSL));
|
||||||
|
ASSERT(src.rs_.is(no_reg));
|
||||||
|
|
||||||
|
int sh = 0;
|
||||||
|
if (src.shift_op_ == ASR) {
|
||||||
|
sh = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
emit(cond | 0x6*B24 | 0xe*B20 | satpos*B16 | dst.code()*B12 |
|
||||||
|
src.shift_imm_*B7 | sh*B6 | 0x1*B4 | src.rm_.code());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Bitfield manipulation instructions.
|
// Bitfield manipulation instructions.
|
||||||
|
|
||||||
// Unsigned bit field extract.
|
// Unsigned bit field extract.
|
||||||
|
@ -445,6 +445,8 @@ class Operand BASE_EMBEDDED {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Register rm() const { return rm_; }
|
Register rm() const { return rm_; }
|
||||||
|
Register rs() const { return rs_; }
|
||||||
|
ShiftOp shift_op() const { return shift_op_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Register rm_;
|
Register rm_;
|
||||||
@ -834,6 +836,25 @@ class Assembler : public Malloced {
|
|||||||
|
|
||||||
void clz(Register dst, Register src, Condition cond = al); // v5 and above
|
void clz(Register dst, Register src, Condition cond = al); // v5 and above
|
||||||
|
|
||||||
|
// Saturating instructions. v6 and above.
|
||||||
|
|
||||||
|
// Unsigned saturate.
|
||||||
|
//
|
||||||
|
// Saturate an optionally shifted signed value to an unsigned range.
|
||||||
|
//
|
||||||
|
// usat dst, #satpos, src
|
||||||
|
// usat dst, #satpos, src, lsl #sh
|
||||||
|
// usat dst, #satpos, src, asr #sh
|
||||||
|
//
|
||||||
|
// Register dst will contain:
|
||||||
|
//
|
||||||
|
// 0, if s < 0
|
||||||
|
// (1 << satpos) - 1, if s > ((1 << satpos) - 1)
|
||||||
|
// s, otherwise
|
||||||
|
//
|
||||||
|
// where s is the contents of src after shifting (if used.)
|
||||||
|
void usat(Register dst, int satpos, const Operand& src, Condition cond = al);
|
||||||
|
|
||||||
// Bitfield manipulation instructions. v7 and above.
|
// Bitfield manipulation instructions. v7 and above.
|
||||||
|
|
||||||
void ubfx(Register dst, Register src, int lsb, int width,
|
void ubfx(Register dst, Register src, int lsb, int width,
|
||||||
|
@ -106,6 +106,7 @@ class Decoder {
|
|||||||
void PrintCondition(Instr* instr);
|
void PrintCondition(Instr* instr);
|
||||||
void PrintShiftRm(Instr* instr);
|
void PrintShiftRm(Instr* instr);
|
||||||
void PrintShiftImm(Instr* instr);
|
void PrintShiftImm(Instr* instr);
|
||||||
|
void PrintShiftSat(Instr* instr);
|
||||||
void PrintPU(Instr* instr);
|
void PrintPU(Instr* instr);
|
||||||
void PrintSoftwareInterrupt(SoftwareInterruptCodes swi);
|
void PrintSoftwareInterrupt(SoftwareInterruptCodes swi);
|
||||||
|
|
||||||
@ -248,6 +249,18 @@ void Decoder::PrintShiftImm(Instr* instr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Print the optional shift and immediate used by saturating instructions.
|
||||||
|
void Decoder::PrintShiftSat(Instr* instr) {
|
||||||
|
int shift = instr->Bits(11, 7);
|
||||||
|
if (shift > 0) {
|
||||||
|
out_buffer_pos_ += v8i::OS::SNPrintF(out_buffer_ + out_buffer_pos_,
|
||||||
|
", %s #%d",
|
||||||
|
shift_names[instr->Bit(6) * 2],
|
||||||
|
instr->Bits(11, 7));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Print PU formatting to reduce complexity of FormatOption.
|
// Print PU formatting to reduce complexity of FormatOption.
|
||||||
void Decoder::PrintPU(Instr* instr) {
|
void Decoder::PrintPU(Instr* instr) {
|
||||||
switch (instr->PUField()) {
|
switch (instr->PUField()) {
|
||||||
@ -440,6 +453,20 @@ int Decoder::FormatOption(Instr* instr, const char* format) {
|
|||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
case 'i': { // 'i: immediate value from adjacent bits.
|
||||||
|
// Expects tokens in the form imm%02d@%02d, ie. imm05@07, imm10@16
|
||||||
|
int width = (format[3] - '0') * 10 + (format[4] - '0');
|
||||||
|
int lsb = (format[6] - '0') * 10 + (format[7] - '0');
|
||||||
|
|
||||||
|
ASSERT((width >= 1) && (width <= 32));
|
||||||
|
ASSERT((lsb >= 0) && (lsb <= 31));
|
||||||
|
ASSERT((width + lsb) <= 32);
|
||||||
|
|
||||||
|
out_buffer_pos_ += v8i::OS::SNPrintF(out_buffer_ + out_buffer_pos_,
|
||||||
|
"#%d",
|
||||||
|
instr->Bits(width + lsb - 1, lsb));
|
||||||
|
return 8;
|
||||||
|
}
|
||||||
case 'l': { // 'l: branch and link
|
case 'l': { // 'l: branch and link
|
||||||
if (instr->HasLink()) {
|
if (instr->HasLink()) {
|
||||||
Print("l");
|
Print("l");
|
||||||
@ -507,7 +534,7 @@ int Decoder::FormatOption(Instr* instr, const char* format) {
|
|||||||
return FormatRegister(instr, format);
|
return FormatRegister(instr, format);
|
||||||
}
|
}
|
||||||
case 's': {
|
case 's': {
|
||||||
if (format[1] == 'h') { // 'shift_op or 'shift_rm
|
if (format[1] == 'h') { // 'shift_op or 'shift_rm or 'shift_sat.
|
||||||
if (format[6] == 'o') { // 'shift_op
|
if (format[6] == 'o') { // 'shift_op
|
||||||
ASSERT(STRING_STARTS_WITH(format, "shift_op"));
|
ASSERT(STRING_STARTS_WITH(format, "shift_op"));
|
||||||
if (instr->TypeField() == 0) {
|
if (instr->TypeField() == 0) {
|
||||||
@ -517,6 +544,10 @@ int Decoder::FormatOption(Instr* instr, const char* format) {
|
|||||||
PrintShiftImm(instr);
|
PrintShiftImm(instr);
|
||||||
}
|
}
|
||||||
return 8;
|
return 8;
|
||||||
|
} else if (format[6] == 's') { // 'shift_sat.
|
||||||
|
ASSERT(STRING_STARTS_WITH(format, "shift_sat"));
|
||||||
|
PrintShiftSat(instr);
|
||||||
|
return 9;
|
||||||
} else { // 'shift_rm
|
} else { // 'shift_rm
|
||||||
ASSERT(STRING_STARTS_WITH(format, "shift_rm"));
|
ASSERT(STRING_STARTS_WITH(format, "shift_rm"));
|
||||||
PrintShiftRm(instr);
|
PrintShiftRm(instr);
|
||||||
@ -897,8 +928,16 @@ void Decoder::DecodeType3(Instr* instr) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 1: {
|
case 1: {
|
||||||
ASSERT(!instr->HasW());
|
if (instr->HasW()) {
|
||||||
Format(instr, "'memop'cond'b 'rd, ['rn], +'shift_rm");
|
ASSERT(instr->Bits(5, 4) == 0x1);
|
||||||
|
if (instr->Bit(22) == 0x1) {
|
||||||
|
Format(instr, "usat 'rd, 'imm05@16, 'rm'shift_sat");
|
||||||
|
} else {
|
||||||
|
UNREACHABLE(); // SSAT.
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Format(instr, "'memop'cond'b 'rd, ['rn], +'shift_rm");
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 2: {
|
case 2: {
|
||||||
|
@ -1674,14 +1674,8 @@ void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm) {
|
|||||||
__ cmp(r4, Operand(ip));
|
__ cmp(r4, Operand(ip));
|
||||||
__ b(hs, &slow);
|
__ b(hs, &slow);
|
||||||
__ mov(r5, Operand(value, ASR, kSmiTagSize)); // Untag the value.
|
__ mov(r5, Operand(value, ASR, kSmiTagSize)); // Untag the value.
|
||||||
{ // Clamp the value to [0..255].
|
__ Usat(r5, 8, Operand(r5)); // Clamp the value to [0..255].
|
||||||
Label done;
|
|
||||||
__ tst(r5, Operand(0xFFFFFF00));
|
|
||||||
__ b(eq, &done);
|
|
||||||
__ mov(r5, Operand(0), LeaveCC, mi); // 0 if negative.
|
|
||||||
__ mov(r5, Operand(255), LeaveCC, pl); // 255 if positive.
|
|
||||||
__ bind(&done);
|
|
||||||
}
|
|
||||||
// Get the pointer to the external array. This clobbers elements.
|
// Get the pointer to the external array. This clobbers elements.
|
||||||
__ ldr(elements,
|
__ ldr(elements,
|
||||||
FieldMemOperand(elements, PixelArray::kExternalPointerOffset));
|
FieldMemOperand(elements, PixelArray::kExternalPointerOffset));
|
||||||
|
@ -281,6 +281,37 @@ void MacroAssembler::Bfc(Register dst, int lsb, int width, Condition cond) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void MacroAssembler::Usat(Register dst, int satpos, const Operand& src,
|
||||||
|
Condition cond) {
|
||||||
|
if (!CpuFeatures::IsSupported(ARMv7)) {
|
||||||
|
ASSERT(!dst.is(pc) && !src.rm().is(pc));
|
||||||
|
ASSERT((satpos >= 0) && (satpos <= 31));
|
||||||
|
|
||||||
|
// These asserts are required to ensure compatibility with the ARMv7
|
||||||
|
// implementation.
|
||||||
|
ASSERT((src.shift_op() == ASR) || (src.shift_op() == LSL));
|
||||||
|
ASSERT(src.rs().is(no_reg));
|
||||||
|
|
||||||
|
Label done;
|
||||||
|
int satval = (1 << satpos) - 1;
|
||||||
|
|
||||||
|
if (cond != al) {
|
||||||
|
b(NegateCondition(cond), &done); // Skip saturate if !condition.
|
||||||
|
}
|
||||||
|
if (!(src.is_reg() && dst.is(src.rm()))) {
|
||||||
|
mov(dst, src);
|
||||||
|
}
|
||||||
|
tst(dst, Operand(~satval));
|
||||||
|
b(eq, &done);
|
||||||
|
mov(dst, Operand(0), LeaveCC, mi); // 0 if negative.
|
||||||
|
mov(dst, Operand(satval), LeaveCC, pl); // satval if positive.
|
||||||
|
bind(&done);
|
||||||
|
} else {
|
||||||
|
usat(dst, satpos, src, cond);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void MacroAssembler::SmiJumpTable(Register index, Vector<Label*> targets) {
|
void MacroAssembler::SmiJumpTable(Register index, Vector<Label*> targets) {
|
||||||
// Empty the const pool.
|
// Empty the const pool.
|
||||||
CheckConstPool(true, true);
|
CheckConstPool(true, true);
|
||||||
|
@ -112,6 +112,8 @@ class MacroAssembler: public Assembler {
|
|||||||
void Sbfx(Register dst, Register src, int lsb, int width,
|
void Sbfx(Register dst, Register src, int lsb, int width,
|
||||||
Condition cond = al);
|
Condition cond = al);
|
||||||
void Bfc(Register dst, int lsb, int width, Condition cond = al);
|
void Bfc(Register dst, int lsb, int width, Condition cond = al);
|
||||||
|
void Usat(Register dst, int satpos, const Operand& src,
|
||||||
|
Condition cond = al);
|
||||||
|
|
||||||
void Call(Label* target);
|
void Call(Label* target);
|
||||||
void Move(Register dst, Handle<Object> value);
|
void Move(Register dst, Handle<Object> value);
|
||||||
|
@ -2047,11 +2047,41 @@ void Simulator::DecodeType3(Instr* instr) {
|
|||||||
case 0: {
|
case 0: {
|
||||||
ASSERT(!instr->HasW());
|
ASSERT(!instr->HasW());
|
||||||
Format(instr, "'memop'cond'b 'rd, ['rn], -'shift_rm");
|
Format(instr, "'memop'cond'b 'rd, ['rn], -'shift_rm");
|
||||||
|
UNIMPLEMENTED();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 1: {
|
case 1: {
|
||||||
ASSERT(!instr->HasW());
|
if (instr->HasW()) {
|
||||||
Format(instr, "'memop'cond'b 'rd, ['rn], +'shift_rm");
|
ASSERT(instr->Bits(5, 4) == 0x1);
|
||||||
|
|
||||||
|
if (instr->Bit(22) == 0x1) { // USAT.
|
||||||
|
int32_t sat_pos = instr->Bits(20, 16);
|
||||||
|
int32_t sat_val = (1 << sat_pos) - 1;
|
||||||
|
int32_t shift = instr->Bits(11, 7);
|
||||||
|
int32_t shift_type = instr->Bit(6);
|
||||||
|
int32_t rm_val = get_register(instr->RmField());
|
||||||
|
if (shift_type == 0) { // LSL
|
||||||
|
rm_val <<= shift;
|
||||||
|
} else { // ASR
|
||||||
|
rm_val >>= shift;
|
||||||
|
}
|
||||||
|
// If saturation occurs, the Q flag should be set in the CPSR.
|
||||||
|
// There is no Q flag yet, and no instruction (MRS) to read the
|
||||||
|
// CPSR directly.
|
||||||
|
if (rm_val > sat_val) {
|
||||||
|
rm_val = sat_val;
|
||||||
|
} else if (rm_val < 0) {
|
||||||
|
rm_val = 0;
|
||||||
|
}
|
||||||
|
set_register(rd, rm_val);
|
||||||
|
} else { // SSAT.
|
||||||
|
UNIMPLEMENTED();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
Format(instr, "'memop'cond'b 'rd, ['rn], +'shift_rm");
|
||||||
|
UNIMPLEMENTED();
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 2: {
|
case 2: {
|
||||||
|
@ -294,6 +294,9 @@ class Simulator {
|
|||||||
void TrashCallerSaveRegisters();
|
void TrashCallerSaveRegisters();
|
||||||
|
|
||||||
// Architecture state.
|
// Architecture state.
|
||||||
|
// Saturating instructions require a Q flag to indicate saturation.
|
||||||
|
// There is currently no way to read the CPSR directly, and thus read the Q
|
||||||
|
// flag, so this is left unimplemented.
|
||||||
int32_t registers_[16];
|
int32_t registers_[16];
|
||||||
bool n_flag_;
|
bool n_flag_;
|
||||||
bool z_flag_;
|
bool z_flag_;
|
||||||
|
@ -310,4 +310,38 @@ TEST(5) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TEST(6) {
|
||||||
|
// Test saturating instructions.
|
||||||
|
InitializeVM();
|
||||||
|
v8::HandleScope scope;
|
||||||
|
|
||||||
|
Assembler assm(NULL, 0);
|
||||||
|
|
||||||
|
if (CpuFeatures::IsSupported(ARMv7)) {
|
||||||
|
CpuFeatures::Scope scope(ARMv7);
|
||||||
|
__ usat(r1, 8, Operand(r0)); // Sat 0xFFFF to 0-255 = 0xFF.
|
||||||
|
__ usat(r2, 12, Operand(r0, ASR, 9)); // Sat (0xFFFF>>9) to 0-4095 = 0x7F.
|
||||||
|
__ usat(r3, 1, Operand(r0, LSL, 16)); // Sat (0xFFFF<<16) to 0-1 = 0x0.
|
||||||
|
__ add(r0, r1, Operand(r2));
|
||||||
|
__ add(r0, r0, Operand(r3));
|
||||||
|
__ mov(pc, Operand(lr));
|
||||||
|
|
||||||
|
CodeDesc desc;
|
||||||
|
assm.GetCode(&desc);
|
||||||
|
Object* code = Heap::CreateCode(desc,
|
||||||
|
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, 0xFFFF, 0, 0, 0, 0));
|
||||||
|
::printf("f() = %d\n", res);
|
||||||
|
CHECK_EQ(382, res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#undef __
|
#undef __
|
||||||
|
@ -396,6 +396,15 @@ TEST(Type3) {
|
|||||||
"e7df0f91 bfi r0, r1, #31, #1");
|
"e7df0f91 bfi r0, r1, #31, #1");
|
||||||
COMPARE(bfi(r1, r0, 31, 1),
|
COMPARE(bfi(r1, r0, 31, 1),
|
||||||
"e7df1f90 bfi r1, r0, #31, #1");
|
"e7df1f90 bfi r1, r0, #31, #1");
|
||||||
|
|
||||||
|
COMPARE(usat(r0, 1, Operand(r1)),
|
||||||
|
"e6e10011 usat r0, #1, r1");
|
||||||
|
COMPARE(usat(r2, 7, Operand(lr)),
|
||||||
|
"e6e7201e usat r2, #7, lr");
|
||||||
|
COMPARE(usat(r3, 31, Operand(r4, LSL, 31)),
|
||||||
|
"e6ff3f94 usat r3, #31, r4, lsl #31");
|
||||||
|
COMPARE(usat(r8, 0, Operand(r5, ASR, 17)),
|
||||||
|
"e6e088d5 usat r8, #0, r5, asr #17");
|
||||||
}
|
}
|
||||||
|
|
||||||
VERIFY_RUN();
|
VERIFY_RUN();
|
||||||
|
Loading…
Reference in New Issue
Block a user