Commit daa1be12 authored by danno@chromium.org's avatar danno@chromium.org

Support conversion of clamped double values for pixel arrays in Crankshaft.

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
parent fbcc2ae6
......@@ -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));
}
......
......@@ -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")
......
......@@ -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)) {
......
......@@ -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),
......
......@@ -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,
......
......@@ -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)));
......
......@@ -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();
......
......@@ -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));
}
}
......
......@@ -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)
......
......@@ -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,
......
......@@ -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 =
......
......@@ -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) {
......
......@@ -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) {
......
......@@ -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,
......
......@@ -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);
......
......@@ -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 =
......
......@@ -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));
}
......
......@@ -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) {
......
......@@ -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);
......
......@@ -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);
......
......@@ -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);
}
}
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