Commit 74f9789f authored by ager@chromium.org's avatar ager@chromium.org

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
parent c9c7f883
......@@ -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();
......
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