Eagerly deoptimize on never-executed code-paths.

If type-feedback indicates that an expression was never executed in
the non-optimized code, we insert a forced deoptimization right away
to enable re-optimization if we ever hit this path.

With this change we still continue to build the graph. As a next step, we
should remove the dead code after the deoptimize.

I had to remove one assert about the optimization status in a test since
we now immediately deoptimize after exiting the loop that triggers OSR.

Also remove a restriction that control-flow from an inlined function in a
test context always reaches both true- and false-target.
Review URL: http://codereview.chromium.org/7105015

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@8140 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent d985af52
...@@ -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() {
...@@ -4306,21 +4305,22 @@ bool HGraphBuilder::TryInline(Call* expr) { ...@@ -4306,21 +4305,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()) {
...@@ -4787,6 +4787,10 @@ void HGraphBuilder::VisitSub(UnaryOperation* expr) { ...@@ -4787,6 +4787,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);
...@@ -4797,6 +4801,10 @@ void HGraphBuilder::VisitSub(UnaryOperation* expr) { ...@@ -4797,6 +4801,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());
} }
...@@ -5028,7 +5036,57 @@ HInstruction* HGraphBuilder::BuildBinaryOperation(BinaryOperation* expr, ...@@ -5028,7 +5036,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.
...@@ -5048,36 +5106,6 @@ HInstruction* HGraphBuilder::BuildBinaryOperation(BinaryOperation* expr, ...@@ -5048,36 +5106,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;
...@@ -5275,6 +5303,13 @@ void HGraphBuilder::VisitCompareOperation(CompareOperation* expr) { ...@@ -5275,6 +5303,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()));
...@@ -5282,7 +5317,6 @@ void HGraphBuilder::VisitCompareOperation(CompareOperation* expr) { ...@@ -5282,7 +5317,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);
} }
......
...@@ -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