Improvements for x87 stack handling

BUG=

Review URL: https://codereview.chromium.org/13426006

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@14179 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 74839e86
......@@ -3190,13 +3190,19 @@ class HConstant: public HTemplateInstruction<0> {
bool InOldSpace() const { return !HEAP->InNewSpace(*handle_); }
bool IsSpecialDouble() const {
return has_double_value_ &&
(BitCast<int64_t>(double_value_) == BitCast<int64_t>(-0.0) ||
FixedDoubleArray::is_the_hole_nan(double_value_) ||
isnan(double_value_));
}
bool ImmortalImmovable() const {
if (has_int32_value_) {
return false;
}
if (has_double_value_) {
if (BitCast<int64_t>(double_value_) == BitCast<int64_t>(-0.0) ||
isnan(double_value_)) {
if (IsSpecialDouble()) {
return true;
}
return false;
......@@ -3227,7 +3233,9 @@ class HConstant: public HTemplateInstruction<0> {
return has_int32_value_;
}
virtual bool EmitAtUses() { return !representation().IsDouble(); }
virtual bool EmitAtUses() {
return !representation().IsDouble() || IsSpecialDouble();
}
virtual void PrintDataTo(StringStream* stream);
virtual HType CalculateInferredType();
bool IsInteger() { return handle()->IsSmi(); }
......@@ -3246,6 +3254,16 @@ class HConstant: public HTemplateInstruction<0> {
ASSERT(HasDoubleValue());
return double_value_;
}
bool IsTheHole() const {
if (HasDoubleValue() && FixedDoubleArray::is_the_hole_nan(double_value_)) {
return true;
}
Heap* heap = isolate()->heap();
if (!handle_.is_null() && *handle_ == heap->the_hole_value()) {
return true;
}
return false;
}
bool HasNumberValue() const { return has_double_value_; }
int32_t NumberValueAsInteger32() const {
ASSERT(HasNumberValue());
......@@ -5677,6 +5695,10 @@ class HStoreKeyed
bool IsDehoisted() { return is_dehoisted_; }
void SetDehoisted(bool is_dehoisted) { is_dehoisted_ = is_dehoisted; }
bool IsConstantHoleStore() {
return value()->IsConstant() && HConstant::cast(value())->IsTheHole();
}
virtual void SetSideEffectDominator(GVNFlag side_effect, HValue* dominator) {
ASSERT(side_effect == kChangesNewSpacePromotion);
new_space_dominator_ = dominator;
......
......@@ -366,7 +366,20 @@ bool LCodeGen::GenerateBody() {
Comment(";;; @%d: %s.", current_instruction_, instr->Mnemonic());
}
}
if (!CpuFeatures::IsSupported(SSE2)) {
FlushX87StackIfNecessary(instr);
}
instr->CompileToNative(this);
if (!CpuFeatures::IsSupported(SSE2)) {
ASSERT(!instr->HasDoubleRegisterResult() || x87_stack_depth_ == 1);
if (FLAG_debug_code && FLAG_enable_slow_asserts) {
__ VerifyX87StackDepth(x87_stack_depth_);
}
}
}
}
EnsureSpaceForLazyDeopt();
......@@ -521,6 +534,52 @@ bool LCodeGen::IsX87TopOfStack(LOperand* op) const {
}
void LCodeGen::ReadX87Operand(Operand dst) {
ASSERT(x87_stack_depth_ == 1);
__ fst_d(dst);
}
void LCodeGen::PushX87DoubleOperand(Operand src) {
ASSERT(x87_stack_depth_ == 0);
x87_stack_depth_++;
__ fld_d(src);
}
void LCodeGen::PushX87FloatOperand(Operand src) {
ASSERT(x87_stack_depth_ == 0);
x87_stack_depth_++;
__ fld_s(src);
}
void LCodeGen::PopX87() {
ASSERT(x87_stack_depth_ == 1);
x87_stack_depth_--;
__ fstp(0);
}
void LCodeGen::CurrentInstructionReturnsX87Result() {
ASSERT(x87_stack_depth_ <= 1);
if (x87_stack_depth_ == 0) {
x87_stack_depth_ = 1;
}
}
void LCodeGen::FlushX87StackIfNecessary(LInstruction* instr) {
if (x87_stack_depth_ > 0) {
if ((instr->ClobbersDoubleRegisters() ||
instr->HasDoubleRegisterResult()) &&
!instr->HasDoubleRegisterInput()) {
PopX87();
}
}
}
Register LCodeGen::ToRegister(LOperand* op) const {
ASSERT(op->IsRegister());
return ToRegister(op->index());
......@@ -846,6 +905,8 @@ void LCodeGen::RegisterEnvironmentForDeoptimization(
void LCodeGen::DeoptimizeIf(Condition cc, LEnvironment* environment) {
RegisterEnvironmentForDeoptimization(environment, Safepoint::kNoLazyDeopt);
ASSERT(environment->HasBeenRegistered());
// It's an error to deoptimize with the x87 fp stack in use.
ASSERT(x87_stack_depth_ == 0);
int id = environment->deoptimization_index();
ASSERT(info()->IsOptimizing() || info()->IsStub());
Deoptimizer::BailoutType bailout_type = info()->IsStub()
......@@ -1689,40 +1750,46 @@ void LCodeGen::DoConstantI(LConstantI* instr) {
void LCodeGen::DoConstantD(LConstantD* instr) {
ASSERT(instr->result()->IsDoubleRegister());
XMMRegister res = ToDoubleRegister(instr->result());
double v = instr->value();
// Use xor to produce +0.0 in a fast and compact way, but avoid to
// do so if the constant is -0.0.
if (BitCast<uint64_t, double>(v) == 0) {
__ xorps(res, res);
uint64_t int_val = BitCast<uint64_t, double>(v);
int32_t lower = static_cast<int32_t>(int_val);
int32_t upper = static_cast<int32_t>(int_val >> (kBitsPerInt));
if (!CpuFeatures::IsSafeForSnapshot(SSE2)) {
__ push(Immediate(lower));
__ push(Immediate(upper));
PushX87DoubleOperand(Operand(esp, 0));
__ add(Operand(esp), Immediate(kDoubleSize));
CurrentInstructionReturnsX87Result();
} else {
Register temp = ToRegister(instr->temp());
uint64_t int_val = BitCast<uint64_t, double>(v);
int32_t lower = static_cast<int32_t>(int_val);
int32_t upper = static_cast<int32_t>(int_val >> (kBitsPerInt));
if (CpuFeatures::IsSupported(SSE4_1)) {
CpuFeatureScope scope1(masm(), SSE2);
CpuFeatureScope scope2(masm(), SSE4_1);
if (lower != 0) {
__ Set(temp, Immediate(lower));
__ movd(res, Operand(temp));
__ Set(temp, Immediate(upper));
__ pinsrd(res, Operand(temp), 1);
CpuFeatureScope scope1(masm(), SSE2);
ASSERT(instr->result()->IsDoubleRegister());
XMMRegister res = ToDoubleRegister(instr->result());
if (int_val == 0) {
__ xorps(res, res);
} else {
Register temp = ToRegister(instr->temp());
if (CpuFeatures::IsSupported(SSE4_1)) {
CpuFeatureScope scope2(masm(), SSE4_1);
if (lower != 0) {
__ Set(temp, Immediate(lower));
__ movd(res, Operand(temp));
__ Set(temp, Immediate(upper));
__ pinsrd(res, Operand(temp), 1);
} else {
__ xorps(res, res);
__ Set(temp, Immediate(upper));
__ pinsrd(res, Operand(temp), 1);
}
} else {
__ xorps(res, res);
__ Set(temp, Immediate(upper));
__ pinsrd(res, Operand(temp), 1);
}
} else {
CpuFeatureScope scope(masm(), SSE2);
__ Set(temp, Immediate(upper));
__ movd(res, Operand(temp));
__ psllq(res, 32);
if (lower != 0) {
__ Set(temp, Immediate(lower));
__ movd(xmm0, Operand(temp));
__ por(res, xmm0);
__ movd(res, Operand(temp));
__ psllq(res, 32);
if (lower != 0) {
__ Set(temp, Immediate(lower));
__ movd(xmm0, Operand(temp));
__ por(res, xmm0);
}
}
}
}
......@@ -3158,16 +3225,16 @@ void LCodeGen::DoLoadKeyedExternalArray(LLoadKeyed* instr) {
__ movss(result, operand);
__ cvtss2sd(result, result);
} else {
__ fld_s(operand);
HandleX87FPReturnValue(instr);
PushX87FloatOperand(operand);
CurrentInstructionReturnsX87Result();
}
} else if (elements_kind == EXTERNAL_DOUBLE_ELEMENTS) {
if (CpuFeatures::IsSupported(SSE2)) {
CpuFeatureScope scope(masm(), SSE2);
__ movdbl(ToDoubleRegister(instr->result()), operand);
} else {
__ fld_d(operand);
HandleX87FPReturnValue(instr);
PushX87DoubleOperand(operand);
CurrentInstructionReturnsX87Result();
}
} else {
Register result(ToRegister(instr->result()));
......@@ -3212,29 +3279,6 @@ void LCodeGen::DoLoadKeyedExternalArray(LLoadKeyed* instr) {
}
void LCodeGen::HandleX87FPReturnValue(LInstruction* instr) {
if (IsX87TopOfStack(instr->result())) {
// Return value is already on stack. If the value has no uses, then
// pop it off the FP stack. Otherwise, make sure that there are enough
// copies of the value on the stack to feed all of the usages, e.g.
// when the following instruction uses the return value in multiple
// inputs.
int count = instr->hydrogen_value()->UseCount();
if (count == 0) {
__ fstp(0);
} else {
count--;
ASSERT(count <= 7);
while (count-- > 0) {
__ fld(0);
}
}
} else {
__ fstp_d(ToOperand(instr->result()));
}
}
void LCodeGen::DoLoadKeyedFixedDoubleArray(LLoadKeyed* instr) {
if (instr->hydrogen()->RequiresHoleCheck()) {
int offset = FixedDoubleArray::kHeaderSize - kHeapObjectTag +
......@@ -3261,8 +3305,8 @@ void LCodeGen::DoLoadKeyedFixedDoubleArray(LLoadKeyed* instr) {
XMMRegister result = ToDoubleRegister(instr->result());
__ movdbl(result, double_load_operand);
} else {
__ fld_d(double_load_operand);
HandleX87FPReturnValue(instr);
PushX87DoubleOperand(double_load_operand);
CurrentInstructionReturnsX87Result();
}
}
......@@ -4311,12 +4355,21 @@ void LCodeGen::DoStoreKeyedExternalArray(LStoreKeyed* instr) {
0,
instr->additional_index()));
if (elements_kind == EXTERNAL_FLOAT_ELEMENTS) {
CpuFeatureScope scope(masm(), SSE2);
__ cvtsd2ss(xmm0, ToDoubleRegister(instr->value()));
__ movss(operand, xmm0);
if (CpuFeatures::IsSafeForSnapshot(SSE2)) {
CpuFeatureScope scope(masm(), SSE2);
__ cvtsd2ss(xmm0, ToDoubleRegister(instr->value()));
__ movss(operand, xmm0);
} else {
__ fld(0);
__ fstp_s(operand);
}
} else if (elements_kind == EXTERNAL_DOUBLE_ELEMENTS) {
CpuFeatureScope scope(masm(), SSE2);
__ movdbl(operand, ToDoubleRegister(instr->value()));
if (CpuFeatures::IsSafeForSnapshot(SSE2)) {
CpuFeatureScope scope(masm(), SSE2);
__ movdbl(operand, ToDoubleRegister(instr->value()));
} else {
__ fst_d(operand);
}
} else {
Register value = ToRegister(instr->value());
switch (elements_kind) {
......@@ -4351,21 +4404,8 @@ void LCodeGen::DoStoreKeyedExternalArray(LStoreKeyed* instr) {
void LCodeGen::DoStoreKeyedFixedDoubleArray(LStoreKeyed* instr) {
CpuFeatureScope scope(masm(), SSE2);
XMMRegister value = ToDoubleRegister(instr->value());
if (instr->NeedsCanonicalization()) {
Label have_value;
__ ucomisd(value, value);
__ j(parity_odd, &have_value); // NaN.
ExternalReference canonical_nan_reference =
ExternalReference::address_of_canonical_non_hole_nan();
__ movdbl(value, Operand::StaticVariable(canonical_nan_reference));
__ bind(&have_value);
}
ExternalReference canonical_nan_reference =
ExternalReference::address_of_canonical_non_hole_nan();
Operand double_store_operand = BuildFastArrayOperand(
instr->elements(),
instr->key(),
......@@ -4373,7 +4413,68 @@ void LCodeGen::DoStoreKeyedFixedDoubleArray(LStoreKeyed* instr) {
FAST_DOUBLE_ELEMENTS,
FixedDoubleArray::kHeaderSize - kHeapObjectTag,
instr->additional_index());
__ movdbl(double_store_operand, value);
if (CpuFeatures::IsSafeForSnapshot(SSE2)) {
CpuFeatureScope scope(masm(), SSE2);
XMMRegister value = ToDoubleRegister(instr->value());
if (instr->NeedsCanonicalization()) {
Label have_value;
__ ucomisd(value, value);
__ j(parity_odd, &have_value); // NaN.
__ movdbl(value, Operand::StaticVariable(canonical_nan_reference));
__ bind(&have_value);
}
__ movdbl(double_store_operand, value);
} else {
// Can't use SSE2 in the serializer
if (instr->hydrogen()->IsConstantHoleStore()) {
// This means we should store the (double) hole. No floating point
// registers required.
double nan_double = FixedDoubleArray::hole_nan_as_double();
uint64_t int_val = BitCast<uint64_t, double>(nan_double);
int32_t lower = static_cast<int32_t>(int_val);
int32_t upper = static_cast<int32_t>(int_val >> (kBitsPerInt));
__ mov(double_store_operand, Immediate(lower));
Operand double_store_operand2 = BuildFastArrayOperand(
instr->elements(),
instr->key(),
instr->hydrogen()->key()->representation(),
FAST_DOUBLE_ELEMENTS,
FixedDoubleArray::kHeaderSize - kHeapObjectTag + kPointerSize,
instr->additional_index());
__ mov(double_store_operand2, Immediate(upper));
} else {
Label no_special_nan_handling;
ASSERT(x87_stack_depth_ > 0);
if (instr->NeedsCanonicalization()) {
__ fld(0);
__ fld(0);
__ FCmp();
__ j(parity_odd, &no_special_nan_handling);
__ sub(esp, Immediate(kDoubleSize));
__ fst_d(MemOperand(esp, 0));
__ cmp(MemOperand(esp, sizeof(kHoleNanLower32)),
Immediate(kHoleNanUpper32));
__ add(esp, Immediate(kDoubleSize));
Label canonicalize;
__ j(not_equal, &canonicalize);
__ jmp(&no_special_nan_handling);
__ bind(&canonicalize);
__ fstp(0);
__ fld_d(Operand::StaticVariable(canonical_nan_reference));
}
__ bind(&no_special_nan_handling);
__ fst_d(double_store_operand);
}
}
}
......@@ -4805,9 +4906,6 @@ void LCodeGen::DoNumberTagD(LNumberTagD* instr) {
XMMRegister input_reg = ToDoubleRegister(instr->value());
__ ucomisd(input_reg, input_reg);
} else {
if (!IsX87TopOfStack(instr->value())) {
__ fld_d(ToOperand(instr->value()));
}
__ fld(0);
__ fld(0);
__ FCmp();
......@@ -4829,6 +4927,9 @@ void LCodeGen::DoNumberTagD(LNumberTagD* instr) {
__ j(not_equal, &canonicalize);
__ add(esp, Immediate(kDoubleSize));
__ mov(reg, factory()->the_hole_value());
if (!use_sse2) {
__ fstp(0);
}
__ jmp(&done);
__ bind(&canonicalize);
__ add(esp, Immediate(kDoubleSize));
......@@ -4858,10 +4959,7 @@ void LCodeGen::DoNumberTagD(LNumberTagD* instr) {
XMMRegister input_reg = ToDoubleRegister(instr->value());
__ movdbl(FieldOperand(reg, HeapNumber::kValueOffset), input_reg);
} else {
if (!IsX87TopOfStack(instr->value())) {
__ fld_d(ToOperand(instr->value()));
}
__ fstp_d(FieldOperand(reg, HeapNumber::kValueOffset));
__ fst_d(FieldOperand(reg, HeapNumber::kValueOffset));
}
__ bind(&done);
}
......@@ -4909,6 +5007,79 @@ void LCodeGen::DoSmiUntag(LSmiUntag* instr) {
}
void LCodeGen::EmitNumberUntagDNoSSE2(Register input_reg,
Register temp_reg,
bool deoptimize_on_undefined,
bool deoptimize_on_minus_zero,
LEnvironment* env,
NumberUntagDMode mode) {
Label load_smi, done;
if (mode == NUMBER_CANDIDATE_IS_ANY_TAGGED) {
// Smi check.
__ JumpIfSmi(input_reg, &load_smi, Label::kNear);
// Heap number map check.
__ cmp(FieldOperand(input_reg, HeapObject::kMapOffset),
factory()->heap_number_map());
if (deoptimize_on_undefined) {
DeoptimizeIf(not_equal, env);
} else {
Label heap_number;
__ j(equal, &heap_number, Label::kNear);
__ cmp(input_reg, factory()->undefined_value());
DeoptimizeIf(not_equal, env);
// Convert undefined to NaN.
ExternalReference nan =
ExternalReference::address_of_canonical_non_hole_nan();
__ fld_d(Operand::StaticVariable(nan));
__ jmp(&done, Label::kNear);
__ bind(&heap_number);
}
// Heap number to x87 conversion.
__ fld_d(FieldOperand(input_reg, HeapNumber::kValueOffset));
if (deoptimize_on_minus_zero) {
__ fldz();
__ FCmp();
__ fld_d(FieldOperand(input_reg, HeapNumber::kValueOffset));
__ j(not_zero, &done, Label::kNear);
// Use general purpose registers to check if we have -0.0
__ mov(temp_reg, FieldOperand(input_reg, HeapNumber::kExponentOffset));
__ test(temp_reg, Immediate(HeapNumber::kSignMask));
__ j(zero, &done, Label::kNear);
// Pop FPU stack before deoptimizing.
__ fstp(0);
DeoptimizeIf(not_zero, env);
}
__ jmp(&done, Label::kNear);
} else if (mode == NUMBER_CANDIDATE_IS_SMI_OR_HOLE) {
__ test(input_reg, Immediate(kSmiTagMask));
DeoptimizeIf(not_equal, env);
} else if (mode == NUMBER_CANDIDATE_IS_SMI_CONVERT_HOLE) {
__ test(input_reg, Immediate(kSmiTagMask));
__ j(zero, &load_smi);
ExternalReference hole_nan_reference =
ExternalReference::address_of_the_hole_nan();
__ fld_d(Operand::StaticVariable(hole_nan_reference));
__ jmp(&done, Label::kNear);
} else {
ASSERT(mode == NUMBER_CANDIDATE_IS_SMI);
}
__ bind(&load_smi);
__ SmiUntag(input_reg); // Untag smi before converting to float.
__ push(input_reg);
__ fild_s(Operand(esp, 0));
__ pop(input_reg);
__ SmiTag(input_reg); // Retag smi.
__ bind(&done);
}
void LCodeGen::EmitNumberUntagD(Register input_reg,
Register temp_reg,
XMMRegister result_reg,
......@@ -5021,7 +5192,7 @@ void LCodeGen::DoDeferredTaggedToI(LTaggedToI* instr) {
__ fisttp_d(Operand(esp, 0));
__ mov(input_reg, Operand(esp, 0)); // Low word of answer is the result.
__ add(Operand(esp), Immediate(kDoubleSize));
} else {
} else if (CpuFeatures::IsSupported(SSE2)) {
CpuFeatureScope scope(masm(), SSE2);
XMMRegister xmm_temp = ToDoubleRegister(instr->temp());
__ movdbl(xmm0, FieldOperand(input_reg, HeapNumber::kValueOffset));
......@@ -5035,6 +5206,8 @@ void LCodeGen::DoDeferredTaggedToI(LTaggedToI* instr) {
__ ucomisd(xmm_temp, xmm0);
DeoptimizeIf(not_equal, instr->environment());
DeoptimizeIf(parity_even, instr->environment()); // NaN.
} else {
UNREACHABLE();
}
} else if (CpuFeatures::IsSupported(SSE2)) {
CpuFeatureScope scope(masm(), SSE2);
......@@ -5079,18 +5252,169 @@ void LCodeGen::DoTaggedToI(LTaggedToI* instr) {
LOperand* input = instr->value();
ASSERT(input->IsRegister());
ASSERT(input->Equals(instr->result()));
Register input_reg = ToRegister(input);
ASSERT(input_reg.is(ToRegister(instr->result())));
DeferredTaggedToI* deferred = new(zone()) DeferredTaggedToI(this, instr);
// Smi check.
__ JumpIfNotSmi(input_reg, deferred->entry());
__ SmiUntag(input_reg);
__ bind(deferred->exit());
}
// Smi to int32 conversion
__ SmiUntag(input_reg); // Untag smi.
void LCodeGen::DoDeferredTaggedToINoSSE2(LTaggedToINoSSE2* instr) {
Label done, heap_number;
Register result_reg = ToRegister(instr->result());
Register input_reg = ToRegister(instr->value());
// Heap number map check.
__ 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 truncating
// conversions.
__ cmp(input_reg, factory()->undefined_value());
__ RecordComment("Deferred TaggedToI: cannot truncate");
DeoptimizeIf(not_equal, instr->environment());
__ xor_(result_reg, result_reg);
__ jmp(&done, Label::kFar);
__ bind(&heap_number);
// Surprisingly, all of this crazy bit manipulation is considerably
// faster than using the built-in x86 CPU conversion functions (about 6x).
Label right_exponent, adjust_bias, zero_result;
Register scratch = ToRegister(instr->scratch());
Register scratch2 = ToRegister(instr->scratch2());
// Get exponent word.
__ mov(scratch, FieldOperand(input_reg, HeapNumber::kExponentOffset));
// Get exponent alone in scratch2.
__ mov(scratch2, scratch);
__ and_(scratch2, HeapNumber::kExponentMask);
__ shr(scratch2, HeapNumber::kExponentShift);
if (instr->truncating()) {
__ j(zero, &zero_result);
} else {
__ j(not_zero, &adjust_bias);
__ test(scratch, Immediate(HeapNumber::kMantissaMask));
DeoptimizeIf(not_zero, instr->environment());
__ cmp(FieldOperand(input_reg, HeapNumber::kMantissaOffset), Immediate(0));
DeoptimizeIf(not_equal, instr->environment());
__ bind(&adjust_bias);
}
__ sub(scratch2, Immediate(HeapNumber::kExponentBias));
if (!instr->truncating()) {
DeoptimizeIf(negative, instr->environment());
} else {
__ j(negative, &zero_result);
}
// Get the second half of the double. For some exponents we don't
// actually need this because the bits get shifted out again, but
// it's probably slower to test than just to do it.
Register scratch3 = ToRegister(instr->scratch3());
__ mov(scratch3, FieldOperand(input_reg, HeapNumber::kMantissaOffset));
__ xor_(result_reg, result_reg);
const uint32_t non_int32_exponent = 31;
__ cmp(scratch2, Immediate(non_int32_exponent));
// If we have a match of the int32 exponent then skip some logic.
__ j(equal, &right_exponent, Label::kNear);
// If the number doesn't find in an int32, deopt.
DeoptimizeIf(greater, instr->environment());
// Exponent word in scratch, exponent in scratch2. We know that 0 <= exponent
// < 31.
__ mov(result_reg, Immediate(31));
__ sub(result_reg, scratch2);
__ bind(&right_exponent);
// Save off exponent for negative check later.
__ mov(scratch2, scratch);
// Here result_reg is the shift, scratch is the exponent word.
// Get the top bits of the mantissa.
__ and_(scratch, HeapNumber::kMantissaMask);
// Put back the implicit 1.
__ or_(scratch, 1 << HeapNumber::kExponentShift);
// Shift up the mantissa bits to take up the space the exponent used to
// take. We have kExponentShift + 1 significant bits int he low end of the
// word. Shift them to the top bits.
const int shift_distance = HeapNumber::kNonMantissaBitsInTopWord - 1;
__ shl(scratch, shift_distance);
if (!instr->truncating()) {
// If not truncating, a non-zero value in the bottom 22 bits means a
// non-integral value --> trigger a deopt.
__ test(scratch3, Immediate((1 << (32 - shift_distance)) - 1));
DeoptimizeIf(not_equal, instr->environment());
}
// Shift down 22 bits to get the most significant 10 bits or the low
// mantissa word.
__ shr(scratch3, 32 - shift_distance);
__ or_(scratch3, scratch);
if (!instr->truncating()) {
// If truncating, a non-zero value in the bits that will be shifted away
// when adjusting the exponent means rounding --> deopt.
__ mov(scratch, 0x1);
ASSERT(result_reg.is(ecx));
__ shl_cl(scratch);
__ dec(scratch);
__ test(scratch3, scratch);
DeoptimizeIf(not_equal, instr->environment());
}
// Move down according to the exponent.
ASSERT(result_reg.is(ecx));
__ shr_cl(scratch3);
// Now the unsigned 32-bit answer is in scratch3. We need to move it to
// result_reg and we may need to fix the sign.
Label negative_result;
__ xor_(result_reg, result_reg);
__ cmp(scratch2, result_reg);
__ j(less, &negative_result, Label::kNear);
__ cmp(scratch3, result_reg);
__ mov(result_reg, scratch3);
// If the result is > MAX_INT, result doesn't fit in signed 32-bit --> deopt.
DeoptimizeIf(less, instr->environment());
__ jmp(&done, Label::kNear);
__ bind(&zero_result);
__ xor_(result_reg, result_reg);
__ jmp(&done, Label::kNear);
__ bind(&negative_result);
__ sub(result_reg, scratch3);
if (!instr->truncating()) {
// -0.0 triggers a deopt.
DeoptimizeIf(zero, instr->environment());
}
// If the negative subtraction overflows into a positive number, there was an
// overflow --> deopt.
DeoptimizeIf(positive, instr->environment());
__ bind(&done);
}
void LCodeGen::DoTaggedToINoSSE2(LTaggedToINoSSE2* instr) {
class DeferredTaggedToINoSSE2: public LDeferredCode {
public:
DeferredTaggedToINoSSE2(LCodeGen* codegen, LTaggedToINoSSE2* instr)
: LDeferredCode(codegen), instr_(instr) { }
virtual void Generate() { codegen()->DoDeferredTaggedToINoSSE2(instr_); }
virtual LInstruction* instr() { return instr_; }
private:
LTaggedToINoSSE2* instr_;
};
LOperand* input = instr->value();
ASSERT(input->IsRegister());
Register input_reg = ToRegister(input);
ASSERT(input_reg.is(ToRegister(instr->result())));
DeferredTaggedToINoSSE2* deferred =
new(zone()) DeferredTaggedToINoSSE2(this, instr);
// Smi check.
__ JumpIfNotSmi(input_reg, deferred->entry());
__ SmiUntag(input_reg); // Untag smi.
__ bind(deferred->exit());
}
......@@ -5103,32 +5427,31 @@ void LCodeGen::DoNumberUntagD(LNumberUntagD* instr) {
LOperand* result = instr->result();
ASSERT(result->IsDoubleRegister());
if (CpuFeatures::IsSupported(SSE2)) {
CpuFeatureScope scope(masm(), SSE2);
Register input_reg = ToRegister(input);
XMMRegister result_reg = ToDoubleRegister(result);
bool deoptimize_on_minus_zero =
instr->hydrogen()->deoptimize_on_minus_zero();
Register temp_reg = deoptimize_on_minus_zero ? ToRegister(temp) : no_reg;
NumberUntagDMode mode = NUMBER_CANDIDATE_IS_ANY_TAGGED;
HValue* value = instr->hydrogen()->value();
if (value->type().IsSmi()) {
if (value->IsLoadKeyed()) {
HLoadKeyed* load = HLoadKeyed::cast(value);
if (load->UsesMustHandleHole()) {
if (load->hole_mode() == ALLOW_RETURN_HOLE) {
mode = NUMBER_CANDIDATE_IS_SMI_CONVERT_HOLE;
} else {
mode = NUMBER_CANDIDATE_IS_SMI_OR_HOLE;
}
Register input_reg = ToRegister(input);
bool deoptimize_on_minus_zero =
instr->hydrogen()->deoptimize_on_minus_zero();
Register temp_reg = deoptimize_on_minus_zero ? ToRegister(temp) : no_reg;
NumberUntagDMode mode = NUMBER_CANDIDATE_IS_ANY_TAGGED;
HValue* value = instr->hydrogen()->value();
if (value->type().IsSmi()) {
if (value->IsLoadKeyed()) {
HLoadKeyed* load = HLoadKeyed::cast(value);
if (load->UsesMustHandleHole()) {
if (load->hole_mode() == ALLOW_RETURN_HOLE) {
mode = NUMBER_CANDIDATE_IS_SMI_CONVERT_HOLE;
} else {
mode = NUMBER_CANDIDATE_IS_SMI;
mode = NUMBER_CANDIDATE_IS_SMI_OR_HOLE;
}
} else {
mode = NUMBER_CANDIDATE_IS_SMI;
}
}
}
if (CpuFeatures::IsSupported(SSE2)) {
CpuFeatureScope scope(masm(), SSE2);
XMMRegister result_reg = ToDoubleRegister(result);
EmitNumberUntagD(input_reg,
temp_reg,
result_reg,
......@@ -5137,7 +5460,13 @@ void LCodeGen::DoNumberUntagD(LNumberUntagD* instr) {
instr->environment(),
mode);
} else {
UNIMPLEMENTED();
EmitNumberUntagDNoSSE2(input_reg,
temp_reg,
instr->hydrogen()->deoptimize_on_undefined(),
deoptimize_on_minus_zero,
instr->environment(),
mode);
CurrentInstructionReturnsX87Result();
}
}
......@@ -5409,7 +5738,128 @@ void LCodeGen::DoClampTToUint8(LClampTToUint8* instr) {
__ bind(&is_smi);
__ SmiUntag(input_reg);
__ ClampUint8(input_reg);
__ bind(&done);
}
void LCodeGen::DoClampTToUint8NoSSE2(LClampTToUint8NoSSE2* instr) {
Register input_reg = ToRegister(instr->unclamped());
Register result_reg = ToRegister(instr->result());
Register scratch = ToRegister(instr->scratch());
Register scratch2 = ToRegister(instr->scratch2());
Register scratch3 = ToRegister(instr->scratch3());
Label is_smi, done, heap_number, valid_exponent,
largest_value, zero_result, maybe_nan_or_infinity;
__ JumpIfSmi(input_reg, &is_smi);
// Check for heap number
__ cmp(FieldOperand(input_reg, HeapObject::kMapOffset),
factory()->heap_number_map());
__ j(equal, &heap_number, Label::kFar);
// Check for undefined. Undefined is converted to zero for clamping
// conversions.
__ cmp(input_reg, factory()->undefined_value());
DeoptimizeIf(not_equal, instr->environment());
__ jmp(&zero_result);
// Heap number
__ bind(&heap_number);
// Surprisingly, all of the hand-crafted bit-manipulations below are much
// faster than the x86 FPU built-in instruction, especially since "banker's
// rounding" would be additionally very expensive
// Get exponent word.
__ mov(scratch, FieldOperand(input_reg, HeapNumber::kExponentOffset));
__ mov(scratch3, FieldOperand(input_reg, HeapNumber::kMantissaOffset));
// Test for negative values --> clamp to zero
__ test(scratch, scratch);
__ j(negative, &zero_result);
// Get exponent alone in scratch2.
__ mov(scratch2, scratch);
__ and_(scratch2, HeapNumber::kExponentMask);
__ shr(scratch2, HeapNumber::kExponentShift);
__ j(zero, &zero_result);
__ sub(scratch2, Immediate(HeapNumber::kExponentBias - 1));
__ j(negative, &zero_result);
const uint32_t non_int8_exponent = 7;
__ cmp(scratch2, Immediate(non_int8_exponent + 1));
// If the exponent is too big, check for special values.
__ j(greater, &maybe_nan_or_infinity, Label::kNear);
__ bind(&valid_exponent);
// Exponent word in scratch, exponent in scratch2. We know that 0 <= exponent
// < 7. The shift bias is the number of bits to shift the mantissa such that
// with an exponent of 7 such the that top-most one is in bit 30, allowing
// detection the rounding overflow of a 255.5 to 256 (bit 31 goes from 0 to
// 1).
int shift_bias = (30 - HeapNumber::kExponentShift) - 7 - 1;
__ lea(result_reg, MemOperand(scratch2, shift_bias));
// Here result_reg (ecx) is the shift, scratch is the exponent word. Get the
// top bits of the mantissa.
__ and_(scratch, HeapNumber::kMantissaMask);
// Put back the implicit 1 of the mantissa
__ or_(scratch, 1 << HeapNumber::kExponentShift);
// Shift up to round
__ shl_cl(scratch);
// Use "banker's rounding" to spec: If fractional part of number is 0.5, then
// use the bit in the "ones" place and add it to the "halves" place, which has
// the effect of rounding to even.
__ mov(scratch2, scratch);
const uint32_t one_half_bit_shift = 30 - sizeof(uint8_t) * 8;
const uint32_t one_bit_shift = one_half_bit_shift + 1;
__ and_(scratch2, Immediate((1 << one_bit_shift) - 1));
__ cmp(scratch2, Immediate(1 << one_half_bit_shift));
Label no_round;
__ j(less, &no_round);
Label round_up;
__ mov(scratch2, Immediate(1 << one_half_bit_shift));
__ j(greater, &round_up);
__ test(scratch3, scratch3);
__ j(not_zero, &round_up);
__ mov(scratch2, scratch);
__ and_(scratch2, Immediate(1 << one_bit_shift));
__ shr(scratch2, 1);
__ bind(&round_up);
__ add(scratch, scratch2);
__ j(overflow, &largest_value);
__ bind(&no_round);
__ shr(scratch, 23);
__ mov(result_reg, scratch);
__ jmp(&done, Label::kNear);
__ bind(&maybe_nan_or_infinity);
// Check for NaN/Infinity, all other values map to 255
__ cmp(scratch2, Immediate(HeapNumber::kInfinityOrNanExponent + 1));
__ j(not_equal, &largest_value, Label::kNear);
// Check for NaN, which differs from Infinity in that at least one mantissa
// bit is set.
__ and_(scratch, HeapNumber::kMantissaMask);
__ or_(scratch, FieldOperand(input_reg, HeapNumber::kMantissaOffset));
__ j(not_zero, &zero_result); // M!=0 --> NaN
// Infinity -> Fall through to map to 255.
__ bind(&largest_value);
__ mov(result_reg, Immediate(255));
__ jmp(&done, Label::kNear);
__ bind(&zero_result);
__ xor_(result_reg, result_reg);
__ jmp(&done);
// smi
__ bind(&is_smi);
if (!input_reg.is(result_reg)) {
__ mov(result_reg, input_reg);
}
__ SmiUntag(result_reg);
__ ClampUint8(result_reg);
__ bind(&done);
}
......
......@@ -68,6 +68,7 @@ class LCodeGen BASE_EMBEDDED {
osr_pc_offset_(-1),
last_lazy_deopt_pc_(0),
frame_is_built_(false),
x87_stack_depth_(0),
safepoints_(info->zone()),
resolver_(this),
expected_safepoint_kind_(Safepoint::kSimple) {
......@@ -102,10 +103,17 @@ class LCodeGen BASE_EMBEDDED {
return Immediate(ToInteger32(LConstantOperand::cast(op)));
}
Handle<Object> ToHandle(LConstantOperand* op) const;
// Support for non-sse2 (x87) floating point stack handling.
// These functions maintain the depth of the stack (either 0 or 1)
void PushX87DoubleOperand(Operand src);
void PushX87FloatOperand(Operand src);
void ReadX87Operand(Operand dst);
bool X87StackNonEmpty() const { return x87_stack_depth_ > 0; }
void PopX87();
void CurrentInstructionReturnsX87Result();
void FlushX87StackIfNecessary(LInstruction* instr);
// A utility for instructions that return floating point values on X87.
void HandleX87FPReturnValue(LInstruction* instr);
Handle<Object> ToHandle(LConstantOperand* op) const;
// The operand denoting the second word (the one with a higher address) of
// a double stack slot.
......@@ -129,6 +137,7 @@ class LCodeGen BASE_EMBEDDED {
IntegerSignedness signedness);
void DoDeferredTaggedToI(LTaggedToI* instr);
void DoDeferredTaggedToINoSSE2(LTaggedToINoSSE2* instr);
void DoDeferredMathAbsTaggedHeapNumber(LUnaryMathOperation* instr);
void DoDeferredStackCheck(LStackCheck* instr);
void DoDeferredRandom(LRandom* instr);
......@@ -315,6 +324,14 @@ class LCodeGen BASE_EMBEDDED {
LEnvironment* env,
NumberUntagDMode mode = NUMBER_CANDIDATE_IS_ANY_TAGGED);
void EmitNumberUntagDNoSSE2(
Register input,
Register temp,
bool deoptimize_on_undefined,
bool deoptimize_on_minus_zero,
LEnvironment* env,
NumberUntagDMode mode = NUMBER_CANDIDATE_IS_ANY_TAGGED);
// Emits optimized code for typeof x == "y". Modifies input register.
// Returns the condition on which a final split to
// true and false label should be made, to optimize fallthrough.
......@@ -404,6 +421,7 @@ class LCodeGen BASE_EMBEDDED {
int osr_pc_offset_;
int last_lazy_deopt_pc_;
bool frame_is_built_;
int x87_stack_depth_;
// Builder that keeps track of safepoints in the code. The table
// itself is emitted at the end of the generated code.
......
......@@ -324,29 +324,61 @@ void LGapResolver::EmitMove(int index) {
}
} else if (source->IsDoubleRegister()) {
CpuFeatureScope scope(cgen_->masm(), SSE2);
XMMRegister src = cgen_->ToDoubleRegister(source);
if (destination->IsDoubleRegister()) {
XMMRegister dst = cgen_->ToDoubleRegister(destination);
__ movaps(dst, src);
if (CpuFeatures::IsSupported(SSE2)) {
CpuFeatureScope scope(cgen_->masm(), SSE2);
XMMRegister src = cgen_->ToDoubleRegister(source);
if (destination->IsDoubleRegister()) {
XMMRegister dst = cgen_->ToDoubleRegister(destination);
__ movaps(dst, src);
} else {
ASSERT(destination->IsDoubleStackSlot());
Operand dst = cgen_->ToOperand(destination);
__ movdbl(dst, src);
}
} else {
// load from the register onto the stack, store in destination, which must
// be a double stack slot in the non-SSE2 case.
ASSERT(source->index() == 0); // source is on top of the stack
ASSERT(destination->IsDoubleStackSlot());
Operand dst = cgen_->ToOperand(destination);
__ movdbl(dst, src);
cgen_->ReadX87Operand(dst);
}
} else if (source->IsDoubleStackSlot()) {
CpuFeatureScope scope(cgen_->masm(), SSE2);
ASSERT(destination->IsDoubleRegister() ||
destination->IsDoubleStackSlot());
Operand src = cgen_->ToOperand(source);
if (destination->IsDoubleRegister()) {
XMMRegister dst = cgen_->ToDoubleRegister(destination);
__ movdbl(dst, src);
if (CpuFeatures::IsSupported(SSE2)) {
CpuFeatureScope scope(cgen_->masm(), SSE2);
ASSERT(destination->IsDoubleRegister() ||
destination->IsDoubleStackSlot());
Operand src = cgen_->ToOperand(source);
if (destination->IsDoubleRegister()) {
XMMRegister dst = cgen_->ToDoubleRegister(destination);
__ movdbl(dst, src);
} else {
// We rely on having xmm0 available as a fixed scratch register.
Operand dst = cgen_->ToOperand(destination);
__ movdbl(xmm0, src);
__ movdbl(dst, xmm0);
}
} else {
// We rely on having xmm0 available as a fixed scratch register.
Operand dst = cgen_->ToOperand(destination);
__ movdbl(xmm0, src);
__ movdbl(dst, xmm0);
// load from the stack slot on top of the floating point stack, and then
// store in destination. If destination is a double register, then it
// represents the top of the stack and nothing needs to be done.
if (destination->IsDoubleStackSlot()) {
Register tmp = EnsureTempRegister();
Operand src0 = cgen_->ToOperand(source);
Operand src1 = cgen_->HighOperand(source);
Operand dst0 = cgen_->ToOperand(destination);
Operand dst1 = cgen_->HighOperand(destination);
__ mov(tmp, src0); // Then use tmp to copy source to destination.
__ mov(dst0, tmp);
__ mov(tmp, src1);
__ mov(dst1, tmp);
} else {
Operand src = cgen_->ToOperand(source);
if (cgen_->X87StackNonEmpty()) {
cgen_->PopX87();
}
cgen_->PushX87DoubleOperand(src);
}
}
} else {
UNREACHABLE();
......@@ -419,21 +451,19 @@ void LGapResolver::EmitSwap(int index) {
__ movaps(xmm0, src);
__ movaps(src, dst);
__ movaps(dst, xmm0);
} else if (source->IsDoubleRegister() || destination->IsDoubleRegister()) {
CpuFeatureScope scope(cgen_->masm(), SSE2);
// XMM register-memory swap. We rely on having xmm0
// available as a fixed scratch register.
ASSERT(source->IsDoubleStackSlot() || destination->IsDoubleStackSlot());
XMMRegister reg = cgen_->ToDoubleRegister(source->IsDoubleRegister()
? source
: destination);
? source
: destination);
Operand other =
cgen_->ToOperand(source->IsDoubleRegister() ? destination : source);
__ movdbl(xmm0, other);
__ movdbl(other, reg);
__ movdbl(reg, Operand(xmm0));
} else if (source->IsDoubleStackSlot() && destination->IsDoubleStackSlot()) {
CpuFeatureScope scope(cgen_->masm(), SSE2);
// Double-width memory-to-memory. Spill on demand to use a general
......
......@@ -91,6 +91,22 @@ void LInstruction::VerifyCall() {
#endif
bool LInstruction::HasDoubleRegisterResult() {
return HasResult() && result()->IsDoubleRegister();
}
bool LInstruction::HasDoubleRegisterInput() {
for (int i = 0; i < InputCount(); i++) {
LOperand* op = InputAt(i);
if (op->IsDoubleRegister()) {
return true;
}
}
return false;
}
void LInstruction::PrintTo(StringStream* stream) {
stream->Add("%s ", this->Mnemonic());
......@@ -542,6 +558,11 @@ LOperand* LChunkBuilder::UseFixedDouble(HValue* value, XMMRegister reg) {
}
LOperand* LChunkBuilder::UseX87TopOfStack(HValue* value) {
return Use(value, ToUnallocated(x87tos));
}
LOperand* LChunkBuilder::UseRegister(HValue* value) {
return Use(value, new(zone()) LUnallocated(LUnallocated::MUST_HAVE_REGISTER));
}
......@@ -1861,20 +1882,33 @@ LInstruction* LChunkBuilder::DoChange(HChange* instr) {
? TempRegister()
: NULL;
LNumberUntagD* res = new(zone()) LNumberUntagD(value, temp);
return AssignEnvironment(DefineAsRegister(res));
if (CpuFeatures::IsSafeForSnapshot(SSE2)) {
return AssignEnvironment(DefineAsRegister(res));
} else {
return AssignEnvironment(DefineX87TOS(res));
}
} else {
ASSERT(to.IsInteger32());
LOperand* value = UseRegister(instr->value());
if (instr->value()->type().IsSmi()) {
LOperand* value = UseRegister(instr->value());
return DefineSameAsFirst(new(zone()) LSmiUntag(value, false));
} else {
bool truncating = instr->CanTruncateToInt32();
LOperand* xmm_temp =
(truncating && CpuFeatures::IsSupported(SSE3))
? NULL
: FixedTemp(xmm1);
LTaggedToI* res = new(zone()) LTaggedToI(value, xmm_temp);
return AssignEnvironment(DefineSameAsFirst(res));
if (CpuFeatures::IsSafeForSnapshot(SSE2)) {
LOperand* value = UseRegister(instr->value());
LOperand* xmm_temp =
(truncating && CpuFeatures::IsSupported(SSE3))
? NULL
: FixedTemp(xmm1);
LTaggedToI* res = new(zone()) LTaggedToI(value, xmm_temp);
return AssignEnvironment(DefineSameAsFirst(res));
} else {
LOperand* value = UseFixed(instr->value(), ecx);
LTaggedToINoSSE2* res =
new(zone()) LTaggedToINoSSE2(value, TempRegister(),
TempRegister(), TempRegister());
return AssignEnvironment(DefineFixed(res, ecx));
}
}
}
} else if (from.IsDouble()) {
......@@ -1992,12 +2026,20 @@ LInstruction* LChunkBuilder::DoClampToUint8(HClampToUint8* instr) {
return DefineFixed(new(zone()) 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);
LClampTToUint8* result = new(zone()) LClampTToUint8(reg, temp);
return AssignEnvironment(DefineFixed(result, eax));
if (CpuFeatures::IsSupported(SSE2)) {
LOperand* reg = UseFixed(value, eax);
// Register allocator doesn't (yet) support allocation of double
// temps. Reserve xmm1 explicitly.
LOperand* temp = FixedTemp(xmm1);
LClampTToUint8* result = new(zone()) LClampTToUint8(reg, temp);
return AssignEnvironment(DefineFixed(result, eax));
} else {
LOperand* value = UseRegister(instr->value());
LClampTToUint8NoSSE2* res =
new(zone()) LClampTToUint8NoSSE2(value, TempRegister(),
TempRegister(), TempRegister());
return AssignEnvironment(DefineFixed(res, ecx));
}
}
}
......@@ -2018,10 +2060,13 @@ LInstruction* LChunkBuilder::DoConstant(HConstant* instr) {
return DefineAsRegister(new(zone()) LConstantI);
} else if (r.IsDouble()) {
double value = instr->DoubleValue();
LOperand* temp = (BitCast<uint64_t, double>(value) != 0)
? TempRegister()
: NULL;
return DefineAsRegister(new(zone()) LConstantD(temp));
bool value_is_zero = BitCast<uint64_t, double>(value) == 0;
if (CpuFeatures::IsSafeForSnapshot(SSE2)) {
LOperand* temp = value_is_zero ? NULL : TempRegister();
return DefineAsRegister(new(zone()) LConstantD(temp));
} else {
return DefineX87TOS(new(zone()) LConstantD(NULL));
}
} else if (r.IsTagged()) {
return DefineAsRegister(new(zone()) LConstantT);
} else {
......@@ -2190,6 +2235,27 @@ LInstruction* LChunkBuilder::DoLoadKeyedGeneric(HLoadKeyedGeneric* instr) {
}
LOperand* LChunkBuilder::GetStoreKeyedValueOperand(HStoreKeyed* instr) {
ElementsKind elements_kind = instr->elements_kind();
// Determine if we need a byte register in this case for the value.
bool val_is_fixed_register =
elements_kind == EXTERNAL_BYTE_ELEMENTS ||
elements_kind == EXTERNAL_UNSIGNED_BYTE_ELEMENTS ||
elements_kind == EXTERNAL_PIXEL_ELEMENTS;
if (val_is_fixed_register) {
return UseFixed(instr->value(), eax);
}
if (!CpuFeatures::IsSafeForSnapshot(SSE2) &&
IsDoubleOrFloatElementsKind(elements_kind)) {
return UseRegisterAtStart(instr->value());
}
return UseRegister(instr->value());
}
LInstruction* LChunkBuilder::DoStoreKeyed(HStoreKeyed* instr) {
if (!instr->is_external()) {
ASSERT(instr->elements()->representation().IsTagged());
......@@ -2198,7 +2264,12 @@ LInstruction* LChunkBuilder::DoStoreKeyed(HStoreKeyed* instr) {
if (instr->value()->representation().IsDouble()) {
LOperand* object = UseRegisterAtStart(instr->elements());
LOperand* val = UseTempRegister(instr->value());
LOperand* val = NULL;
if (CpuFeatures::IsSafeForSnapshot(SSE2)) {
val = UseRegisterAtStart(instr->value());
} else if (!instr->IsConstantHoleStore()) {
val = UseX87TopOfStack(instr->value());
}
LOperand* key = UseRegisterOrConstantAtStart(instr->key());
return new(zone()) LStoreKeyed(object, key, val);
......@@ -2228,15 +2299,7 @@ LInstruction* LChunkBuilder::DoStoreKeyed(HStoreKeyed* instr) {
ASSERT(instr->elements()->representation().IsExternal());
LOperand* external_pointer = UseRegister(instr->elements());
// Determine if we need a byte register in this case for the value.
bool val_is_fixed_register =
elements_kind == EXTERNAL_BYTE_ELEMENTS ||
elements_kind == EXTERNAL_UNSIGNED_BYTE_ELEMENTS ||
elements_kind == EXTERNAL_PIXEL_ELEMENTS;
LOperand* val = val_is_fixed_register
? UseFixed(instr->value(), eax)
: UseRegister(instr->value());
LOperand* val = GetStoreKeyedValueOperand(instr);
bool clobbers_key = ExternalArrayOpRequiresTemp(
instr->key()->representation(), elements_kind);
LOperand* key = clobbers_key
......
......@@ -74,6 +74,7 @@ class LCodeGen;
V(ClampDToUint8) \
V(ClampIToUint8) \
V(ClampTToUint8) \
V(ClampTToUint8NoSSE2) \
V(ClassOfTestAndBranch) \
V(CmpIDAndBranch) \
V(CmpObjectEqAndBranch) \
......@@ -167,6 +168,7 @@ class LCodeGen;
V(StringLength) \
V(SubI) \
V(TaggedToI) \
V(TaggedToINoSSE2) \
V(ThisFunction) \
V(Throw) \
V(ToFastProperties) \
......@@ -265,6 +267,9 @@ class LInstruction: public ZoneObject {
virtual bool HasResult() const = 0;
virtual LOperand* result() = 0;
bool HasDoubleRegisterResult();
bool HasDoubleRegisterInput();
LOperand* FirstInput() { return InputAt(0); }
LOperand* Output() { return HasResult() ? result() : NULL; }
......@@ -1088,6 +1093,10 @@ class LConstantD: public LTemplateInstruction<1, 0, 1> {
temps_[0] = temp;
}
virtual bool ClobbersDoubleRegisters() const {
return false;
}
LOperand* temp() { return temps_[0]; }
DECLARE_CONCRETE_INSTRUCTION(ConstantD, "constant-d")
......@@ -2018,6 +2027,31 @@ class LTaggedToI: public LTemplateInstruction<1, 1, 1> {
};
// Truncating conversion from a tagged value to an int32.
class LTaggedToINoSSE2: public LTemplateInstruction<1, 1, 3> {
public:
LTaggedToINoSSE2(LOperand* value,
LOperand* temp1,
LOperand* temp2,
LOperand* temp3) {
inputs_[0] = value;
temps_[0] = temp1;
temps_[1] = temp2;
temps_[2] = temp3;
}
LOperand* value() { return inputs_[0]; }
LOperand* scratch() { return temps_[0]; }
LOperand* scratch2() { return temps_[1]; }
LOperand* scratch3() { return temps_[2]; }
DECLARE_CONCRETE_INSTRUCTION(TaggedToINoSSE2, "tagged-to-i-nosse2")
DECLARE_HYDROGEN_ACCESSOR(UnaryOperation)
bool truncating() { return hydrogen()->CanTruncateToInt32(); }
};
class LSmiTag: public LTemplateInstruction<1, 1, 0> {
public:
explicit LSmiTag(LOperand* value) {
......@@ -2040,6 +2074,10 @@ class LNumberUntagD: public LTemplateInstruction<1, 1, 1> {
LOperand* value() { return inputs_[0]; }
LOperand* temp() { return temps_[0]; }
virtual bool ClobbersDoubleRegisters() const {
return false;
}
DECLARE_CONCRETE_INSTRUCTION(NumberUntagD, "double-untag")
DECLARE_HYDROGEN_ACCESSOR(Change);
};
......@@ -2380,6 +2418,30 @@ class LClampTToUint8: public LTemplateInstruction<1, 1, 1> {
};
// Truncating conversion from a tagged value to an int32.
class LClampTToUint8NoSSE2: public LTemplateInstruction<1, 1, 3> {
public:
LClampTToUint8NoSSE2(LOperand* unclamped,
LOperand* temp1,
LOperand* temp2,
LOperand* temp3) {
inputs_[0] = unclamped;
temps_[0] = temp1;
temps_[1] = temp2;
temps_[2] = temp3;
}
LOperand* unclamped() { return inputs_[0]; }
LOperand* scratch() { return temps_[0]; }
LOperand* scratch2() { return temps_[1]; }
LOperand* scratch3() { return temps_[2]; }
DECLARE_CONCRETE_INSTRUCTION(ClampTToUint8NoSSE2,
"clamp-t-to-uint8-nosse2")
DECLARE_HYDROGEN_ACCESSOR(UnaryOperation)
};
class LCheckNonSmi: public LTemplateInstruction<0, 1, 0> {
public:
explicit LCheckNonSmi(LOperand* value) {
......@@ -2742,6 +2804,7 @@ class LChunkBuilder BASE_EMBEDDED {
MUST_USE_RESULT LOperand* UseFixed(HValue* value, Register fixed_register);
MUST_USE_RESULT LOperand* UseFixedDouble(HValue* value,
XMMRegister fixed_register);
MUST_USE_RESULT LOperand* UseX87TopOfStack(HValue* value);
// A value that is guaranteed to be allocated to a register.
// Operand created by UseRegister is guaranteed to be live until the end of
......@@ -2827,6 +2890,8 @@ class LChunkBuilder BASE_EMBEDDED {
LInstruction* DoArithmeticT(Token::Value op,
HArithmeticBinaryOperation* instr);
LOperand* GetStoreKeyedValueOperand(HStoreKeyed* instr);
LPlatformChunk* chunk_;
CompilationInfo* info_;
HGraph* const graph_;
......
......@@ -2518,6 +2518,28 @@ void MacroAssembler::Ret(int bytes_dropped, Register scratch) {
}
void MacroAssembler::VerifyX87StackDepth(uint depth) {
// Make sure the floating point stack is either empty or has depth items.
ASSERT(depth <= 7);
// The top-of-stack (tos) is 7 if there is one item pushed.
int tos = (8 - depth) % 8;
const int kTopMask = 0x3800;
push(eax);
fwait();
fnstsw_ax();
and_(eax, kTopMask);
shr(eax, 11);
cmp(eax, Immediate(tos));
Label all_ok;
j(equal, &all_ok);
Check(equal, "Unexpected FPU stack depth after instruction");
bind(&all_ok);
fnclex();
pop(eax);
}
void MacroAssembler::Drop(int stack_elements) {
if (stack_elements > 0) {
add(esp, Immediate(stack_elements * kPointerSize));
......
......@@ -807,6 +807,8 @@ class MacroAssembler: public Assembler {
return code_object_;
}
// Insert code to verify that the x87 stack has the specified depth (0-7)
void VerifyX87StackDepth(uint depth);
// ---------------------------------------------------------------------------
// StatsCounter support
......
......@@ -1788,7 +1788,7 @@ STATIC_ASSERT(DoubleRegister::kMaxNumAllocatableRegisters >=
bool LAllocator::TryAllocateFreeReg(LiveRange* current) {
LifetimePosition free_until_pos[DoubleRegister::kMaxNumAllocatableRegisters];
for (int i = 0; i < DoubleRegister::kMaxNumAllocatableRegisters; i++) {
for (int i = 0; i < num_registers_; i++) {
free_until_pos[i] = LifetimePosition::MaxPosition();
}
......@@ -1880,7 +1880,7 @@ void LAllocator::AllocateBlockedReg(LiveRange* current) {
LifetimePosition use_pos[DoubleRegister::kMaxNumAllocatableRegisters];
LifetimePosition block_pos[DoubleRegister::kMaxNumAllocatableRegisters];
for (int i = 0; i < DoubleRegister::NumAllocatableRegisters(); i++) {
for (int i = 0; i < num_registers_; i++) {
use_pos[i] = block_pos[i] = LifetimePosition::MaxPosition();
}
......
......@@ -1495,6 +1495,8 @@ class HeapNumber: public HeapObject {
static const int kExponentBits = 11;
static const int kExponentBias = 1023;
static const int kExponentShift = 20;
static const int kInfinityOrNanExponent =
(kExponentMask >> kExponentShift) - kExponentBias;
static const int kMantissaBitsInTopWord = 20;
static const int kNonMantissaBitsInTopWord = 12;
......
// Copyright 2012 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Flags: --allow-natives-syntax --expose-gc --noenable-sse2
// Helper
function assertInstance(o, f) {
assertSame(o.constructor, f);
assertInstanceof(o, f);
}
// This is a regression test for overlapping key and value registers.
function f(a) {
a[0] = 0;
a[1] = 0;
}
var a = new Int32Array(2);
for (var i = 0; i < 5; i++) {
f(a);
}
%OptimizeFunctionOnNextCall(f);
f(a);
assertEquals(0, a[0]);
assertEquals(0, a[1]);
// No-parameter constructor should fail right now.
function abfunc1() {
return new ArrayBuffer();
}
assertThrows(abfunc1);
// Test derivation from an ArrayBuffer
var ab = new ArrayBuffer(12);
assertInstance(ab, ArrayBuffer);
var derived_uint8 = new Uint8Array(ab);
assertInstance(derived_uint8, Uint8Array);
assertSame(ab, derived_uint8.buffer);
assertEquals(12, derived_uint8.length);
assertEquals(12, derived_uint8.byteLength);
assertEquals(0, derived_uint8.byteOffset);
assertEquals(1, derived_uint8.BYTES_PER_ELEMENT);
var derived_uint8_2 = new Uint8Array(ab,7);
assertInstance(derived_uint8_2, Uint8Array);
assertSame(ab, derived_uint8_2.buffer);
assertEquals(5, derived_uint8_2.length);
assertEquals(5, derived_uint8_2.byteLength);
assertEquals(7, derived_uint8_2.byteOffset);
assertEquals(1, derived_uint8_2.BYTES_PER_ELEMENT);
var derived_int16 = new Int16Array(ab);
assertInstance(derived_int16, Int16Array);
assertSame(ab, derived_int16.buffer);
assertEquals(6, derived_int16.length);
assertEquals(12, derived_int16.byteLength);
assertEquals(0, derived_int16.byteOffset);
assertEquals(2, derived_int16.BYTES_PER_ELEMENT);
var derived_int16_2 = new Int16Array(ab,6);
assertInstance(derived_int16_2, Int16Array);
assertSame(ab, derived_int16_2.buffer);
assertEquals(3, derived_int16_2.length);
assertEquals(6, derived_int16_2.byteLength);
assertEquals(6, derived_int16_2.byteOffset);
assertEquals(2, derived_int16_2.BYTES_PER_ELEMENT);
var derived_uint32 = new Uint32Array(ab);
assertInstance(derived_uint32, Uint32Array);
assertSame(ab, derived_uint32.buffer);
assertEquals(3, derived_uint32.length);
assertEquals(12, derived_uint32.byteLength);
assertEquals(0, derived_uint32.byteOffset);
assertEquals(4, derived_uint32.BYTES_PER_ELEMENT);
var derived_uint32_2 = new Uint32Array(ab,4);
assertInstance(derived_uint32_2, Uint32Array);
assertSame(ab, derived_uint32_2.buffer);
assertEquals(2, derived_uint32_2.length);
assertEquals(8, derived_uint32_2.byteLength);
assertEquals(4, derived_uint32_2.byteOffset);
assertEquals(4, derived_uint32_2.BYTES_PER_ELEMENT);
var derived_uint32_3 = new Uint32Array(ab,4,1);
assertInstance(derived_uint32_3, Uint32Array);
assertSame(ab, derived_uint32_3.buffer);
assertEquals(1, derived_uint32_3.length);
assertEquals(4, derived_uint32_3.byteLength);
assertEquals(4, derived_uint32_3.byteOffset);
assertEquals(4, derived_uint32_3.BYTES_PER_ELEMENT);
var derived_float64 = new Float64Array(ab,0,1);
assertInstance(derived_float64, Float64Array);
assertSame(ab, derived_float64.buffer);
assertEquals(1, derived_float64.length);
assertEquals(8, derived_float64.byteLength);
assertEquals(0, derived_float64.byteOffset);
assertEquals(8, derived_float64.BYTES_PER_ELEMENT);
// If a given byteOffset and length references an area beyond the end of the
// ArrayBuffer an exception is raised.
function abfunc3() {
new Uint32Array(ab,4,3);
}
assertThrows(abfunc3);
function abfunc4() {
new Uint32Array(ab,16);
}
assertThrows(abfunc4);
// The given byteOffset must be a multiple of the element size of the specific
// type, otherwise an exception is raised.
function abfunc5() {
new Uint32Array(ab,5);
}
assertThrows(abfunc5);
// If length is not explicitly specified, the length of the ArrayBuffer minus
// the byteOffset must be a multiple of the element size of the specific type,
// or an exception is raised.
var ab2 = new ArrayBuffer(13);
function abfunc6() {
new Uint32Array(ab2,4);
}
assertThrows(abfunc6);
// Test that an array constructed without an array buffer creates one properly.
a = new Uint8Array(31);
assertEquals(a.byteLength, a.buffer.byteLength);
assertEquals(a.length, a.buffer.byteLength);
assertEquals(a.length * a.BYTES_PER_ELEMENT, a.buffer.byteLength);
a = new Int16Array(5);
assertEquals(a.byteLength, a.buffer.byteLength);
assertEquals(a.length * a.BYTES_PER_ELEMENT, a.buffer.byteLength);
a = new Float64Array(7);
assertEquals(a.byteLength, a.buffer.byteLength);
assertEquals(a.length * a.BYTES_PER_ELEMENT, a.buffer.byteLength);
// Test that an implicitly created buffer is a valid buffer.
a = new Float64Array(7);
assertSame(a.buffer, (new Uint16Array(a.buffer)).buffer);
assertSame(a.buffer, (new Float32Array(a.buffer,4)).buffer);
assertSame(a.buffer, (new Int8Array(a.buffer,3,51)).buffer);
assertInstance(a.buffer, ArrayBuffer);
// Test the correct behavior of the |BYTES_PER_ELEMENT| property (which is
// "constant", but not read-only).
a = new Int32Array(2);
assertEquals(4, a.BYTES_PER_ELEMENT);
a.BYTES_PER_ELEMENT = 42;
assertEquals(42, a.BYTES_PER_ELEMENT);
a = new Uint8Array(2);
assertEquals(1, a.BYTES_PER_ELEMENT);
a = new Int16Array(2);
assertEquals(2, a.BYTES_PER_ELEMENT);
// Test Float64Arrays.
function get(a, index) {
return 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);
assertEquals(2.5, array[0]);
}
%OptimizeFunctionOnNextCall(set);
set(array, 0, 2.5);
assertEquals(2.5, array[0]);
set(array, 1, 3.5);
assertEquals(3.5, array[1]);
for (var i = 0; i < 5; i++) {
assertEquals(2.5, get(array, 0));
assertEquals(3.5, array[1]);
}
%OptimizeFunctionOnNextCall(get);
assertEquals(2.5, get(array, 0));
assertEquals(3.5, get(array, 1));
}
// Test non-number parameters.
var array_with_length_from_non_number = new Int32Array("2");
assertEquals(2, array_with_length_from_non_number.length);
array_with_length_from_non_number = new Int32Array(undefined);
assertEquals(0, array_with_length_from_non_number.length);
var foo = { valueOf: function() { return 3; } };
array_with_length_from_non_number = new Int32Array(foo);
assertEquals(3, array_with_length_from_non_number.length);
foo = { toString: function() { return "4"; } };
array_with_length_from_non_number = new Int32Array(foo);
assertEquals(4, array_with_length_from_non_number.length);
// Test loads and stores.
types = [Array, Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array,
Uint32Array, Uint8ClampedArray, 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_low_double = [-1.25, -1, 255, -1, 65535, -1, 0xFFFFFFFF, 0, -1.25, -1.25];
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];
test_result_high_double = [256.25, 0, 0, 256, 256, 256, 256, 255, 256.25, 256.25];
const kElementCount = 40;
function test_load(array, sum) {
for (var i = 0; i < kElementCount; i++) {
sum += array[i];
}
return sum;
}
function test_load_const_key(array, sum) {
sum += array[0];
sum += array[1];
sum += array[2];
return sum;
}
function test_store(array, sum) {
for (var i = 0; i < kElementCount; i++) {
sum += array[i] = i+1;
}
return sum;
}
function test_store_const_key(array, sum) {
sum += array[0] = 1;
sum += array[1] = 2;
sum += array[2] = 3;
return sum;
}
function zero() {
return 0.0;
}
function test_store_middle_tagged(array, sum) {
array[0] = 253.75;
return array[0];
}
function test_store_high_tagged(array, sum) {
array[0] = 256.25;
return array[0];
}
function test_store_middle_double(array, sum) {
array[0] = 253.75 + zero(); // + forces double type feedback
return array[0];
}
function test_store_high_double(array, sum) {
array[0] = 256.25 + zero(); // + forces double type feedback
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_low_tagged(array, sum) {
array[0] = -1.25;
return array[0];
}
function test_store_low_double(array, sum) {
array[0] = -1.25 + zero(); // + forces double type feedback
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);
var sum = 0;
for (var i = 0; i < kRuns; i++) {
sum = test_func(array, sum);
}
assertEquals(expected_result, sum);
%DeoptimizeFunction(test_func);
gc(); // Makes V8 forget about type information for test_func.
}
function run_bounds_test(test_func, array, expected_result) {
assertEquals(undefined, a[kElementCount]);
a[kElementCount] = 456;
assertEquals(undefined, a[kElementCount]);
assertEquals(undefined, a[kElementCount+1]);
a[kElementCount+1] = 456;
assertEquals(undefined, a[kElementCount+1]);
}
for (var t = 0; t < types.length; t++) {
var type = types[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 * 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_low_double, a, test_result_low_double[t]);
run_test(test_store_low_tagged, a, test_result_low_double[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]);
run_test(test_store_middle_tagged, a, test_result_middle[t]);
run_test(test_store_high_double, a, test_result_high_double[t]);
run_test(test_store_high_tagged, a, test_result_high_double[t]);
// Test the correct behavior of the |length| property (which is read-only).
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);
// Make sure bounds checks are handled correctly for external arrays.
run_bounds_test(a);
run_bounds_test(a);
run_bounds_test(a);
%OptimizeFunctionOnNextCall(run_bounds_test);
run_bounds_test(a);
%DeoptimizeFunction(run_bounds_test);
gc(); // Makes V8 forget about type information for test_func.
}
function array_load_set_smi_check(a) {
return a[0] = a[0] = 1;
}
array_load_set_smi_check(a);
array_load_set_smi_check(0);
function array_load_set_smi_check2(a) {
return a[0] = a[0] = 1;
}
array_load_set_smi_check2(a);
%OptimizeFunctionOnNextCall(array_load_set_smi_check2);
array_load_set_smi_check2(a);
array_load_set_smi_check2(0);
%DeoptimizeFunction(array_load_set_smi_check2);
gc(); // Makes V8 forget about type information for array_load_set_smi_check.
}
// Check handling of undefined in 32- and 64-bit external float arrays.
function store_float32_undefined(ext_array) {
ext_array[0] = undefined;
}
var float32_array = new Float32Array(1);
// Make sure runtime does it right
store_float32_undefined(float32_array);
assertTrue(isNaN(float32_array[0]));
// Make sure the ICs do it right
store_float32_undefined(float32_array);
assertTrue(isNaN(float32_array[0]));
// Make sure that Cranskshft does it right.
%OptimizeFunctionOnNextCall(store_float32_undefined);
store_float32_undefined(float32_array);
assertTrue(isNaN(float32_array[0]));
function store_float64_undefined(ext_array) {
ext_array[0] = undefined;
}
var float64_array = new Float64Array(1);
// Make sure runtime does it right
store_float64_undefined(float64_array);
assertTrue(isNaN(float64_array[0]));
// Make sure the ICs do it right
store_float64_undefined(float64_array);
assertTrue(isNaN(float64_array[0]));
// Make sure that Cranskshft does it right.
%OptimizeFunctionOnNextCall(store_float64_undefined);
store_float64_undefined(float64_array);
assertTrue(isNaN(float64_array[0]));
// Check handling of 0-sized buffers and arrays.
ab = new ArrayBuffer(0);
assertInstance(ab, ArrayBuffer);
assertEquals(0, ab.byteLength);
a = new Int8Array(ab);
assertInstance(a, Int8Array);
assertEquals(0, a.byteLength);
assertEquals(0, a.length);
a[0] = 1;
assertEquals(undefined, a[0]);
ab = new ArrayBuffer(16);
assertInstance(ab, ArrayBuffer);
a = new Float32Array(ab,4,0);
assertInstance(a, Float32Array);
assertEquals(0, a.byteLength);
assertEquals(0, a.length);
a[0] = 1;
assertEquals(undefined, a[0]);
a = new Uint16Array(0);
assertInstance(a, Uint16Array);
assertEquals(0, a.byteLength);
assertEquals(0, a.length);
a[0] = 1;
assertEquals(undefined, a[0]);
// Check construction from arrays.
a = new Uint32Array([]);
assertInstance(a, Uint32Array);
assertEquals(0, a.length);
assertEquals(0, a.byteLength);
assertEquals(0, a.buffer.byteLength);
assertEquals(4, a.BYTES_PER_ELEMENT);
assertInstance(a.buffer, ArrayBuffer);
a = new Uint16Array([1,2,3]);
assertInstance(a, Uint16Array);
assertEquals(3, a.length);
assertEquals(6, a.byteLength);
assertEquals(6, a.buffer.byteLength);
assertEquals(2, a.BYTES_PER_ELEMENT);
assertEquals(1, a[0]);
assertEquals(3, a[2]);
assertInstance(a.buffer, ArrayBuffer);
a = new Uint32Array(a);
assertInstance(a, Uint32Array);
assertEquals(3, a.length);
assertEquals(12, a.byteLength);
assertEquals(12, a.buffer.byteLength);
assertEquals(4, a.BYTES_PER_ELEMENT);
assertEquals(1, a[0]);
assertEquals(3, a[2]);
assertInstance(a.buffer, ArrayBuffer);
// Check subarrays.
a = new Uint16Array([1,2,3,4,5,6]);
aa = a.subarray(3);
assertInstance(aa, Uint16Array);
assertEquals(3, aa.length);
assertEquals(6, aa.byteLength);
assertEquals(2, aa.BYTES_PER_ELEMENT);
assertSame(a.buffer, aa.buffer);
aa = a.subarray(3,5);
assertInstance(aa, Uint16Array);
assertEquals(2, aa.length);
assertEquals(4, aa.byteLength);
assertEquals(2, aa.BYTES_PER_ELEMENT);
assertSame(a.buffer, aa.buffer);
aa = a.subarray(4,8);
assertInstance(aa, Uint16Array);
assertEquals(2, aa.length);
assertEquals(4, aa.byteLength);
assertEquals(2, aa.BYTES_PER_ELEMENT);
assertSame(a.buffer, aa.buffer);
aa = a.subarray(9);
assertInstance(aa, Uint16Array);
assertEquals(0, aa.length);
assertEquals(0, aa.byteLength);
assertEquals(2, aa.BYTES_PER_ELEMENT);
assertSame(a.buffer, aa.buffer);
aa = a.subarray(-4);
assertInstance(aa, Uint16Array);
assertEquals(4, aa.length);
assertEquals(8, aa.byteLength);
assertEquals(2, aa.BYTES_PER_ELEMENT);
assertSame(a.buffer, aa.buffer);
aa = a.subarray(-3,-1);
assertInstance(aa, Uint16Array);
assertEquals(2, aa.length);
assertEquals(4, aa.byteLength);
assertEquals(2, aa.BYTES_PER_ELEMENT);
assertSame(a.buffer, aa.buffer);
aa = a.subarray(3,2);
assertInstance(aa, Uint16Array);
assertEquals(0, aa.length);
assertEquals(0, aa.byteLength);
assertEquals(2, aa.BYTES_PER_ELEMENT);
assertSame(a.buffer, aa.buffer);
aa = a.subarray(-3,-4);
assertInstance(aa, Uint16Array);
assertEquals(0, aa.length);
assertEquals(0, aa.byteLength);
assertEquals(2, aa.BYTES_PER_ELEMENT);
assertSame(a.buffer, aa.buffer);
aa = a.subarray(0,-8);
assertInstance(aa, Uint16Array);
assertEquals(0, aa.length);
assertEquals(0, aa.byteLength);
assertEquals(2, aa.BYTES_PER_ELEMENT);
assertSame(a.buffer, aa.buffer);
assertThrows(function(){ a.subarray.call({}, 0) });
assertThrows(function(){ a.subarray.call([], 0) });
assertThrows(function(){ a.subarray.call(a) });
// Call constructors directly as functions, and through .call and .apply
b = ArrayBuffer(100)
a = Int8Array(b, 5, 77)
assertInstance(b, ArrayBuffer)
assertInstance(a, Int8Array)
assertSame(b, a.buffer)
assertEquals(5, a.byteOffset)
assertEquals(77, a.byteLength)
b = ArrayBuffer.call(null, 10)
a = Uint16Array.call(null, b, 2, 4)
assertInstance(b, ArrayBuffer)
assertInstance(a, Uint16Array)
assertSame(b, a.buffer)
assertEquals(2, a.byteOffset)
assertEquals(8, a.byteLength)
b = ArrayBuffer.apply(null, [1000])
a = Float32Array.apply(null, [b, 128, 1])
assertInstance(b, ArrayBuffer)
assertInstance(a, Float32Array)
assertSame(b, a.buffer)
assertEquals(128, a.byteOffset)
assertEquals(4, a.byteLength)
// Test array.set in different combinations.
function assertArrayPrefix(expected, array) {
for (var i = 0; i < expected.length; ++i) {
assertEquals(expected[i], array[i]);
}
}
var a11 = new Int16Array([1, 2, 3, 4, 0, -1])
var a12 = new Uint16Array(15)
a12.set(a11, 3)
assertArrayPrefix([0, 0, 0, 1, 2, 3, 4, 0, 0xffff, 0, 0], a12)
assertThrows(function(){ a11.set(a12) })
var a21 = [1, undefined, 10, NaN, 0, -1, {valueOf: function() {return 3}}]
var a22 = new Int32Array(12)
a22.set(a21, 2)
assertArrayPrefix([0, 0, 1, 0, 10, 0, 0, -1, 3, 0], a22)
var a31 = new Float32Array([2, 4, 6, 8, 11, NaN, 1/0, -3])
var a32 = a31.subarray(2, 6)
a31.set(a32, 4)
assertArrayPrefix([2, 4, 6, 8, 6, 8, 11, NaN], a31)
assertArrayPrefix([6, 8, 6, 8], a32)
var a4 = new Uint8ClampedArray([3,2,5,6])
a4.set(a4)
assertArrayPrefix([3, 2, 5, 6], a4)
// Cases with overlapping backing store but different element sizes.
var b = new ArrayBuffer(4)
var a5 = new Int16Array(b)
var a50 = new Int8Array(b)
var a51 = new Int8Array(b, 0, 2)
var a52 = new Int8Array(b, 1, 2)
var a53 = new Int8Array(b, 2, 2)
a5.set([0x5050, 0x0a0a])
assertArrayPrefix([0x50, 0x50, 0x0a, 0x0a], a50)
assertArrayPrefix([0x50, 0x50], a51)
assertArrayPrefix([0x50, 0x0a], a52)
assertArrayPrefix([0x0a, 0x0a], a53)
a50.set([0x50, 0x50, 0x0a, 0x0a])
a51.set(a5)
assertArrayPrefix([0x50, 0x0a, 0x0a, 0x0a], a50)
a50.set([0x50, 0x50, 0x0a, 0x0a])
a52.set(a5)
assertArrayPrefix([0x50, 0x50, 0x0a, 0x0a], a50)
a50.set([0x50, 0x50, 0x0a, 0x0a])
a53.set(a5)
assertArrayPrefix([0x50, 0x50, 0x50, 0x0a], a50)
a50.set([0x50, 0x51, 0x0a, 0x0b])
a5.set(a51)
assertArrayPrefix([0x0050, 0x0051], a5)
a50.set([0x50, 0x51, 0x0a, 0x0b])
a5.set(a52)
assertArrayPrefix([0x0051, 0x000a], a5)
a50.set([0x50, 0x51, 0x0a, 0x0b])
a5.set(a53)
assertArrayPrefix([0x000a, 0x000b], a5)
// Mixed types of same size.
var a61 = new Float32Array([1.2, 12.3])
var a62 = new Int32Array(2)
a62.set(a61)
assertArrayPrefix([1, 12], a62)
a61.set(a62)
assertArrayPrefix([1, 12], a61)
// Invalid source
assertThrows(function() { a.set(0) })
assertThrows(function() { a.set({}) })
// Test arraybuffer.slice
var a0 = new Int8Array([1, 2, 3, 4, 5, 6])
var b0 = a0.buffer
var b1 = b0.slice(0)
assertEquals(b0.byteLength, b1.byteLength)
assertArrayPrefix([1, 2, 3, 4, 5, 6], Int8Array(b1))
var b2 = b0.slice(3)
assertEquals(b0.byteLength - 3, b2.byteLength)
assertArrayPrefix([4, 5, 6], Int8Array(b2))
var b3 = b0.slice(2, 4)
assertEquals(2, b3.byteLength)
assertArrayPrefix([3, 4], Int8Array(b3))
function goo(a, i) {
return a[i];
}
function boo(a, i, v) {
return a[i] = v;
}
function do_tagged_index_external_array_test(constructor) {
var t_array = new constructor([1, 2, 3, 4, 5, 6]);
assertEquals(1, goo(t_array, 0));
assertEquals(1, goo(t_array, 0));
boo(t_array, 0, 13);
assertEquals(13, goo(t_array, 0));
%OptimizeFunctionOnNextCall(goo);
%OptimizeFunctionOnNextCall(boo);
boo(t_array, 0, 15);
assertEquals(15, goo(t_array, 0));
%ClearFunctionTypeFeedback(goo);
%ClearFunctionTypeFeedback(boo);
}
do_tagged_index_external_array_test(Int8Array);
do_tagged_index_external_array_test(Uint8Array);
do_tagged_index_external_array_test(Int16Array);
do_tagged_index_external_array_test(Uint16Array);
do_tagged_index_external_array_test(Int32Array);
do_tagged_index_external_array_test(Uint32Array);
do_tagged_index_external_array_test(Float32Array);
do_tagged_index_external_array_test(Float64Array);
var built_in_array = new Array(1, 2, 3, 4, 5, 6);
assertEquals(1, goo(built_in_array, 0));
assertEquals(1, goo(built_in_array, 0));
%OptimizeFunctionOnNextCall(goo);
%OptimizeFunctionOnNextCall(boo);
boo(built_in_array, 0, 11);
assertEquals(11, goo(built_in_array, 0));
%ClearFunctionTypeFeedback(goo);
%ClearFunctionTypeFeedback(boo);
built_in_array = new Array(1.5, 2, 3, 4, 5, 6);
assertEquals(1.5, goo(built_in_array, 0));
assertEquals(1.5, goo(built_in_array, 0));
%OptimizeFunctionOnNextCall(goo);
%OptimizeFunctionOnNextCall(boo);
boo(built_in_array, 0, 2.5);
assertEquals(2.5, goo(built_in_array, 0));
%ClearFunctionTypeFeedback(goo);
%ClearFunctionTypeFeedback(boo);
......@@ -27,12 +27,15 @@
// Flags: --allow-natives-syntax
var pixels = new Uint8ClampedArray(8);
var pixels = new Uint8ClampedArray(11);
function f() {
for (var i = 0; i < 8; i++) {
pixels[i] = (i * 1.1);
}
pixels[8] = 255.5;
pixels[9] = NaN;
pixels[10] = -0.5;
return pixels[1] + pixels[6];
}
......@@ -42,3 +45,6 @@ assertEquals(6, pixels[5]);
%OptimizeFunctionOnNextCall(f);
f();
assertEquals(6, pixels[5]);
assertEquals(255, pixels[8]);
assertEquals(0, pixels[9]);
assertEquals(0, pixels[10]);
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