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.
|
||||
|
||||
// Unsigned bit field extract.
|
||||
|
@ -445,6 +445,8 @@ class Operand BASE_EMBEDDED {
|
||||
}
|
||||
|
||||
Register rm() const { return rm_; }
|
||||
Register rs() const { return rs_; }
|
||||
ShiftOp shift_op() const { return shift_op_; }
|
||||
|
||||
private:
|
||||
Register rm_;
|
||||
@ -834,6 +836,25 @@ class Assembler : public Malloced {
|
||||
|
||||
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.
|
||||
|
||||
void ubfx(Register dst, Register src, int lsb, int width,
|
||||
|
@ -106,6 +106,7 @@ class Decoder {
|
||||
void PrintCondition(Instr* instr);
|
||||
void PrintShiftRm(Instr* instr);
|
||||
void PrintShiftImm(Instr* instr);
|
||||
void PrintShiftSat(Instr* instr);
|
||||
void PrintPU(Instr* instr);
|
||||
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.
|
||||
void Decoder::PrintPU(Instr* instr) {
|
||||
switch (instr->PUField()) {
|
||||
@ -440,6 +453,20 @@ int Decoder::FormatOption(Instr* instr, const char* format) {
|
||||
}
|
||||
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
|
||||
if (instr->HasLink()) {
|
||||
Print("l");
|
||||
@ -507,7 +534,7 @@ int Decoder::FormatOption(Instr* instr, const char* format) {
|
||||
return FormatRegister(instr, format);
|
||||
}
|
||||
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
|
||||
ASSERT(STRING_STARTS_WITH(format, "shift_op"));
|
||||
if (instr->TypeField() == 0) {
|
||||
@ -517,6 +544,10 @@ int Decoder::FormatOption(Instr* instr, const char* format) {
|
||||
PrintShiftImm(instr);
|
||||
}
|
||||
return 8;
|
||||
} else if (format[6] == 's') { // 'shift_sat.
|
||||
ASSERT(STRING_STARTS_WITH(format, "shift_sat"));
|
||||
PrintShiftSat(instr);
|
||||
return 9;
|
||||
} else { // 'shift_rm
|
||||
ASSERT(STRING_STARTS_WITH(format, "shift_rm"));
|
||||
PrintShiftRm(instr);
|
||||
@ -897,8 +928,16 @@ void Decoder::DecodeType3(Instr* instr) {
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
ASSERT(!instr->HasW());
|
||||
Format(instr, "'memop'cond'b 'rd, ['rn], +'shift_rm");
|
||||
if (instr->HasW()) {
|
||||
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;
|
||||
}
|
||||
case 2: {
|
||||
|
@ -1674,14 +1674,8 @@ void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm) {
|
||||
__ cmp(r4, Operand(ip));
|
||||
__ b(hs, &slow);
|
||||
__ mov(r5, Operand(value, ASR, kSmiTagSize)); // Untag the value.
|
||||
{ // 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);
|
||||
}
|
||||
__ Usat(r5, 8, Operand(r5)); // Clamp the value to [0..255].
|
||||
|
||||
// Get the pointer to the external array. This clobbers elements.
|
||||
__ ldr(elements,
|
||||
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) {
|
||||
// Empty the const pool.
|
||||
CheckConstPool(true, true);
|
||||
|
@ -112,6 +112,8 @@ class MacroAssembler: public Assembler {
|
||||
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 Usat(Register dst, int satpos, const Operand& src,
|
||||
Condition cond = al);
|
||||
|
||||
void Call(Label* target);
|
||||
void Move(Register dst, Handle<Object> value);
|
||||
|
@ -2047,11 +2047,41 @@ void Simulator::DecodeType3(Instr* instr) {
|
||||
case 0: {
|
||||
ASSERT(!instr->HasW());
|
||||
Format(instr, "'memop'cond'b 'rd, ['rn], -'shift_rm");
|
||||
UNIMPLEMENTED();
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
ASSERT(!instr->HasW());
|
||||
Format(instr, "'memop'cond'b 'rd, ['rn], +'shift_rm");
|
||||
if (instr->HasW()) {
|
||||
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;
|
||||
}
|
||||
case 2: {
|
||||
|
@ -294,6 +294,9 @@ class Simulator {
|
||||
void TrashCallerSaveRegisters();
|
||||
|
||||
// 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];
|
||||
bool n_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 __
|
||||
|
@ -396,6 +396,15 @@ TEST(Type3) {
|
||||
"e7df0f91 bfi r0, r1, #31, #1");
|
||||
COMPARE(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();
|
||||
|
Loading…
Reference in New Issue
Block a user