Support conversion of clamped double values for pixel arrays in Crankshaft.
authordanno@chromium.org <danno@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Mon, 16 May 2011 14:10:56 +0000 (14:10 +0000)
committerdanno@chromium.org <danno@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Mon, 16 May 2011 14:10:56 +0000 (14:10 +0000)
BUG=1313
TEST=test/mjsunit/external-array.js

Review URL: http://codereview.chromium.org/7014033

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@7901 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

21 files changed:
src/arm/lithium-arm.cc
src/arm/lithium-arm.h
src/arm/lithium-codegen-arm.cc
src/arm/macro-assembler-arm.cc
src/arm/macro-assembler-arm.h
src/assembler.cc
src/assembler.h
src/hydrogen-instructions.cc
src/hydrogen-instructions.h
src/hydrogen.cc
src/ia32/lithium-codegen-ia32.cc
src/ia32/lithium-ia32.cc
src/ia32/lithium-ia32.h
src/ia32/macro-assembler-ia32.cc
src/ia32/macro-assembler-ia32.h
src/x64/lithium-codegen-x64.cc
src/x64/lithium-x64.cc
src/x64/lithium-x64.h
src/x64/macro-assembler-x64.cc
src/x64/macro-assembler-x64.h
test/mjsunit/external-array.js

index af01c296b12895ebe4f91d205f47f4b1f51aa501..48c3ea39de6787a88e074916945ffb99d2ebfd7c 100644 (file)
@@ -1751,6 +1751,24 @@ LInstruction* LChunkBuilder::DoCheckMap(HCheckMap* instr) {
 }
 
 
+LInstruction* LChunkBuilder::DoClampToUint8(HClampToUint8* instr) {
+  HValue* value = instr->value();
+  Representation input_rep = value->representation();
+  LOperand* reg = UseRegister(value);
+  if (input_rep.IsDouble()) {
+    return DefineAsRegister(new LClampDoubleToUint8(reg, FixedTemp(d1)));
+  } else if (input_rep.IsInteger32()) {
+    return DefineAsRegister(new LClampIToUint8(reg));
+  } else {
+    ASSERT(input_rep.IsTagged());
+    // Register allocator doesn't (yet) support allocation of double
+    // temps. Reserve d1 explicitly.
+    LClampTaggedToUint8* result = new LClampTaggedToUint8(reg, FixedTemp(d1));
+    return AssignEnvironment(DefineAsRegister(result));
+  }
+}
+
+
 LInstruction* LChunkBuilder::DoReturn(HReturn* instr) {
   return new LReturn(UseFixed(instr->value(), r0));
 }
index f98c68f2197bb67e89f9aa5cb78e0be9a988d0c3..5a90f7a2ce4cdd5413e8238df9e13c8112c5b18e 100644 (file)
@@ -73,6 +73,9 @@ class LCodeGen;
   V(CheckMap)                                   \
   V(CheckPrototypeMaps)                         \
   V(CheckSmi)                                   \
+  V(ClampDoubleToUint8)                         \
+  V(ClampIToUint8)                              \
+  V(ClampTaggedToUint8)                         \
   V(ClassOfTest)                                \
   V(ClassOfTestAndBranch)                       \
   V(CmpID)                                      \
@@ -1927,6 +1930,44 @@ class LCheckNonSmi: public LTemplateInstruction<0, 1, 0> {
 };
 
 
+class LClampDoubleToUint8: public LTemplateInstruction<1, 1, 1> {
+ public:
+  explicit LClampDoubleToUint8(LOperand* value, LOperand* temp) {
+    inputs_[0] = value;
+    temps_[0] = temp;
+  }
+
+  LOperand* unclamped() { return inputs_[0]; }
+
+  DECLARE_CONCRETE_INSTRUCTION(ClampDoubleToUint8, "clamp-d-to-uint8")
+};
+
+
+class LClampIToUint8: public LTemplateInstruction<1, 1, 0> {
+ public:
+  explicit LClampIToUint8(LOperand* value) {
+    inputs_[0] = value;
+  }
+
+  LOperand* unclamped() { return inputs_[0]; }
+
+  DECLARE_CONCRETE_INSTRUCTION(ClampIToUint8, "clamp-i-to-uint8")
+};
+
+
+class LClampTaggedToUint8: public LTemplateInstruction<1, 1, 1> {
+ public:
+  explicit LClampTaggedToUint8(LOperand* value, LOperand* temp) {
+    inputs_[0] = value;
+    temps_[0] = temp;
+  }
+
+  LOperand* unclamped() { return inputs_[0]; }
+
+  DECLARE_CONCRETE_INSTRUCTION(ClampTaggedToUint8, "clamp-t-to-uint8")
+};
+
+
 class LArrayLiteral: public LTemplateInstruction<1, 0, 0> {
  public:
   DECLARE_CONCRETE_INSTRUCTION(ArrayLiteral, "array-literal")
index 33733fb4636c70213747b857058496763cb36cd1..878f09d21a22f57fa463275e7e5887846bacf06b 100644 (file)
@@ -3352,9 +3352,6 @@ void LCodeGen::DoStoreKeyedSpecializedArrayElement(
         : MemOperand(external_pointer, key, LSL, shift_size));
     switch (array_type) {
       case kExternalPixelArray:
-        // Clamp the value to [0..255].
-        __ Usat(value, 8, Operand(value));
-        // Fall through to the next case for the store instruction:
       case kExternalByteArray:
       case kExternalUnsignedByteArray:
         __ strb(value, mem_operand);
@@ -4014,6 +4011,59 @@ void LCodeGen::DoCheckMap(LCheckMap* instr) {
 }
 
 
+void LCodeGen::DoClampDoubleToUint8(LClampDoubleToUint8* instr) {
+  DoubleRegister value_reg = ToDoubleRegister(instr->unclamped());
+  Register result_reg = ToRegister(instr->result());
+  DoubleRegister temp_reg = ToDoubleRegister(instr->TempAt(0));
+  __ ClampDoubleToUint8(result_reg, value_reg, temp_reg);
+}
+
+
+void LCodeGen::DoClampIToUint8(LClampIToUint8* instr) {
+  Register unclamped_reg = ToRegister(instr->unclamped());
+  Register result_reg = ToRegister(instr->result());
+  __ ClampUint8(result_reg, unclamped_reg);
+}
+
+
+void LCodeGen::DoClampTaggedToUint8(LClampTaggedToUint8* instr) {
+  Register scratch = scratch0();
+  Register input_reg = ToRegister(instr->unclamped());
+  Register result_reg = ToRegister(instr->result());
+  DoubleRegister temp_reg = ToDoubleRegister(instr->TempAt(0));
+  Label is_smi, done, heap_number;
+
+  // Both smi and heap number cases are handled.
+  __ JumpIfSmi(input_reg, &is_smi);
+
+  // Check for heap number
+  __ ldr(scratch, FieldMemOperand(input_reg, HeapObject::kMapOffset));
+  __ cmp(scratch, Operand(factory()->heap_number_map()));
+  __ b(eq, &heap_number);
+
+  // Check for undefined. Undefined is converted to zero for clamping
+  // conversions.
+  __ cmp(input_reg, Operand(factory()->undefined_value()));
+  DeoptimizeIf(ne, instr->environment());
+  __ movt(input_reg, 0);
+  __ jmp(&done);
+
+  // Heap number
+  __ bind(&heap_number);
+  __ vldr(double_scratch0(), FieldMemOperand(input_reg,
+                                             HeapNumber::kValueOffset));
+  __ ClampDoubleToUint8(result_reg, double_scratch0(), temp_reg);
+  __ jmp(&done);
+
+  // smi
+  __ bind(&is_smi);
+  __ SmiUntag(result_reg, input_reg);
+  __ ClampUint8(result_reg, result_reg);
+
+  __ bind(&done);
+}
+
+
 void LCodeGen::LoadHeapObject(Register result,
                               Handle<HeapObject> object) {
   if (heap()->InNewSpace(*object)) {
index 034e22bd6392db8ff38ae8d2bc39ec0b97acdbad..b582ef7e7e4e61f9edf2ac0015c0a76763075ad9 100644 (file)
@@ -3028,6 +3028,44 @@ void MacroAssembler::GetRelocatedValueLocation(Register ldr_location,
 }
 
 
+void MacroAssembler::ClampUint8(Register output_reg, Register input_reg) {
+  Usat(output_reg, 8, Operand(input_reg));
+}
+
+
+void MacroAssembler::ClampDoubleToUint8(Register result_reg,
+                                        DoubleRegister input_reg,
+                                        DoubleRegister temp_double_reg) {
+  Label above_zero;
+  Label done;
+  Label in_bounds;
+
+  vmov(temp_double_reg, 0.0);
+  VFPCompareAndSetFlags(input_reg, temp_double_reg);
+  b(gt, &above_zero);
+
+  // Double value is less than zero, NaN or Inf, return 0.
+  mov(result_reg, Operand(0));
+  b(al, &done);
+
+  // Double value is >= 255, return 255.
+  bind(&above_zero);
+  vmov(temp_double_reg, 255.0);
+  VFPCompareAndSetFlags(input_reg, temp_double_reg);
+  b(le, &in_bounds);
+  mov(result_reg, Operand(255));
+  b(al, &done);
+
+  // In 0-255 range, round and truncate.
+  bind(&in_bounds);
+  vmov(temp_double_reg, 0.5);
+  vadd(temp_double_reg, input_reg, temp_double_reg);
+  vcvt_u32_f64(s0, temp_double_reg);
+  vmov(result_reg, s0);
+  bind(&done);
+}
+
+
 CodePatcher::CodePatcher(byte* address, int instructions)
     : address_(address),
       instructions_(instructions),
index 8e8409d6914dee698cc73af5fdda26a83f1f0de6..6986aa3ae2f17c295def9dea530c5f2a5ee97d20 100644 (file)
@@ -987,6 +987,13 @@ class MacroAssembler: public Assembler {
                                  Register result);
 
 
+  void ClampUint8(Register output_reg, Register input_reg);
+
+  void ClampDoubleToUint8(Register result_reg,
+                          DoubleRegister input_reg,
+                          DoubleRegister temp_double_reg);
+
+
  private:
   void CallCFunctionHelper(Register function,
                            ExternalReference function_reference,
index a75c94aa0d4570975be1367588806bad5043bf0d..3c7fc1c6313557b17494907794151eccce614f35 100644 (file)
@@ -69,6 +69,8 @@ namespace internal {
 const double DoubleConstant::min_int = kMinInt;
 const double DoubleConstant::one_half = 0.5;
 const double DoubleConstant::minus_zero = -0.0;
+const double DoubleConstant::uint8_max_value = 255;
+const double DoubleConstant::zero = 0.0;
 const double DoubleConstant::nan = OS::nan_value();
 const double DoubleConstant::negative_infinity = -V8_INFINITY;
 const char* RelocInfo::kFillerCommentString = "DEOPTIMIZATION PADDING";
@@ -901,6 +903,18 @@ ExternalReference ExternalReference::address_of_minus_zero() {
 }
 
 
+ExternalReference ExternalReference::address_of_zero() {
+  return ExternalReference(reinterpret_cast<void*>(
+      const_cast<double*>(&DoubleConstant::zero)));
+}
+
+
+ExternalReference ExternalReference::address_of_uint8_max_value() {
+  return ExternalReference(reinterpret_cast<void*>(
+      const_cast<double*>(&DoubleConstant::uint8_max_value)));
+}
+
+
 ExternalReference ExternalReference::address_of_negative_infinity() {
   return ExternalReference(reinterpret_cast<void*>(
       const_cast<double*>(&DoubleConstant::negative_infinity)));
index 7a786bc077463029f596c8b0a5b08c717520d2c9..af8d82efd1e933abfb3b18f802278780ddeb9093 100644 (file)
@@ -67,6 +67,8 @@ class DoubleConstant: public AllStatic {
   static const double min_int;
   static const double one_half;
   static const double minus_zero;
+  static const double zero;
+  static const double uint8_max_value;
   static const double negative_infinity;
   static const double nan;
 };
@@ -607,6 +609,8 @@ class ExternalReference BASE_EMBEDDED {
   static ExternalReference address_of_min_int();
   static ExternalReference address_of_one_half();
   static ExternalReference address_of_minus_zero();
+  static ExternalReference address_of_zero();
+  static ExternalReference address_of_uint8_max_value();
   static ExternalReference address_of_negative_infinity();
   static ExternalReference address_of_nan();
 
index ccc3c182eadf51d008f5b5abec5a9b4cb22d0616..1880111a5eef927880749071ad03e40e89865517 100644 (file)
@@ -366,7 +366,6 @@ const char* HValue::Mnemonic() const {
 
 
 void HValue::SetOperandAt(int index, HValue* value) {
-  ASSERT(value == NULL || !value->representation().IsNone());
   RegisterUse(index, value);
   InternalSetOperandAt(index, value);
 }
@@ -600,6 +599,8 @@ void HInstruction::Verify() {
         ASSERT(cur == other_operand);
       }
     } else {
+      // If the following assert fires, you may have forgotten an
+      // AddInstruction.
       ASSERT(other_block->Dominates(cur_block));
     }
   }
index 88ce87dbc264b2411a9d3711d7d95fd0db9759a3..56810ef6b8c16d5216a4715126f9c7558c26a8a7 100644 (file)
@@ -87,6 +87,7 @@ class LChunkBuilder;
   V(CheckNonSmi)                               \
   V(CheckPrototypeMaps)                        \
   V(CheckSmi)                                  \
+  V(ClampToUint8)                              \
   V(ClassOfTest)                               \
   V(Compare)                                   \
   V(CompareJSObjectEq)                         \
@@ -551,7 +552,6 @@ class HValue: public ZoneObject {
   Representation representation() const { return representation_; }
   void ChangeRepresentation(Representation r) {
     // Representation was already set and is allowed to be changed.
-    ASSERT(!representation_.IsNone());
     ASSERT(!r.IsNone());
     ASSERT(CheckFlag(kFlexibleRepresentation));
     RepresentationChanged(r);
@@ -1054,6 +1054,46 @@ class HChange: public HUnaryOperation {
 };
 
 
+class HClampToUint8: public HUnaryOperation {
+ public:
+  explicit HClampToUint8(HValue* value)
+      : HUnaryOperation(value),
+        input_rep_(Representation::None()) {
+    SetFlag(kFlexibleRepresentation);
+    set_representation(Representation::Tagged());
+    SetFlag(kUseGVN);
+  }
+
+  virtual Representation RequiredInputRepresentation(int index) const {
+    return input_rep_;
+  }
+
+  virtual Representation InferredRepresentation() {
+    // TODO(danno): Inference on input types should happen separately from
+    // return representation.
+    Representation new_rep = value()->representation();
+    if (input_rep_.IsNone()) {
+      if (!new_rep.IsNone()) {
+        input_rep_ = new_rep;
+        return Representation::Integer32();
+      } else {
+        return Representation::None();
+      }
+    } else {
+      return Representation::Integer32();
+    }
+  }
+
+  DECLARE_CONCRETE_INSTRUCTION(ClampToUint8)
+
+ protected:
+  virtual bool DataEquals(HValue* other) { return true; }
+
+ private:
+  Representation input_rep_;
+};
+
+
 class HSimulate: public HInstruction {
  public:
   HSimulate(int ast_id, int pop_count)
index 18d77f3390acd9a457c77bd8249fe76223df4173..73d3e498e5c2782eaeb36c6c368573365e81975e 100644 (file)
@@ -3725,6 +3725,11 @@ HInstruction* HGraphBuilder::BuildStoreKeyedSpecializedArrayElement(
   HLoadExternalArrayPointer* external_elements =
       new(zone()) HLoadExternalArrayPointer(elements);
   AddInstruction(external_elements);
+  if (expr->external_array_type() == kExternalPixelArray) {
+    HClampToUint8* clamp = new(zone()) HClampToUint8(val);
+    AddInstruction(clamp);
+    val = clamp;
+  }
   return new(zone()) HStoreKeyedSpecializedArrayElement(
       external_elements,
       key,
index 1f2cd4c92b1b4b35548f2f0280a2a4ce5e495954..a9fa79273d0d8dea8b9d8d1f24ba3aa4b4776435 100644 (file)
@@ -3199,24 +3199,7 @@ void LCodeGen::DoStoreKeyedSpecializedArrayElement(
   } else {
     Register value = ToRegister(instr->value());
     switch (array_type) {
-      case kExternalPixelArray: {
-        // Clamp the value to [0..255].
-        Register temp = ToRegister(instr->TempAt(0));
-        // The dec_b below requires that the clamped value is in a byte
-        // register. eax is an arbitrary choice to satisfy this requirement, we
-        // hinted the register allocator to give us eax when building the
-        // instruction.
-        ASSERT(temp.is(eax));
-        __ mov(temp, ToRegister(instr->value()));
-        Label done;
-        __ test(temp, Immediate(0xFFFFFF00));
-        __ j(zero, &done, Label::kNear);
-        __ setcc(negative, temp);  // 1 if negative, 0 if positive.
-        __ dec_b(temp);  // 0 if negative, 255 if positive.
-        __ bind(&done);
-        __ mov_b(operand, temp);
-        break;
-      }
+      case kExternalPixelArray:
       case kExternalByteArray:
       case kExternalUnsignedByteArray:
         __ mov_b(operand, value);
@@ -3968,6 +3951,54 @@ void LCodeGen::DoCheckMap(LCheckMap* instr) {
 }
 
 
+void LCodeGen::DoClampDoubleToUint8(LClampDoubleToUint8* instr) {
+  XMMRegister value_reg = ToDoubleRegister(instr->unclamped());
+  Register result_reg = ToRegister(instr->result());
+  __ ClampDoubleToUint8(value_reg, xmm0, result_reg);
+}
+
+
+void LCodeGen::DoClampIToUint8(LClampIToUint8* instr) {
+  ASSERT(instr->unclamped()->Equals(instr->result()));
+  Register value_reg = ToRegister(instr->result());
+  __ ClampUint8(value_reg);
+}
+
+
+void LCodeGen::DoClampTaggedToUint8(LClampTaggedToUint8* instr) {
+  ASSERT(instr->unclamped()->Equals(instr->result()));
+  Register input_reg = ToRegister(instr->unclamped());
+  Label is_smi, done, heap_number;
+
+  __ JumpIfSmi(input_reg, &is_smi);
+
+  // Check for heap number
+  __ cmp(FieldOperand(input_reg, HeapObject::kMapOffset),
+         factory()->heap_number_map());
+  __ j(equal, &heap_number, Label::kNear);
+
+  // Check for undefined. Undefined is converted to zero for clamping
+  // conversions.
+  __ cmp(input_reg, factory()->undefined_value());
+  DeoptimizeIf(not_equal, instr->environment());
+  __ mov(input_reg, 0);
+  __ jmp(&done, Label::kNear);
+
+  // Heap number
+  __ bind(&heap_number);
+  __ movdbl(xmm0, FieldOperand(input_reg, HeapNumber::kValueOffset));
+  __ ClampDoubleToUint8(xmm0, xmm1, input_reg);
+  __ jmp(&done, Label::kNear);
+
+  // smi
+  __ bind(&is_smi);
+  __ SmiUntag(input_reg);
+  __ ClampUint8(input_reg);
+
+  __ bind(&done);
+}
+
+
 void LCodeGen::LoadHeapObject(Register result, Handle<HeapObject> object) {
   if (isolate()->heap()->InNewSpace(*object)) {
     Handle<JSGlobalPropertyCell> cell =
index 4acbf7201799b93f429461a3d712fa89c3011671..ee4f5c66826a5a8ebe1ba50630e6ec58c17d7ac5 100644 (file)
@@ -1778,6 +1778,27 @@ LInstruction* LChunkBuilder::DoCheckMap(HCheckMap* instr) {
 }
 
 
+LInstruction* LChunkBuilder::DoClampToUint8(HClampToUint8* instr) {
+  HValue* value = instr->value();
+  Representation input_rep = value->representation();
+  if (input_rep.IsDouble()) {
+    LOperand* reg = UseRegister(value);
+    return DefineAsRegister(new LClampDoubleToUint8(reg));
+  } else if (input_rep.IsInteger32()) {
+    LOperand* reg = UseFixed(value, eax);
+    return DefineFixed(new LClampIToUint8(reg), eax);
+  } else {
+    ASSERT(input_rep.IsTagged());
+    LOperand* reg = UseFixed(value, eax);
+    // Register allocator doesn't (yet) support allocation of double
+    // temps. Reserve xmm1 explicitly.
+    LOperand* temp = FixedTemp(xmm1);
+    LClampTaggedToUint8* result = new LClampTaggedToUint8(reg, temp);
+    return AssignEnvironment(DefineFixed(result, eax));
+  }
+}
+
+
 LInstruction* LChunkBuilder::DoReturn(HReturn* instr) {
   return new LReturn(UseFixed(instr->value(), eax));
 }
@@ -1988,14 +2009,6 @@ LInstruction* LChunkBuilder::DoStoreKeyedSpecializedArrayElement(
   LOperand* external_pointer = UseRegister(instr->external_pointer());
   LOperand* key = UseRegisterOrConstant(instr->key());
   LOperand* temp = NULL;
-
-  if (array_type == kExternalPixelArray) {
-    // The generated code for pixel array stores requires that the clamped value
-    // is in a byte register. eax is an arbitrary choice to satisfy this
-    // requirement.
-    temp = FixedTemp(eax);
-  }
-
   LOperand* val = NULL;
   if (array_type == kExternalByteArray ||
       array_type == kExternalUnsignedByteArray) {
index 1ed3eaabd7efcf29af78c37c6ac5c47e93c5cd82..516222348e4a802baae30c06fdaf5633bd005de7 100644 (file)
@@ -67,6 +67,9 @@ class LCodeGen;
   V(CheckNonSmi)                                \
   V(CheckPrototypeMaps)                         \
   V(CheckSmi)                                   \
+  V(ClampDoubleToUint8)                         \
+  V(ClampIToUint8)                              \
+  V(ClampTaggedToUint8)                         \
   V(ClassOfTest)                                \
   V(ClassOfTestAndBranch)                       \
   V(CmpID)                                      \
@@ -1974,6 +1977,43 @@ class LCheckSmi: public LTemplateInstruction<0, 1, 0> {
 };
 
 
+class LClampDoubleToUint8: public LTemplateInstruction<1, 1, 0> {
+ public:
+  explicit LClampDoubleToUint8(LOperand* value) {
+    inputs_[0] = value;
+  }
+
+  LOperand* unclamped() { return inputs_[0]; }
+
+  DECLARE_CONCRETE_INSTRUCTION(ClampDoubleToUint8, "clamp-d-to-uint8")
+};
+
+
+class LClampIToUint8: public LTemplateInstruction<1, 1, 0> {
+ public:
+  explicit LClampIToUint8(LOperand* value) {
+    inputs_[0] = value;
+  }
+
+  LOperand* unclamped() { return inputs_[0]; }
+
+  DECLARE_CONCRETE_INSTRUCTION(ClampIToUint8, "clamp-i-to-uint8")
+};
+
+
+class LClampTaggedToUint8: public LTemplateInstruction<1, 1, 1> {
+ public:
+  explicit LClampTaggedToUint8(LOperand* value, LOperand* temp) {
+    inputs_[0] = value;
+    temps_[0] = temp;
+  }
+
+  LOperand* unclamped() { return inputs_[0]; }
+
+  DECLARE_CONCRETE_INSTRUCTION(ClampTaggedToUint8, "clamp-t-to-uint8")
+};
+
+
 class LCheckNonSmi: public LTemplateInstruction<0, 1, 0> {
  public:
   explicit LCheckNonSmi(LOperand* value) {
index d5eda7a0d7640be2bc25d2f5375316c91acdd006..e5b24c7ca223652df1e56c823d550e69e3d4dedb 100644 (file)
@@ -77,6 +77,36 @@ void MacroAssembler::RecordWriteHelper(Register object,
 }
 
 
+void MacroAssembler::ClampDoubleToUint8(XMMRegister input_reg,
+                                        XMMRegister scratch_reg,
+                                        Register result_reg) {
+  Label done;
+  ExternalReference zero_ref = ExternalReference::address_of_zero();
+  movdbl(scratch_reg, Operand::StaticVariable(zero_ref));
+  Set(result_reg, Immediate(0));
+  ucomisd(input_reg, scratch_reg);
+  j(below, &done, Label::kNear);
+  ExternalReference half_ref = ExternalReference::address_of_one_half();
+  movdbl(scratch_reg, Operand::StaticVariable(half_ref));
+  addsd(scratch_reg, input_reg);
+  cvttsd2si(result_reg, Operand(scratch_reg));
+  test(result_reg, Immediate(0xFFFFFF00));
+  j(zero, &done, Label::kNear);
+  Set(result_reg, Immediate(255));
+  bind(&done);
+}
+
+
+void MacroAssembler::ClampUint8(Register reg) {
+  Label done;
+  test(reg, Immediate(0xFFFFFF00));
+  j(zero, &done, Label::kNear);
+  setcc(negative, reg);  // 1 if negative, 0 if positive.
+  dec_b(reg);  // 0 if negative, 255 if positive.
+  bind(&done);
+}
+
+
 void MacroAssembler::InNewSpace(Register object,
                                 Register scratch,
                                 Condition cc,
index f88d992fc7acc019e16d1dd524f1e59a4fe4c121..9cb8a3eed3d547f4aef63325739e6120f77b9290 100644 (file)
@@ -238,6 +238,13 @@ class MacroAssembler: public Assembler {
   // jcc instructions (je, ja, jae, jb, jbe, je, and jz).
   void FCmp();
 
+  void ClampUint8(Register reg);
+
+  void ClampDoubleToUint8(XMMRegister input_reg,
+                          XMMRegister scratch_reg,
+                          Register result_reg);
+
+
   // Smi tagging support.
   void SmiTag(Register reg) {
     ASSERT(kSmiTag == 0);
index 64a6a1d3a0d42b8bf65480a92ba7d89e49e1df55..c0ef079dc5bedea5bfaee021323c187a47a71c9e 100644 (file)
@@ -3185,16 +3185,6 @@ void LCodeGen::DoStoreKeyedSpecializedArrayElement(
     Register value(ToRegister(instr->value()));
     switch (array_type) {
       case kExternalPixelArray:
-        {  // Clamp the value to [0..255].
-          Label done;
-          __ testl(value, Immediate(0xFFFFFF00));
-          __ j(zero, &done, Label::kNear);
-          __ setcc(negative, value);  // 1 if negative, 0 if positive.
-          __ decb(value);  // 0 if negative, 255 if positive.
-          __ bind(&done);
-          __ movb(operand, value);
-        }
-        break;
       case kExternalByteArray:
       case kExternalUnsignedByteArray:
         __ movb(operand, value);
@@ -3785,6 +3775,57 @@ void LCodeGen::DoCheckMap(LCheckMap* instr) {
 }
 
 
+void LCodeGen::DoClampDoubleToUint8(LClampDoubleToUint8* instr) {
+  XMMRegister value_reg = ToDoubleRegister(instr->unclamped());
+  Register result_reg = ToRegister(instr->result());
+  Register temp_reg = ToRegister(instr->TempAt(0));
+  __ ClampDoubleToUint8(value_reg, xmm0, result_reg, temp_reg);
+}
+
+
+void LCodeGen::DoClampIToUint8(LClampIToUint8* instr) {
+  ASSERT(instr->unclamped()->Equals(instr->result()));
+  Register value_reg = ToRegister(instr->result());
+  __ ClampUint8(value_reg);
+}
+
+
+void LCodeGen::DoClampTaggedToUint8(LClampTaggedToUint8* instr) {
+  ASSERT(instr->unclamped()->Equals(instr->result()));
+  Register input_reg = ToRegister(instr->unclamped());
+  Register temp_reg = ToRegister(instr->TempAt(0));
+  XMMRegister temp_xmm_reg = ToDoubleRegister(instr->TempAt(1));
+  Label is_smi, done, heap_number;
+
+  __ JumpIfSmi(input_reg, &is_smi);
+
+  // Check for heap number
+  __ Cmp(FieldOperand(input_reg, HeapObject::kMapOffset),
+         factory()->heap_number_map());
+  __ j(equal, &heap_number, Label::kNear);
+
+  // Check for undefined. Undefined is converted to zero for clamping
+  // conversions.
+  __ Cmp(input_reg, factory()->undefined_value());
+  DeoptimizeIf(not_equal, instr->environment());
+  __ movq(input_reg, Immediate(0));
+  __ jmp(&done, Label::kNear);
+
+  // Heap number
+  __ bind(&heap_number);
+  __ movsd(xmm0, FieldOperand(input_reg, HeapNumber::kValueOffset));
+  __ ClampDoubleToUint8(xmm0, temp_xmm_reg, input_reg, temp_reg);
+  __ jmp(&done, Label::kNear);
+
+  // smi
+  __ bind(&is_smi);
+  __ SmiToInteger32(input_reg, input_reg);
+  __ ClampUint8(input_reg);
+
+  __ bind(&done);
+}
+
+
 void LCodeGen::LoadHeapObject(Register result, Handle<HeapObject> object) {
   if (heap()->InNewSpace(*object)) {
     Handle<JSGlobalPropertyCell> cell =
index f5f2879afd3ec8f836d37b578bc9a5e383d9f46f..69c8c476b9332c101419af852f8aac87471ccabd 100644 (file)
@@ -1742,6 +1742,27 @@ LInstruction* LChunkBuilder::DoCheckMap(HCheckMap* instr) {
 }
 
 
+LInstruction* LChunkBuilder::DoClampToUint8(HClampToUint8* instr) {
+  HValue* value = instr->value();
+  Representation input_rep = value->representation();
+  LOperand* reg = UseRegister(value);
+  if (input_rep.IsDouble()) {
+    return DefineAsRegister(new LClampDoubleToUint8(reg,
+                                                    TempRegister()));
+  } else if (input_rep.IsInteger32()) {
+    return DefineSameAsFirst(new LClampIToUint8(reg));
+  } else {
+    ASSERT(input_rep.IsTagged());
+    // Register allocator doesn't (yet) support allocation of double
+    // temps. Reserve xmm1 explicitly.
+    LClampTaggedToUint8* result = new LClampTaggedToUint8(reg,
+                                                          TempRegister(),
+                                                          FixedTemp(xmm1));
+    return AssignEnvironment(DefineSameAsFirst(result));
+  }
+}
+
+
 LInstruction* LChunkBuilder::DoReturn(HReturn* instr) {
   return new LReturn(UseFixed(instr->value(), rax));
 }
index 4aa235f13789cfc841b66cf6ead69de961f80e13..0a16b65cb856df1e6503afd58516888e532ba779 100644 (file)
@@ -73,6 +73,9 @@ class LCodeGen;
   V(CheckNonSmi)                                \
   V(CheckPrototypeMaps)                         \
   V(CheckSmi)                                   \
+  V(ClampDoubleToUint8)                         \
+  V(ClampIToUint8)                              \
+  V(ClampTaggedToUint8)                         \
   V(ClassOfTest)                                \
   V(ClassOfTestAndBranch)                       \
   V(CmpID)                                      \
@@ -1907,6 +1910,47 @@ class LCheckSmi: public LTemplateInstruction<0, 1, 0> {
 };
 
 
+class LClampDoubleToUint8: public LTemplateInstruction<1, 1, 1> {
+ public:
+  explicit LClampDoubleToUint8(LOperand* value, LOperand* temp) {
+    inputs_[0] = value;
+    temps_[0] = temp;
+  }
+
+  LOperand* unclamped() { return inputs_[0]; }
+
+  DECLARE_CONCRETE_INSTRUCTION(ClampDoubleToUint8, "clamp-d-to-uint8")
+};
+
+
+class LClampIToUint8: public LTemplateInstruction<1, 1, 0> {
+ public:
+  explicit LClampIToUint8(LOperand* value) {
+    inputs_[0] = value;
+  }
+
+  LOperand* unclamped() { return inputs_[0]; }
+
+  DECLARE_CONCRETE_INSTRUCTION(ClampIToUint8, "clamp-i-to-uint8")
+};
+
+
+class LClampTaggedToUint8: public LTemplateInstruction<1, 1, 2> {
+ public:
+  explicit LClampTaggedToUint8(LOperand* value,
+                               LOperand* temp,
+                               LOperand* temp2) {
+    inputs_[0] = value;
+    temps_[0] = temp;
+    temps_[1] = temp2;
+  }
+
+  LOperand* unclamped() { return inputs_[0]; }
+
+  DECLARE_CONCRETE_INSTRUCTION(ClampTaggedToUint8, "clamp-t-to-uint8")
+};
+
+
 class LCheckNonSmi: public LTemplateInstruction<0, 1, 0> {
  public:
   explicit LCheckNonSmi(LOperand* value) {
index eb3f7c19e3d00ae16c41a6a3576f1886cbf86480..2ce4658b6d68021281a5d972e0a7af35398c31d1 100644 (file)
@@ -2570,6 +2570,37 @@ void MacroAssembler::CheckMap(Register obj,
 }
 
 
+void MacroAssembler::ClampUint8(Register reg) {
+  Label done;
+  testl(reg, Immediate(0xFFFFFF00));
+  j(zero, &done, Label::kNear);
+  setcc(negative, reg);  // 1 if negative, 0 if positive.
+  decb(reg);  // 0 if negative, 255 if positive.
+  bind(&done);
+}
+
+
+void MacroAssembler::ClampDoubleToUint8(XMMRegister input_reg,
+                                        XMMRegister temp_xmm_reg,
+                                        Register result_reg,
+                                        Register temp_reg) {
+  Label done;
+  Set(result_reg, 0);
+  xorps(temp_xmm_reg, temp_xmm_reg);
+  ucomisd(input_reg, temp_xmm_reg);
+  j(below, &done, Label::kNear);
+  uint64_t one_half = BitCast<uint64_t, double>(0.5);
+  Set(temp_reg, one_half);
+  movq(temp_xmm_reg, temp_reg);
+  addsd(temp_xmm_reg, input_reg);
+  cvttsd2si(result_reg, temp_xmm_reg);
+  testl(result_reg, Immediate(0xFFFFFF00));
+  j(zero, &done, Label::kNear);
+  Set(result_reg, 255);
+  bind(&done);
+}
+
+
 void MacroAssembler::AbortIfNotNumber(Register object) {
   Label ok;
   Condition is_smi = CheckSmi(object);
index 719c8d3d99e0c7f7a250ad904e3f3c1624dcad89..f377ca4c34d14c8edc8f1429a4d775006ec20ba1 100644 (file)
@@ -765,6 +765,13 @@ class MacroAssembler: public Assembler {
   // jcc instructions (je, ja, jae, jb, jbe, je, and jz).
   void FCmp();
 
+  void ClampUint8(Register reg);
+
+  void ClampDoubleToUint8(XMMRegister input_reg,
+                          XMMRegister temp_xmm_reg,
+                          Register result_reg,
+                          Register temp_reg);
+
   // Abort execution if argument is not a number. Used in debug code.
   void AbortIfNotNumber(Register object);
 
index f03f5799fa4d6c2ccd18a1fa6dec62b48b6d8a21..3847d595fcc4d3a98bdf745eead0d393877d95de 100644 (file)
@@ -61,7 +61,7 @@ function get(a, index) {
 function set(a, index, value) {
   a[index] = value;
 }
-
+function temp() {
 var array = new Float64Array(2);
 for (var i = 0; i < 5; i++) {
   set(array, 0, 2.5);
@@ -79,11 +79,17 @@ for (var i = 0; i < 5; i++) {
 %OptimizeFunctionOnNextCall(get);
 assertEquals(2.5, get(array, 0));
 assertEquals(3.5, get(array, 1));
+}
 
 // Test loads and stores.
-types = [Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array,
+types = [Array, Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array,
          Uint32Array, PixelArray, Float32Array, Float64Array];
 
+test_result_nan = [NaN, 0, 0, 0, 0, 0, 0, 0, NaN, NaN];
+test_result_low_int = [-1, -1, 255, -1, 65535, -1, 0xFFFFFFFF, 0, -1, -1];
+test_result_middle = [253.75, -3, 253, 253, 253, 253, 253, 254, 253.75, 253.75];
+test_result_high_int = [256, 0, 0, 256, 256, 256, 256, 255, 256, 256];
+
 const kElementCount = 40;
 
 function test_load(array, sum) {
@@ -114,37 +120,77 @@ function test_store_const_key(array, sum) {
   return sum;
 }
 
-function run_test(test_func, array, expected_sum_per_run) {
+
+function test_store_middle_double(array, sum) {
+  array[0] = 253.75;
+  return array[0];
+}
+
+
+function test_store_high_double(array, sum) {
+  array[0] = 256.25;
+  return array[0];
+}
+
+function test_store_high_double(array, sum) {
+  array[0] = 256.25;
+  return array[0];
+}
+
+function test_store_low_int(array, sum) {
+  array[0] = -1;
+  return array[0];
+}
+
+function test_store_high_int(array, sum) {
+  array[0] = 256;
+  return array[0];
+}
+
+function test_store_nan(array, sum) {
+  array[0] = NaN;
+  return array[0];
+}
+
+const kRuns = 10;
+
+function run_test(test_func, array, expected_result) {
   for (var i = 0; i < 5; i++) test_func(array, 0);
   %OptimizeFunctionOnNextCall(test_func);
-  const kRuns = 10;
   var sum = 0;
   for (var i = 0; i < kRuns; i++) {
     sum = test_func(array, sum);
   }
-  assertEquals(sum, expected_sum_per_run * kRuns);
+  assertEquals(expected_result, sum);
   %DeoptimizeFunction(test_func);
   gc();  // Makes V8 forget about type information for test_func.
 }
 
 for (var t = 0; t < types.length; t++) {
   var type = types[t];
+  print ("type = " + t);
   var a = new type(kElementCount);
   for (var i = 0; i < kElementCount; i++) {
     a[i] = i;
   }
-  
+
   // Run test functions defined above.
-  run_test(test_load, a, 780);
-  run_test(test_load_const_key, a, 3);
-  run_test(test_store, a, 820);
-  run_test(test_store_const_key, a, 6);
-  
+  run_test(test_load, a, 780 * kRuns);
+  run_test(test_load_const_key, a, 3 * kRuns);
+  run_test(test_store, a, 820 * kRuns);
+  run_test(test_store_const_key, a, 6 * kRuns);
+  run_test(test_store_low_int, a, test_result_low_int[t]);
+  run_test(test_store_high_int, a, test_result_high_int[t]);
+  run_test(test_store_nan, a, test_result_nan[t]);
+  run_test(test_store_middle_double, a, test_result_middle[t]);
+
   // Test the correct behavior of the |length| property (which is read-only).
-  assertEquals(kElementCount, a.length);
-  a.length = 2;
-  assertEquals(kElementCount, a.length);
-  assertTrue(delete a.length);
-  a.length = 2
-  assertEquals(2, a.length);
+  if (t != 0) {
+    assertEquals(kElementCount, a.length);
+    a.length = 2;
+    assertEquals(kElementCount, a.length);
+    assertTrue(delete a.length);
+    a.length = 2;
+    assertEquals(2, a.length);
+  }
 }