Re-land r8140: Deoptimize on never-executed code-paths.

Original cl: http://codereview.chromium.org/7105015

I'm removing the test GlobalLoadICGC test that was introduced for testing
inlined global cell loads (in the classic backend) and has an invalid assumption
about the number of global objects referenced from a v8 context. We don't have
this feature with Crankshaft anymore.
Review URL: http://codereview.chromium.org/7112032

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@8185 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 0c1702b1
...@@ -808,6 +808,11 @@ LInstruction* LChunkBuilder::DoBlockEntry(HBlockEntry* instr) { ...@@ -808,6 +808,11 @@ LInstruction* LChunkBuilder::DoBlockEntry(HBlockEntry* instr) {
} }
LInstruction* LChunkBuilder::DoSoftDeoptimize(HSoftDeoptimize* instr) {
return AssignEnvironment(new LDeoptimize);
}
LInstruction* LChunkBuilder::DoDeoptimize(HDeoptimize* instr) { LInstruction* LChunkBuilder::DoDeoptimize(HDeoptimize* instr) {
return AssignEnvironment(new LDeoptimize); return AssignEnvironment(new LDeoptimize);
} }
......
...@@ -1083,6 +1083,16 @@ void HSimulate::PrintDataTo(StringStream* stream) { ...@@ -1083,6 +1083,16 @@ void HSimulate::PrintDataTo(StringStream* stream) {
} }
void HDeoptimize::PrintDataTo(StringStream* stream) {
if (OperandCount() == 0) return;
OperandAt(0)->PrintNameTo(stream);
for (int i = 1; i < OperandCount(); ++i) {
stream->Add(" ");
OperandAt(i)->PrintNameTo(stream);
}
}
void HEnterInlined::PrintDataTo(StringStream* stream) { void HEnterInlined::PrintDataTo(StringStream* stream) {
SmartPointer<char> name = function()->debug_name()->ToCString(); SmartPointer<char> name = function()->debug_name()->ToCString();
stream->Add("%s, id=%d", *name, function()->id()); stream->Add("%s, id=%d", *name, function()->id());
......
...@@ -146,6 +146,7 @@ class LChunkBuilder; ...@@ -146,6 +146,7 @@ class LChunkBuilder;
V(Shl) \ V(Shl) \
V(Shr) \ V(Shr) \
V(Simulate) \ V(Simulate) \
V(SoftDeoptimize) \
V(StackCheck) \ V(StackCheck) \
V(StoreContextSlot) \ V(StoreContextSlot) \
V(StoreGlobalCell) \ V(StoreGlobalCell) \
...@@ -847,6 +848,19 @@ class HBlockEntry: public HTemplateInstruction<0> { ...@@ -847,6 +848,19 @@ class HBlockEntry: public HTemplateInstruction<0> {
}; };
// We insert soft-deoptimize when we hit code with unknown typefeedback,
// so that we get a chance of re-optimizing with useful typefeedback.
// HSoftDeoptimize does not end a basic block as opposed to HDeoptimize.
class HSoftDeoptimize: public HTemplateInstruction<0> {
public:
virtual Representation RequiredInputRepresentation(int index) const {
return Representation::None();
}
DECLARE_CONCRETE_INSTRUCTION(SoftDeoptimize)
};
class HDeoptimize: public HControlInstruction { class HDeoptimize: public HControlInstruction {
public: public:
explicit HDeoptimize(int environment_length) explicit HDeoptimize(int environment_length)
...@@ -859,6 +873,7 @@ class HDeoptimize: public HControlInstruction { ...@@ -859,6 +873,7 @@ class HDeoptimize: public HControlInstruction {
virtual int OperandCount() { return values_.length(); } virtual int OperandCount() { return values_.length(); }
virtual HValue* OperandAt(int index) { return values_[index]; } virtual HValue* OperandAt(int index) { return values_[index]; }
virtual void PrintDataTo(StringStream* stream);
void AddEnvironmentValue(HValue* value) { void AddEnvironmentValue(HValue* value) {
values_.Add(NULL); values_.Add(NULL);
......
...@@ -68,8 +68,7 @@ HBasicBlock::HBasicBlock(HGraph* graph) ...@@ -68,8 +68,7 @@ HBasicBlock::HBasicBlock(HGraph* graph)
last_instruction_index_(-1), last_instruction_index_(-1),
deleted_phis_(4), deleted_phis_(4),
parent_loop_header_(NULL), parent_loop_header_(NULL),
is_inline_return_target_(false) { is_inline_return_target_(false) { }
}
void HBasicBlock::AttachLoopInformation() { void HBasicBlock::AttachLoopInformation() {
...@@ -4310,21 +4309,22 @@ bool HGraphBuilder::TryInline(Call* expr) { ...@@ -4310,21 +4309,22 @@ bool HGraphBuilder::TryInline(Call* expr) {
if (inlined_test_context() != NULL) { if (inlined_test_context() != NULL) {
HBasicBlock* if_true = inlined_test_context()->if_true(); HBasicBlock* if_true = inlined_test_context()->if_true();
HBasicBlock* if_false = inlined_test_context()->if_false(); HBasicBlock* if_false = inlined_test_context()->if_false();
if_true->SetJoinId(expr->id());
if_false->SetJoinId(expr->id());
ASSERT(ast_context() == inlined_test_context());
// Pop the return test context from the expression context stack. // Pop the return test context from the expression context stack.
ASSERT(ast_context() == inlined_test_context());
ClearInlinedTestContext(); ClearInlinedTestContext();
// Forward to the real test context. // Forward to the real test context.
HBasicBlock* true_target = TestContext::cast(ast_context())->if_true(); if (if_true->HasPredecessor()) {
HBasicBlock* false_target = TestContext::cast(ast_context())->if_false(); if_true->SetJoinId(expr->id());
if_true->Goto(true_target, false); HBasicBlock* true_target = TestContext::cast(ast_context())->if_true();
if_false->Goto(false_target, false); if_true->Goto(true_target, false);
}
// TODO(kmillikin): Come up with a better way to handle this. It is too if (if_false->HasPredecessor()) {
// subtle. NULL here indicates that the enclosing context has no control if_false->SetJoinId(expr->id());
// flow to handle. HBasicBlock* false_target = TestContext::cast(ast_context())->if_false();
if_false->Goto(false_target, false);
}
set_current_block(NULL); set_current_block(NULL);
} else if (function_return()->HasPredecessor()) { } else if (function_return()->HasPredecessor()) {
...@@ -4791,6 +4791,10 @@ void HGraphBuilder::VisitSub(UnaryOperation* expr) { ...@@ -4791,6 +4791,10 @@ void HGraphBuilder::VisitSub(UnaryOperation* expr) {
HValue* value = Pop(); HValue* value = Pop();
HInstruction* instr = new(zone()) HMul(value, graph_->GetConstantMinus1()); HInstruction* instr = new(zone()) HMul(value, graph_->GetConstantMinus1());
TypeInfo info = oracle()->UnaryType(expr); TypeInfo info = oracle()->UnaryType(expr);
if (info.IsUninitialized()) {
AddInstruction(new(zone()) HSoftDeoptimize);
info = TypeInfo::Unknown();
}
Representation rep = ToRepresentation(info); Representation rep = ToRepresentation(info);
TraceRepresentation(expr->op(), info, instr, rep); TraceRepresentation(expr->op(), info, instr, rep);
instr->AssumeRepresentation(rep); instr->AssumeRepresentation(rep);
...@@ -4801,6 +4805,10 @@ void HGraphBuilder::VisitSub(UnaryOperation* expr) { ...@@ -4801,6 +4805,10 @@ void HGraphBuilder::VisitSub(UnaryOperation* expr) {
void HGraphBuilder::VisitBitNot(UnaryOperation* expr) { void HGraphBuilder::VisitBitNot(UnaryOperation* expr) {
CHECK_ALIVE(VisitForValue(expr->expression())); CHECK_ALIVE(VisitForValue(expr->expression()));
HValue* value = Pop(); HValue* value = Pop();
TypeInfo info = oracle()->UnaryType(expr);
if (info.IsUninitialized()) {
AddInstruction(new(zone()) HSoftDeoptimize);
}
HInstruction* instr = new(zone()) HBitNot(value); HInstruction* instr = new(zone()) HBitNot(value);
ast_context()->ReturnInstruction(instr, expr->id()); ast_context()->ReturnInstruction(instr, expr->id());
} }
...@@ -5032,7 +5040,57 @@ HInstruction* HGraphBuilder::BuildBinaryOperation(BinaryOperation* expr, ...@@ -5032,7 +5040,57 @@ HInstruction* HGraphBuilder::BuildBinaryOperation(BinaryOperation* expr,
HValue* left, HValue* left,
HValue* right) { HValue* right) {
TypeInfo info = oracle()->BinaryType(expr); TypeInfo info = oracle()->BinaryType(expr);
HInstruction* instr = BuildBinaryOperation(expr->op(), left, right, info); if (info.IsUninitialized()) {
AddInstruction(new(zone()) HSoftDeoptimize);
info = TypeInfo::Unknown();
}
HInstruction* instr = NULL;
switch (expr->op()) {
case Token::ADD:
if (info.IsString()) {
AddInstruction(new(zone()) HCheckNonSmi(left));
AddInstruction(HCheckInstanceType::NewIsString(left));
AddInstruction(new(zone()) HCheckNonSmi(right));
AddInstruction(HCheckInstanceType::NewIsString(right));
instr = new(zone()) HStringAdd(left, right);
} else {
instr = new(zone()) HAdd(left, right);
}
break;
case Token::SUB:
instr = new(zone()) HSub(left, right);
break;
case Token::MUL:
instr = new(zone()) HMul(left, right);
break;
case Token::MOD:
instr = new(zone()) HMod(left, right);
break;
case Token::DIV:
instr = new(zone()) HDiv(left, right);
break;
case Token::BIT_XOR:
instr = new(zone()) HBitXor(left, right);
break;
case Token::BIT_AND:
instr = new(zone()) HBitAnd(left, right);
break;
case Token::BIT_OR:
instr = new(zone()) HBitOr(left, right);
break;
case Token::SAR:
instr = new(zone()) HSar(left, right);
break;
case Token::SHR:
instr = new(zone()) HShr(left, right);
break;
case Token::SHL:
instr = new(zone()) HShl(left, right);
break;
default:
UNREACHABLE();
}
// If we hit an uninitialized binary op stub we will get type info // If we hit an uninitialized binary op stub we will get type info
// for a smi operation. If one of the operands is a constant string // for a smi operation. If one of the operands is a constant string
// do not generate code assuming it is a smi operation. // do not generate code assuming it is a smi operation.
...@@ -5052,36 +5110,6 @@ HInstruction* HGraphBuilder::BuildBinaryOperation(BinaryOperation* expr, ...@@ -5052,36 +5110,6 @@ HInstruction* HGraphBuilder::BuildBinaryOperation(BinaryOperation* expr,
} }
HInstruction* HGraphBuilder::BuildBinaryOperation(
Token::Value op, HValue* left, HValue* right, TypeInfo info) {
switch (op) {
case Token::ADD:
if (info.IsString()) {
AddInstruction(new(zone()) HCheckNonSmi(left));
AddInstruction(HCheckInstanceType::NewIsString(left));
AddInstruction(new(zone()) HCheckNonSmi(right));
AddInstruction(HCheckInstanceType::NewIsString(right));
return new(zone()) HStringAdd(left, right);
} else {
return new(zone()) HAdd(left, right);
}
case Token::SUB: return new(zone()) HSub(left, right);
case Token::MUL: return new(zone()) HMul(left, right);
case Token::MOD: return new(zone()) HMod(left, right);
case Token::DIV: return new(zone()) HDiv(left, right);
case Token::BIT_XOR: return new(zone()) HBitXor(left, right);
case Token::BIT_AND: return new(zone()) HBitAnd(left, right);
case Token::BIT_OR: return new(zone()) HBitOr(left, right);
case Token::SAR: return new(zone()) HSar(left, right);
case Token::SHR: return new(zone()) HShr(left, right);
case Token::SHL: return new(zone()) HShl(left, right);
default:
UNREACHABLE();
return NULL;
}
}
// Check for the form (%_ClassOf(foo) === 'BarClass'). // Check for the form (%_ClassOf(foo) === 'BarClass').
static bool IsClassOfTest(CompareOperation* expr) { static bool IsClassOfTest(CompareOperation* expr) {
if (expr->op() != Token::EQ_STRICT) return false; if (expr->op() != Token::EQ_STRICT) return false;
...@@ -5279,6 +5307,13 @@ void HGraphBuilder::VisitCompareOperation(CompareOperation* expr) { ...@@ -5279,6 +5307,13 @@ void HGraphBuilder::VisitCompareOperation(CompareOperation* expr) {
return; return;
} }
TypeInfo type_info = oracle()->CompareType(expr);
// Check if this expression was ever executed according to type feedback.
if (type_info.IsUninitialized()) {
AddInstruction(new(zone()) HSoftDeoptimize);
type_info = TypeInfo::Unknown();
}
CHECK_ALIVE(VisitForValue(expr->left())); CHECK_ALIVE(VisitForValue(expr->left()));
CHECK_ALIVE(VisitForValue(expr->right())); CHECK_ALIVE(VisitForValue(expr->right()));
...@@ -5286,7 +5321,6 @@ void HGraphBuilder::VisitCompareOperation(CompareOperation* expr) { ...@@ -5286,7 +5321,6 @@ void HGraphBuilder::VisitCompareOperation(CompareOperation* expr) {
HValue* left = Pop(); HValue* left = Pop();
Token::Value op = expr->op(); Token::Value op = expr->op();
TypeInfo type_info = oracle()->CompareType(expr);
HInstruction* instr = NULL; HInstruction* instr = NULL;
if (op == Token::INSTANCEOF) { if (op == Token::INSTANCEOF) {
// Check to see if the rhs of the instanceof is a global function not // Check to see if the rhs of the instanceof is a global function not
......
...@@ -878,10 +878,6 @@ class HGraphBuilder: public AstVisitor { ...@@ -878,10 +878,6 @@ class HGraphBuilder: public AstVisitor {
HInstruction* BuildBinaryOperation(BinaryOperation* expr, HInstruction* BuildBinaryOperation(BinaryOperation* expr,
HValue* left, HValue* left,
HValue* right); HValue* right);
HInstruction* BuildBinaryOperation(Token::Value op,
HValue* left,
HValue* right,
TypeInfo info);
HInstruction* BuildIncrement(bool returns_original_input, HInstruction* BuildIncrement(bool returns_original_input,
CountOperation* expr); CountOperation* expr);
HLoadNamedField* BuildLoadNamedField(HValue* object, HLoadNamedField* BuildLoadNamedField(HValue* object,
......
...@@ -804,6 +804,11 @@ LInstruction* LChunkBuilder::DoBlockEntry(HBlockEntry* instr) { ...@@ -804,6 +804,11 @@ LInstruction* LChunkBuilder::DoBlockEntry(HBlockEntry* instr) {
} }
LInstruction* LChunkBuilder::DoSoftDeoptimize(HSoftDeoptimize* instr) {
return AssignEnvironment(new LDeoptimize);
}
LInstruction* LChunkBuilder::DoDeoptimize(HDeoptimize* instr) { LInstruction* LChunkBuilder::DoDeoptimize(HDeoptimize* instr) {
return AssignEnvironment(new LDeoptimize); return AssignEnvironment(new LDeoptimize);
} }
......
...@@ -224,8 +224,7 @@ TypeInfo TypeFeedbackOracle::CompareType(CompareOperation* expr) { ...@@ -224,8 +224,7 @@ TypeInfo TypeFeedbackOracle::CompareType(CompareOperation* expr) {
switch (state) { switch (state) {
case CompareIC::UNINITIALIZED: case CompareIC::UNINITIALIZED:
// Uninitialized means never executed. // Uninitialized means never executed.
// TODO(fschneider): Introduce a separate value for never-executed ICs. return TypeInfo::Uninitialized();
return unknown;
case CompareIC::SMIS: case CompareIC::SMIS:
return TypeInfo::Smi(); return TypeInfo::Smi();
case CompareIC::HEAP_NUMBERS: case CompareIC::HEAP_NUMBERS:
...@@ -286,8 +285,7 @@ TypeInfo TypeFeedbackOracle::BinaryType(BinaryOperation* expr) { ...@@ -286,8 +285,7 @@ TypeInfo TypeFeedbackOracle::BinaryType(BinaryOperation* expr) {
switch (type) { switch (type) {
case BinaryOpIC::UNINITIALIZED: case BinaryOpIC::UNINITIALIZED:
// Uninitialized means never executed. // Uninitialized means never executed.
// TODO(fschneider): Introduce a separate value for never-executed ICs return TypeInfo::Uninitialized();
return unknown;
case BinaryOpIC::SMI: case BinaryOpIC::SMI:
switch (result_type) { switch (result_type) {
case BinaryOpIC::UNINITIALIZED: case BinaryOpIC::UNINITIALIZED:
......
...@@ -803,6 +803,11 @@ LInstruction* LChunkBuilder::DoBlockEntry(HBlockEntry* instr) { ...@@ -803,6 +803,11 @@ LInstruction* LChunkBuilder::DoBlockEntry(HBlockEntry* instr) {
} }
LInstruction* LChunkBuilder::DoSoftDeoptimize(HSoftDeoptimize* instr) {
return AssignEnvironment(new LDeoptimize);
}
LInstruction* LChunkBuilder::DoDeoptimize(HDeoptimize* instr) { LInstruction* LChunkBuilder::DoDeoptimize(HDeoptimize* instr) {
return AssignEnvironment(new LDeoptimize); return AssignEnvironment(new LDeoptimize);
} }
......
...@@ -14038,48 +14038,6 @@ TEST(DontDeleteCellLoadICAPI) { ...@@ -14038,48 +14038,6 @@ TEST(DontDeleteCellLoadICAPI) {
} }
TEST(GlobalLoadICGC) {
const char* function_code =
"function readCell() { while (true) { return cell; } }";
// Check inline load code for a don't delete cell is cleared during
// GC.
{
v8::HandleScope scope;
LocalContext context;
CompileRun("var cell = \"value\";");
ExpectBoolean("delete cell", false);
CompileRun(function_code);
ExpectString("readCell()", "value");
ExpectString("readCell()", "value");
}
{
v8::HandleScope scope;
LocalContext context2;
// Hold the code object in the second context.
CompileRun(function_code);
CheckSurvivingGlobalObjectsCount(1);
}
// Check inline load code for a deletable cell is cleared during GC.
{
v8::HandleScope scope;
LocalContext context;
CompileRun("cell = \"value\";");
CompileRun(function_code);
ExpectString("readCell()", "value");
ExpectString("readCell()", "value");
}
{
v8::HandleScope scope;
LocalContext context2;
// Hold the code object in the second context.
CompileRun(function_code);
CheckSurvivingGlobalObjectsCount(1);
}
}
TEST(RegExp) { TEST(RegExp) {
v8::HandleScope scope; v8::HandleScope scope;
LocalContext context; LocalContext context;
......
...@@ -55,8 +55,6 @@ function h() { ...@@ -55,8 +55,6 @@ function h() {
while (%GetOptimizationStatus(h) == 2) { while (%GetOptimizationStatus(h) == 2) {
for (var j = 0; j < 100; j++) g(); for (var j = 0; j < 100; j++) g();
} }
assertTrue(%GetOptimizationStatus(h) == 1 ||
%GetOptimizationStatus(h) == 3);
g(); g();
} }
} }
......
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