Revision: 8077
Author: [email protected]
Date: Thu May 26 05:07:22 2011
Log: Prevent deopt on double value assignment to typed arrays
Implement truncation of double and tagged values when assigning to an
element of a typed arrays in order to avoid depots.
BUG=1313
TEST=test/mjsunit/external-array.js
Review URL: http://codereview.chromium.org/6961019
http://code.google.com/p/v8/source/detail?r=8077
Modified:
/branches/bleeding_edge/src/arm/lithium-arm.cc
/branches/bleeding_edge/src/arm/lithium-arm.h
/branches/bleeding_edge/src/arm/stub-cache-arm.cc
/branches/bleeding_edge/src/hydrogen-instructions.h
/branches/bleeding_edge/src/hydrogen.cc
/branches/bleeding_edge/src/ia32/lithium-ia32.cc
/branches/bleeding_edge/src/ia32/lithium-ia32.h
/branches/bleeding_edge/src/x64/lithium-x64.cc
/branches/bleeding_edge/src/x64/lithium-x64.h
/branches/bleeding_edge/test/mjsunit/external-array.js
=======================================
--- /branches/bleeding_edge/src/arm/lithium-arm.cc Tue May 24 07:01:36 2011
+++ /branches/bleeding_edge/src/arm/lithium-arm.cc Thu May 26 05:07:22 2011
@@ -1765,6 +1765,31 @@
return AssignEnvironment(DefineAsRegister(result));
}
}
+
+
+LInstruction* LChunkBuilder::DoToInt32(HToInt32* instr) {
+ HValue* value = instr->value();
+ Representation input_rep = value->representation();
+ LOperand* reg = UseRegister(value);
+ if (input_rep.IsDouble()) {
+ LOperand* temp1 = TempRegister();
+ LOperand* temp2 = TempRegister();
+ LDoubleToI* res = new LDoubleToI(reg, temp1, temp2);
+ return AssignEnvironment(DefineAsRegister(res));
+ } else if (input_rep.IsInteger32()) {
+ // Canonicalization should already have removed the hydrogen
instruction in
+ // this case, since it is a noop.
+ UNREACHABLE();
+ return NULL;
+ } else {
+ ASSERT(input_rep.IsTagged());
+ LOperand* temp1 = TempRegister();
+ LOperand* temp2 = TempRegister();
+ LOperand* temp3 = FixedTemp(d3);
+ LTaggedToI* res = new LTaggedToI(reg, temp1, temp2, temp3);
+ return AssigneEnvironment(DefineSameAsFirst(res));
+ }
+}
LInstruction* LChunkBuilder::DoReturn(HReturn* instr) {
=======================================
--- /branches/bleeding_edge/src/arm/lithium-arm.h Tue May 17 00:22:01 2011
+++ /branches/bleeding_edge/src/arm/lithium-arm.h Thu May 26 05:07:22 2011
@@ -1639,7 +1639,7 @@
}
DECLARE_CONCRETE_INSTRUCTION(DoubleToI, "double-to-i")
- DECLARE_HYDROGEN_ACCESSOR(Change)
+ DECLARE_HYDROGEN_ACCESSOR(UnaryOperation)
bool truncating() { return hydrogen()->CanTruncateToInt32(); }
};
@@ -1659,7 +1659,7 @@
}
DECLARE_CONCRETE_INSTRUCTION(TaggedToI, "tagged-to-i")
- DECLARE_HYDROGEN_ACCESSOR(Change)
+ DECLARE_HYDROGEN_ACCESSOR(UnaryOperation)
bool truncating() { return hydrogen()->CanTruncateToInt32(); }
};
=======================================
--- /branches/bleeding_edge/src/arm/stub-cache-arm.cc Tue May 24 07:01:36
2011
+++ /branches/bleeding_edge/src/arm/stub-cache-arm.cc Thu May 26 05:07:22
2011
@@ -3926,27 +3926,11 @@
__ add(r5, r3, Operand(r4, LSL, 3));
__ vstr(d0, r5, 0);
} else {
- // Need to perform float-to-int conversion.
- // Test for NaN or infinity (both give zero).
- __ ldr(r6, FieldMemOperand(value, HeapNumber::kExponentOffset));
-
// Hoisted load. vldr requires offset to be a multiple of 4 so we
can
// not include -kHeapObjectTag into it.
__ sub(r5, value, Operand(kHeapObjectTag));
__ vldr(d0, r5, HeapNumber::kValueOffset);
-
- __ Sbfx(r6, r6, HeapNumber::kExponentShift,
HeapNumber::kExponentBits);
- // NaNs and Infinities have all-one exponents so they sign extend
to -1.
- __ cmp(r6, Operand(-1));
- __ mov(r5, Operand(0), LeaveCC, eq);
-
- // Not infinity or NaN simply convert to int.
- if (IsElementTypeSigned(array_type)) {
- __ vcvt_s32_f64(s0, d0, kDefaultRoundToZero, ne);
- } else {
- __ vcvt_u32_f64(s0, d0, kDefaultRoundToZero, ne);
- }
- __ vmov(r5, s0, ne);
+ __ EmitECMATruncate(r5, d0, s2, r6, r7, r9);
switch (array_type) {
case kExternalByteArray:
=======================================
--- /branches/bleeding_edge/src/hydrogen-instructions.h Tue May 24 07:01:36
2011
+++ /branches/bleeding_edge/src/hydrogen-instructions.h Thu May 26 05:07:22
2011
@@ -101,6 +101,7 @@
V(EnterInlined) \
V(ExternalArrayLength) \
V(FixedArrayLength) \
+ V(ToInt32) \
V(ForceRepresentation) \
V(FunctionLiteral) \
V(GetCachedArrayIndex) \
@@ -990,6 +991,14 @@
explicit HUnaryOperation(HValue* value) {
SetOperandAt(0, value);
}
+
+ static HUnaryOperation* cast(HValue* value) {
+ return reinterpret_cast<HUnaryOperation*>(value);
+ }
+
+ virtual bool CanTruncateToInt32() const {
+ return CheckFlag(kTruncatingToInt32);
+ }
HValue* value() { return OperandAt(0); }
virtual void PrintDataTo(StringStream* stream);
@@ -1054,8 +1063,6 @@
virtual Representation RequiredInputRepresentation(int index) const {
return from_;
}
-
- bool CanTruncateToInt32() const { return CheckFlag(kTruncatingToInt32); }
virtual void PrintDataTo(StringStream* stream);
@@ -1114,6 +1121,37 @@
};
+class HToInt32: public HUnaryOperation {
+ public:
+ explicit HToInt32(HValue* value)
+ : HUnaryOperation(value) {
+ set_representation(Representation::Integer32());
+ SetFlag(kUseGVN);
+ }
+
+ virtual Representation RequiredInputRepresentation(int index) const {
+ return Representation::None();
+ }
+
+ virtual bool CanTruncateToInt32() const {
+ return true;
+ }
+
+ virtual HValue* Canonicalize() {
+ if (value()->representation().IsInteger32()) {
+ return value();
+ } else {
+ return this;
+ }
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(ToInt32)
+
+ protected:
+ virtual bool DataEquals(HValue* other) { return true; }
+};
+
+
class HSimulate: public HInstruction {
public:
HSimulate(int ast_id, int pop_count)
=======================================
--- /branches/bleeding_edge/src/hydrogen.cc Thu May 26 04:22:29 2011
+++ /branches/bleeding_edge/src/hydrogen.cc Thu May 26 05:07:22 2011
@@ -3805,10 +3805,28 @@
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;
+ ExternalArrayType array_type = expr->external_array_type();
+ switch (array_type) {
+ case kExternalPixelArray: {
+ HClampToUint8* clamp = new(zone()) HClampToUint8(val);
+ AddInstruction(clamp);
+ val = clamp;
+ break;
+ }
+ case kExternalByteArray:
+ case kExternalUnsignedByteArray:
+ case kExternalShortArray:
+ case kExternalUnsignedShortArray:
+ case kExternalIntArray:
+ case kExternalUnsignedIntArray: {
+ HToInt32* floor_val = new(zone()) HToInt32(val);
+ AddInstruction(floor_val);
+ val = floor_val;
+ break;
+ }
+ case kExternalFloatArray:
+ case kExternalDoubleArray:
+ break;
}
return new(zone()) HStoreKeyedSpecializedArrayElement(
external_elements,
=======================================
--- /branches/bleeding_edge/src/ia32/lithium-ia32.cc Tue May 24 09:23:22
2011
+++ /branches/bleeding_edge/src/ia32/lithium-ia32.cc Thu May 26 05:07:22
2011
@@ -1684,8 +1684,9 @@
LOperand* value = UseRegister(instr->value());
bool needs_check = !instr->value()->type().IsSmi();
if (needs_check) {
+ bool truncating = instr->CanTruncateToInt32();
LOperand* xmm_temp =
- (instr->CanTruncateToInt32() && CpuFeatures::IsSupported(SSE3))
+ (truncating && CpuFeatures::IsSupported(SSE3))
? NULL
: FixedTemp(xmm1);
LTaggedToI* res = new LTaggedToI(value, xmm_temp);
@@ -1705,8 +1706,8 @@
return AssignPointerMap(Define(result, result_temp));
} else {
ASSERT(to.IsInteger32());
- bool needs_temp = instr->CanTruncateToInt32() &&
- !CpuFeatures::IsSupported(SSE3);
+ bool truncating = instr->CanTruncateToInt32();
+ bool needs_temp = truncating && !CpuFeatures::IsSupported(SSE3);
LOperand* value = needs_temp ?
UseTempRegister(instr->value()) : UseRegister(instr->value());
LOperand* temp = needs_temp ? TempRegister() : NULL;
@@ -1791,6 +1792,35 @@
return AssignEnvironment(DefineFixed(result, eax));
}
}
+
+
+LInstruction* LChunkBuilder::DoToInt32(HToInt32* instr) {
+ HValue* value = instr->value();
+ Representation input_rep = value->representation();
+ // Register allocator doesn't (yet) support allocation of double
+ // temps. Reserve xmm1 explicitly.
+ LOperand* xmm_temp =
+ CpuFeatures::IsSupported(SSE3)
+ ? NULL
+ : FixedTemp(xmm1);
+ LInstruction* result;
+ if (input_rep.IsDouble()) {
+ LOperand* reg = UseRegister(value);
+ // Register allocator doesn't (yet) support allocation of double
+ // temps. Reserve xmm1 explicitly.
+ result = DefineAsRegister(new LDoubleToI(reg, xmm_temp));
+ } else if (input_rep.IsInteger32()) {
+ // Canonicalization should already have removed the hydrogen
instruction in
+ // this case, since it is a noop.
+ UNREACHABLE();
+ return NULL;
+ } else {
+ ASSERT(input_rep.IsTagged());
+ LOperand* reg = UseRegister(value);
+ result = DefineSameAsFirst(new LTaggedToI(reg, xmm_temp));
+ }
+ return AssignEnvironment(result);
+}
LInstruction* LChunkBuilder::DoReturn(HReturn* instr) {
=======================================
--- /branches/bleeding_edge/src/ia32/lithium-ia32.h Wed May 18 02:19:14 2011
+++ /branches/bleeding_edge/src/ia32/lithium-ia32.h Thu May 26 05:07:22 2011
@@ -1691,7 +1691,7 @@
}
DECLARE_CONCRETE_INSTRUCTION(DoubleToI, "double-to-i")
- DECLARE_HYDROGEN_ACCESSOR(Change)
+ DECLARE_HYDROGEN_ACCESSOR(UnaryOperation)
bool truncating() { return hydrogen()->CanTruncateToInt32(); }
};
@@ -1706,7 +1706,7 @@
}
DECLARE_CONCRETE_INSTRUCTION(TaggedToI, "tagged-to-i")
- DECLARE_HYDROGEN_ACCESSOR(Change)
+ DECLARE_HYDROGEN_ACCESSOR(UnaryOperation)
bool truncating() { return hydrogen()->CanTruncateToInt32(); }
};
=======================================
--- /branches/bleeding_edge/src/x64/lithium-x64.cc Tue May 24 07:01:36 2011
+++ /branches/bleeding_edge/src/x64/lithium-x64.cc Thu May 26 05:07:22 2011
@@ -1655,8 +1655,8 @@
LOperand* value = UseRegister(instr->value());
bool needs_check = !instr->value()->type().IsSmi();
if (needs_check) {
- LOperand* xmm_temp = instr->CanTruncateToInt32() ? NULL
- : FixedTemp(xmm1);
+ bool truncating = instr->CanTruncateToInt32();
+ LOperand* xmm_temp = truncating ? NULL : FixedTemp(xmm1);
LTaggedToI* res = new LTaggedToI(value, xmm_temp);
return AssignEnvironment(DefineSameAsFirst(res));
} else {
@@ -1755,6 +1755,32 @@
return AssignEnvironment(DefineSameAsFirst(result));
}
}
+
+
+LInstruction* LChunkBuilder::DoToInt32(HToInt32* instr) {
+ HValue* value = instr->value();
+ Representation input_rep = value->representation();
+ LOperand* reg = UseRegister(value);
+ if (input_rep.IsDouble()) {
+ return AssignEnvironment(DefineAsRegister(new LDoubleToI(reg)));
+ } else if (input_rep.IsInteger32()) {
+ // Canonicalization should already have removed the hydrogen
instruction in
+ // this case, since it is a noop.
+ UNREACHABLE();
+ return NULL;
+ } else {
+ ASSERT(input_rep.IsTagged());
+ LOperand* reg = UseRegister(value);
+ // Register allocator doesn't (yet) support allocation of double
+ // temps. Reserve xmm1 explicitly.
+ LOperand* xmm_temp =
+ CpuFeatures::IsSupported(SSE3)
+ ? NULL
+ : FixedTemp(xmm1);
+ return AssignEnvironment(
+ DefineSameAsFirst(new LTaggedToI(reg, xmm_temp)));
+ }
+}
LInstruction* LChunkBuilder::DoReturn(HReturn* instr) {
=======================================
--- /branches/bleeding_edge/src/x64/lithium-x64.h Tue May 17 00:22:01 2011
+++ /branches/bleeding_edge/src/x64/lithium-x64.h Thu May 26 05:07:22 2011
@@ -1634,7 +1634,7 @@
}
DECLARE_CONCRETE_INSTRUCTION(DoubleToI, "double-to-i")
- DECLARE_HYDROGEN_ACCESSOR(Change)
+ DECLARE_HYDROGEN_ACCESSOR(UnaryOperation)
bool truncating() { return hydrogen()->CanTruncateToInt32(); }
};
@@ -1649,7 +1649,7 @@
}
DECLARE_CONCRETE_INSTRUCTION(TaggedToI, "tagged-to-i")
- DECLARE_HYDROGEN_ACCESSOR(Change)
+ DECLARE_HYDROGEN_ACCESSOR(UnaryOperation)
bool truncating() { return hydrogen()->CanTruncateToInt32(); }
};
=======================================
--- /branches/bleeding_edge/test/mjsunit/external-array.js Mon May 23
06:42:33 2011
+++ /branches/bleeding_edge/test/mjsunit/external-array.js Thu May 26
05:07:22 2011
@@ -87,8 +87,10 @@
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;
@@ -120,15 +122,27 @@
return sum;
}
-
-function test_store_middle_double(array, 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;
+ array[0] = 256.25 + zero(); // + forces double type feedback
return array[0];
}
@@ -141,6 +155,16 @@
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;
@@ -168,7 +192,6 @@
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;
@@ -180,9 +203,14 @@
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) {
--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev