Landing for Rodolph Perfetta.
authorager@chromium.org <ager@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Wed, 21 Jul 2010 07:42:51 +0000 (07:42 +0000)
committerager@chromium.org <ager@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Wed, 21 Jul 2010 07:42:51 +0000 (07:42 +0000)
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

src/arm/assembler-arm.cc
src/arm/assembler-arm.h
src/arm/disasm-arm.cc
src/arm/ic-arm.cc
src/arm/macro-assembler-arm.cc
src/arm/macro-assembler-arm.h
src/arm/simulator-arm.cc
src/arm/simulator-arm.h
test/cctest/test-assembler-arm.cc
test/cctest/test-disasm-arm.cc

index 0dc6b7710652258b451538adfb5eccf538eb861f..a16f2002f994876a22990a066ad6dd83896ddb83 100644 (file)
@@ -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.
index 226fb87403fd872a1c14789e8eb7d21b52ab5db1..39a5b4e9d36250608db3facb39a58772d8fc753d 100644 (file)
@@ -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,
index 37401ed28f26857104623f7b1301a28b8a8aed7b..fd142bd96164f5ce4be71deaa9cb333069b63391 100644 (file)
@@ -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: {
index 57d9376ecb54646984b9a91b9d109f9e04e2106a..144f8e3b9fd4f56a8280234a650f5b60dc131231 100644 (file)
@@ -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));
index f251b31f0b2383ed386286e7c990d0218c3f3c8a..ad22cd940ac5a1ad1851880cf04acc3e206b5adb 100644 (file)
@@ -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);
index 156e1326986b8dbcab6711ddaca6990a9c804874..9949fac154281b7d88a9a55dff3d69d1063adb9a 100644 (file)
@@ -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);
index 3345e4559bc5fe3de866d5f1e8a301c1ec194d34..04635e3f91587a1db9258e420031d0006c0c95e0 100644 (file)
@@ -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: {
index 61af3aa6e09622933e82ea4ecf27e7cef3d15ec5..fee296e40e8e56231c7779c9a16c188df79c8164 100644 (file)
@@ -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_;
index 5e49c0cdadea88fb0b6d253d34a80722fb23185f..9033f4b874c281bc0a36da6453e19c060f28e446 100644 (file)
@@ -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 __
index 2bb32e7f5ff45a557c6009447f9f28fdb85a4c4c..0ba4f9ae4773943fed331acfc07e16158bb339e3 100644 (file)
@@ -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();