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
This commit is contained in:
parent
fbcc2ae6f0
commit
daa1be1226
@ -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);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user