}
+// 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.
}
Register rm() const { return rm_; }
+ Register rs() const { return rs_; }
+ ShiftOp shift_op() const { return shift_op_; }
private:
Register rm_;
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,
void PrintCondition(Instr* instr);
void PrintShiftRm(Instr* instr);
void PrintShiftImm(Instr* instr);
+ void PrintShiftSat(Instr* instr);
void PrintPU(Instr* instr);
void PrintSoftwareInterrupt(SoftwareInterruptCodes swi);
}
+// 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()) {
}
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");
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) {
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);
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: {
__ 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));
}
+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);
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);
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: {
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_;
}
}
+
+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 __
"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();