Commit 0eca2b4f authored by whesse@chromium.org's avatar whesse@chromium.org

Fix error in postfix ++ in Crankshaft.

Add HForceRepresentation, to represent the implicit ToNumber applied to the input of a count operation.

BUG=v8:1389

TEST=

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

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@7913 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent e3fd7c45
...@@ -1643,6 +1643,14 @@ LInstruction* LChunkBuilder::DoThrow(HThrow* instr) { ...@@ -1643,6 +1643,14 @@ LInstruction* LChunkBuilder::DoThrow(HThrow* instr) {
} }
LInstruction* LChunkBuilder::DoForceRepresentation(HForceRepresentation* bad) {
// All HForceRepresentation instructions should be eliminated in the
// representation change phase of Hydrogen.
UNREACHABLE();
return NULL;
}
LInstruction* LChunkBuilder::DoChange(HChange* instr) { LInstruction* LChunkBuilder::DoChange(HChange* instr) {
Representation from = instr->from(); Representation from = instr->from();
Representation to = instr->to(); Representation to = instr->to();
......
...@@ -1638,6 +1638,13 @@ HValue* HChange::EnsureAndPropagateNotMinusZero(BitVector* visited) { ...@@ -1638,6 +1638,13 @@ HValue* HChange::EnsureAndPropagateNotMinusZero(BitVector* visited) {
} }
HValue* HForceRepresentation::EnsureAndPropagateNotMinusZero(
BitVector* visited) {
visited->Add(id());
return value();
}
HValue* HMod::EnsureAndPropagateNotMinusZero(BitVector* visited) { HValue* HMod::EnsureAndPropagateNotMinusZero(BitVector* visited) {
visited->Add(id()); visited->Add(id());
if (range() == NULL || range()->CanBeMinusZero()) { if (range() == NULL || range()->CanBeMinusZero()) {
......
...@@ -101,6 +101,7 @@ class LChunkBuilder; ...@@ -101,6 +101,7 @@ class LChunkBuilder;
V(EnterInlined) \ V(EnterInlined) \
V(ExternalArrayLength) \ V(ExternalArrayLength) \
V(FixedArrayLength) \ V(FixedArrayLength) \
V(ForceRepresentation) \
V(FunctionLiteral) \ V(FunctionLiteral) \
V(GetCachedArrayIndex) \ V(GetCachedArrayIndex) \
V(GlobalObject) \ V(GlobalObject) \
...@@ -1009,6 +1010,25 @@ class HThrow: public HUnaryOperation { ...@@ -1009,6 +1010,25 @@ class HThrow: public HUnaryOperation {
}; };
class HForceRepresentation: public HTemplateInstruction<1> {
public:
HForceRepresentation(HValue* value, Representation required_representation) {
SetOperandAt(0, value);
set_representation(required_representation);
}
HValue* value() { return OperandAt(0); }
virtual HValue* EnsureAndPropagateNotMinusZero(BitVector* visited);
virtual Representation RequiredInputRepresentation(int index) const {
return representation(); // Same as the output representation.
}
DECLARE_CONCRETE_INSTRUCTION(ForceRepresentation)
};
class HChange: public HUnaryOperation { class HChange: public HUnaryOperation {
public: public:
HChange(HValue* value, HChange(HValue* value,
......
...@@ -1803,6 +1803,12 @@ void HGraph::InsertRepresentationChangesForValue(HValue* value) { ...@@ -1803,6 +1803,12 @@ void HGraph::InsertRepresentationChangesForValue(HValue* value) {
ASSERT(value->IsConstant()); ASSERT(value->IsConstant());
value->DeleteAndReplaceWith(NULL); value->DeleteAndReplaceWith(NULL);
} }
// The only purpose of a HForceRepresentation is to represent the value
// after the (possible) HChange instruction. We make it disappear.
if (value->IsForceRepresentation()) {
value->DeleteAndReplaceWith(HForceRepresentation::cast(value)->value());
}
} }
...@@ -4699,20 +4705,35 @@ void HGraphBuilder::VisitNot(UnaryOperation* expr) { ...@@ -4699,20 +4705,35 @@ void HGraphBuilder::VisitNot(UnaryOperation* expr) {
} }
HInstruction* HGraphBuilder::BuildIncrement(HValue* value, HInstruction* HGraphBuilder::BuildIncrement(bool returns_original_input,
bool increment,
CountOperation* expr) { CountOperation* expr) {
HConstant* delta = increment // The input to the count operation is on top of the expression stack.
? graph_->GetConstant1()
: graph_->GetConstantMinus1();
HInstruction* instr = new(zone()) HAdd(value, delta);
TypeInfo info = oracle()->IncrementType(expr); TypeInfo info = oracle()->IncrementType(expr);
Representation rep = ToRepresentation(info); Representation rep = ToRepresentation(info);
if (rep.IsTagged()) { if (rep.IsTagged()) {
rep = Representation::Integer32(); rep = Representation::Integer32();
} }
if (returns_original_input) {
// We need an explicit HValue representing ToNumber(input). The
// actual HChange instruction we need is (sometimes) added in a later
// phase, so it is not available now to be used as an input to HAdd and
// as the return value.
HInstruction* number_input = new(zone()) HForceRepresentation(Pop(), rep);
AddInstruction(number_input);
Push(number_input);
}
// The addition has no side effects, so we do not need
// to simulate the expression stack after this instruction.
// Any later failures deopt to the load of the input or earlier.
HConstant* delta = (expr->op() == Token::INC)
? graph_->GetConstant1()
: graph_->GetConstantMinus1();
HInstruction* instr = new(zone()) HAdd(Top(), delta);
TraceRepresentation(expr->op(), info, instr, rep); TraceRepresentation(expr->op(), info, instr, rep);
instr->AssumeRepresentation(rep); instr->AssumeRepresentation(rep);
AddInstruction(instr);
return instr; return instr;
} }
...@@ -4725,18 +4746,25 @@ void HGraphBuilder::VisitCountOperation(CountOperation* expr) { ...@@ -4725,18 +4746,25 @@ void HGraphBuilder::VisitCountOperation(CountOperation* expr) {
VariableProxy* proxy = target->AsVariableProxy(); VariableProxy* proxy = target->AsVariableProxy();
Variable* var = proxy->AsVariable(); Variable* var = proxy->AsVariable();
Property* prop = target->AsProperty(); Property* prop = target->AsProperty();
ASSERT(var == NULL || prop == NULL); if (var == NULL && prop == NULL) {
bool inc = expr->op() == Token::INC; return Bailout("invalid lhs in count operation");
}
// Match the full code generator stack by simulating an extra stack
// element for postfix operations in a non-effect context. The return
// value is ToNumber(input).
bool returns_original_input =
expr->is_postfix() && !ast_context()->IsEffect();
HValue* input = NULL; // ToNumber(original_input).
HValue* after = NULL; // The result after incrementing or decrementing.
if (var != NULL) { if (var != NULL) {
// Argument of the count operation is a variable, not a property.
ASSERT(prop == NULL);
CHECK_ALIVE(VisitForValue(target)); CHECK_ALIVE(VisitForValue(target));
// Match the full code generator stack by simulating an extra stack after = BuildIncrement(returns_original_input, expr);
// element for postfix operations in a non-effect context. input = returns_original_input ? Top() : Pop();
bool has_extra = expr->is_postfix() && !ast_context()->IsEffect();
HValue* before = has_extra ? Top() : Pop();
HInstruction* after = BuildIncrement(before, inc, expr);
AddInstruction(after);
Push(after); Push(after);
if (var->is_global()) { if (var->is_global()) {
...@@ -4756,19 +4784,15 @@ void HGraphBuilder::VisitCountOperation(CountOperation* expr) { ...@@ -4756,19 +4784,15 @@ void HGraphBuilder::VisitCountOperation(CountOperation* expr) {
} else { } else {
return Bailout("lookup variable in count operation"); return Bailout("lookup variable in count operation");
} }
Drop(has_extra ? 2 : 1);
ast_context()->ReturnValue(expr->is_postfix() ? before : after);
} else if (prop != NULL) { } else {
// Argument of the count operation is a property.
ASSERT(prop != NULL);
prop->RecordTypeFeedback(oracle()); prop->RecordTypeFeedback(oracle());
if (prop->key()->IsPropertyName()) { if (prop->key()->IsPropertyName()) {
// Named property. // Named property.
if (returns_original_input) Push(graph_->GetConstantUndefined());
// Match the full code generator stack by simulating an extra stack
// element for postfix operations in a non-effect context.
bool has_extra = expr->is_postfix() && !ast_context()->IsEffect();
if (has_extra) Push(graph_->GetConstantUndefined());
CHECK_ALIVE(VisitForValue(prop->obj())); CHECK_ALIVE(VisitForValue(prop->obj()));
HValue* obj = Top(); HValue* obj = Top();
...@@ -4784,11 +4808,8 @@ void HGraphBuilder::VisitCountOperation(CountOperation* expr) { ...@@ -4784,11 +4808,8 @@ void HGraphBuilder::VisitCountOperation(CountOperation* expr) {
PushAndAdd(load); PushAndAdd(load);
if (load->HasSideEffects()) AddSimulate(expr->CountId()); if (load->HasSideEffects()) AddSimulate(expr->CountId());
HValue* before = Pop(); after = BuildIncrement(returns_original_input, expr);
// There is no deoptimization to after the increment, so we don't need input = Pop();
// to simulate the expression stack after this instruction.
HInstruction* after = BuildIncrement(before, inc, expr);
AddInstruction(after);
HInstruction* store = BuildStoreNamed(obj, after, prop); HInstruction* store = BuildStoreNamed(obj, after, prop);
AddInstruction(store); AddInstruction(store);
...@@ -4797,19 +4818,12 @@ void HGraphBuilder::VisitCountOperation(CountOperation* expr) { ...@@ -4797,19 +4818,12 @@ void HGraphBuilder::VisitCountOperation(CountOperation* expr) {
// of the operation, and the placeholder with the original value if // of the operation, and the placeholder with the original value if
// necessary. // necessary.
environment()->SetExpressionStackAt(0, after); environment()->SetExpressionStackAt(0, after);
if (has_extra) environment()->SetExpressionStackAt(1, before); if (returns_original_input) environment()->SetExpressionStackAt(1, input);
if (store->HasSideEffects()) AddSimulate(expr->AssignmentId()); if (store->HasSideEffects()) AddSimulate(expr->AssignmentId());
Drop(has_extra ? 2 : 1);
ast_context()->ReturnValue(expr->is_postfix() ? before : after);
} else { } else {
// Keyed property. // Keyed property.
if (returns_original_input) Push(graph_->GetConstantUndefined());
// Match the full code generator stack by simulate an extra stack element
// for postfix operations in a non-effect context.
bool has_extra = expr->is_postfix() && !ast_context()->IsEffect();
if (has_extra) Push(graph_->GetConstantUndefined());
CHECK_ALIVE(VisitForValue(prop->obj())); CHECK_ALIVE(VisitForValue(prop->obj()));
CHECK_ALIVE(VisitForValue(prop->key())); CHECK_ALIVE(VisitForValue(prop->key()));
...@@ -4820,11 +4834,8 @@ void HGraphBuilder::VisitCountOperation(CountOperation* expr) { ...@@ -4820,11 +4834,8 @@ void HGraphBuilder::VisitCountOperation(CountOperation* expr) {
PushAndAdd(load); PushAndAdd(load);
if (load->HasSideEffects()) AddSimulate(expr->CountId()); if (load->HasSideEffects()) AddSimulate(expr->CountId());
HValue* before = Pop(); after = BuildIncrement(returns_original_input, expr);
// There is no deoptimization to after the increment, so we don't need input = Pop();
// to simulate the expression stack after this instruction.
HInstruction* after = BuildIncrement(before, inc, expr);
AddInstruction(after);
expr->RecordTypeFeedback(oracle()); expr->RecordTypeFeedback(oracle());
HInstruction* store = BuildStoreKeyed(obj, key, after, expr); HInstruction* store = BuildStoreKeyed(obj, key, after, expr);
...@@ -4835,16 +4846,13 @@ void HGraphBuilder::VisitCountOperation(CountOperation* expr) { ...@@ -4835,16 +4846,13 @@ void HGraphBuilder::VisitCountOperation(CountOperation* expr) {
// original value if necessary. // original value if necessary.
Drop(1); Drop(1);
environment()->SetExpressionStackAt(0, after); environment()->SetExpressionStackAt(0, after);
if (has_extra) environment()->SetExpressionStackAt(1, before); if (returns_original_input) environment()->SetExpressionStackAt(1, input);
if (store->HasSideEffects()) AddSimulate(expr->AssignmentId()); if (store->HasSideEffects()) AddSimulate(expr->AssignmentId());
Drop(has_extra ? 2 : 1);
ast_context()->ReturnValue(expr->is_postfix() ? before : after);
} }
} else {
return Bailout("invalid lhs in count operation");
} }
Drop(returns_original_input ? 2 : 1);
ast_context()->ReturnValue(expr->is_postfix() ? input : after);
} }
......
...@@ -888,8 +888,7 @@ class HGraphBuilder: public AstVisitor { ...@@ -888,8 +888,7 @@ class HGraphBuilder: public AstVisitor {
HValue* left, HValue* left,
HValue* right, HValue* right,
TypeInfo info); TypeInfo info);
HInstruction* BuildIncrement(HValue* value, HInstruction* BuildIncrement(bool returns_original_input,
bool increment,
CountOperation* expr); CountOperation* expr);
HLoadNamedField* BuildLoadNamedField(HValue* object, HLoadNamedField* BuildLoadNamedField(HValue* object,
Property* expr, Property* expr,
......
...@@ -1677,6 +1677,14 @@ LInstruction* LChunkBuilder::DoThrow(HThrow* instr) { ...@@ -1677,6 +1677,14 @@ LInstruction* LChunkBuilder::DoThrow(HThrow* instr) {
} }
LInstruction* LChunkBuilder::DoForceRepresentation(HForceRepresentation* bad) {
// All HForceRepresentation instructions should be eliminated in the
// representation change phase of Hydrogen.
UNREACHABLE();
return NULL;
}
LInstruction* LChunkBuilder::DoChange(HChange* instr) { LInstruction* LChunkBuilder::DoChange(HChange* instr) {
Representation from = instr->from(); Representation from = instr->from();
Representation to = instr->to(); Representation to = instr->to();
......
...@@ -1648,6 +1648,14 @@ LInstruction* LChunkBuilder::DoThrow(HThrow* instr) { ...@@ -1648,6 +1648,14 @@ LInstruction* LChunkBuilder::DoThrow(HThrow* instr) {
} }
LInstruction* LChunkBuilder::DoForceRepresentation(HForceRepresentation* bad) {
// All HForceRepresentation instructions should be eliminated in the
// representation change phase of Hydrogen.
UNREACHABLE();
return NULL;
}
LInstruction* LChunkBuilder::DoChange(HChange* instr) { LInstruction* LChunkBuilder::DoChange(HChange* instr) {
Representation from = instr->from(); Representation from = instr->from();
Representation to = instr->to(); Representation to = instr->to();
......
// Copyright 2011 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.
// Test optimized implementation of postfix ++ on undefined input.
// See http://code.google.com/p/v8/issues/detail?id=1389
for (var i=0; i<4; i++) {
(function () {
(function () {
(function () {
var x;
y = x++;
})();
})();
})();
}
assertEquals(NaN, y);
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