Push version 2.0.0 to trunk.
Added support for VFP on ARM.
Added TryCatch::ReThrow method to the API.
Reduced the size of snapshots and improved the snapshot load time.
Improved heap profiler support.
64-bit version now supported on Windows.
Fixed a number of debugger issues.
Fixed bugs.
git-svn-id: http://v8.googlecode.com/svn/trunk@3333 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
diff --git a/src/compiler.cc b/src/compiler.cc
index d85f104..4e80a24 100644
--- a/src/compiler.cc
+++ b/src/compiler.cc
@@ -48,23 +48,24 @@
CodeGenSelector()
: has_supported_syntax_(true),
- location_(Location::Uninitialized()) {
+ context_(Expression::kUninitialized) {
}
CodeGenTag Select(FunctionLiteral* fun);
private:
+ // Visit an expression in a given expression context.
+ void ProcessExpression(Expression* expr, Expression::Context context) {
+ Expression::Context saved = context_;
+ context_ = context;
+ Visit(expr);
+ expr->set_context(context);
+ context_ = saved;
+ }
+
void VisitDeclarations(ZoneList<Declaration*>* decls);
void VisitStatements(ZoneList<Statement*>* stmts);
- // Visit an expression in effect context with a desired location of
- // nowhere.
- void VisitAsEffect(Expression* expr);
-
- // Visit an expression in value context with a desired location of
- // temporary.
- void VisitAsValue(Expression* expr);
-
// AST node visit functions.
#define DECLARE_VISIT(type) virtual void Visit##type(type* node);
AST_NODE_LIST(DECLARE_VISIT)
@@ -72,8 +73,8 @@
bool has_supported_syntax_;
- // The desired location of the currently visited expression.
- Location location_;
+ // The desired expression context of the currently visited expression.
+ Expression::Context context_;
DISALLOW_COPY_AND_ASSIGN(CodeGenSelector);
};
@@ -82,7 +83,8 @@
static Handle<Code> MakeCode(FunctionLiteral* literal,
Handle<Script> script,
Handle<Context> context,
- bool is_eval) {
+ bool is_eval,
+ Handle<SharedFunctionInfo> shared) {
ASSERT(literal != NULL);
// Rewrite the AST by introducing .result assignments where needed.
@@ -119,12 +121,21 @@
// Generate code and return it.
if (FLAG_fast_compiler) {
- CodeGenSelector selector;
- CodeGenSelector::CodeGenTag code_gen = selector.Select(literal);
- if (code_gen == CodeGenSelector::FAST) {
- return FastCodeGenerator::MakeCode(literal, script, is_eval);
+ // If there is no shared function info, try the fast code
+ // generator for code in the global scope. Otherwise obey the
+ // explicit hint in the shared function info.
+ if (shared.is_null() && !literal->scope()->is_global_scope()) {
+ if (FLAG_trace_bailout) PrintF("Non-global scope\n");
+ } else if (!shared.is_null() && !shared->try_fast_codegen()) {
+ if (FLAG_trace_bailout) PrintF("No hint to try fast\n");
+ } else {
+ CodeGenSelector selector;
+ CodeGenSelector::CodeGenTag code_gen = selector.Select(literal);
+ if (code_gen == CodeGenSelector::FAST) {
+ return FastCodeGenerator::MakeCode(literal, script, is_eval);
+ }
+ ASSERT(code_gen == CodeGenSelector::NORMAL);
}
- ASSERT(code_gen == CodeGenSelector::NORMAL);
}
return CodeGenerator::MakeCode(literal, script, is_eval);
}
@@ -166,7 +177,8 @@
if (is_eval) {
JavaScriptFrameIterator it;
script->set_eval_from_function(it.frame()->function());
- int offset = it.frame()->pc() - it.frame()->code()->instruction_start();
+ int offset = static_cast<int>(
+ it.frame()->pc() - it.frame()->code()->instruction_start());
script->set_eval_from_instructions_offset(Smi::FromInt(offset));
}
}
@@ -209,7 +221,8 @@
HistogramTimerScope timer(rate);
// Compile the code.
- Handle<Code> code = MakeCode(lit, script, context, is_eval);
+ Handle<Code> code = MakeCode(lit, script, context, is_eval,
+ Handle<SharedFunctionInfo>::null());
// Check for stack-overflow exceptions.
if (code.is_null()) {
@@ -246,7 +259,7 @@
code);
ASSERT_EQ(RelocInfo::kNoPosition, lit->function_token_position());
- CodeGenerator::SetFunctionInfo(fun, lit, true, script);
+ Compiler::SetFunctionInfo(fun, lit, true, script);
// Hint to the runtime system used when allocating space for initial
// property space by setting the expected number of properties for
@@ -410,7 +423,8 @@
HistogramTimerScope timer(&Counters::compile_lazy);
// Compile the code.
- Handle<Code> code = MakeCode(lit, script, Handle<Context>::null(), false);
+ Handle<Code> code = MakeCode(lit, script, Handle<Context>::null(), false,
+ shared);
// Check for stack-overflow exception.
if (code.is_null()) {
@@ -461,18 +475,137 @@
}
+Handle<JSFunction> Compiler::BuildBoilerplate(FunctionLiteral* literal,
+ Handle<Script> script,
+ AstVisitor* caller) {
+#ifdef DEBUG
+ // We should not try to compile the same function literal more than
+ // once.
+ literal->mark_as_compiled();
+#endif
+
+ // Determine if the function can be lazily compiled. This is
+ // necessary to allow some of our builtin JS files to be lazily
+ // compiled. These builtins cannot be handled lazily by the parser,
+ // since we have to know if a function uses the special natives
+ // syntax, which is something the parser records.
+ bool allow_lazy = literal->AllowsLazyCompilation();
+
+ // Generate code
+ Handle<Code> code;
+ if (FLAG_lazy && allow_lazy) {
+ code = ComputeLazyCompile(literal->num_parameters());
+ } else {
+ // The bodies of function literals have not yet been visited by
+ // the AST optimizer/analyzer.
+ if (!Rewriter::Optimize(literal)) {
+ return Handle<JSFunction>::null();
+ }
+
+ // Generate code and return it.
+ bool is_compiled = false;
+ if (FLAG_fast_compiler && literal->try_fast_codegen()) {
+ CodeGenSelector selector;
+ CodeGenSelector::CodeGenTag code_gen = selector.Select(literal);
+ if (code_gen == CodeGenSelector::FAST) {
+ code = FastCodeGenerator::MakeCode(literal,
+ script,
+ false); // Not eval.
+ is_compiled = true;
+ }
+ }
+
+ if (!is_compiled) {
+ // We didn't try the fast compiler, or we failed to select it.
+ code = CodeGenerator::MakeCode(literal,
+ script,
+ false); // Not eval.
+ }
+
+ // Check for stack-overflow exception.
+ if (code.is_null()) {
+ caller->SetStackOverflow();
+ return Handle<JSFunction>::null();
+ }
+
+ // Function compilation complete.
+ LOG(CodeCreateEvent(Logger::FUNCTION_TAG, *code, *literal->name()));
+
+#ifdef ENABLE_OPROFILE_AGENT
+ OProfileAgent::CreateNativeCodeRegion(*node->name(),
+ code->instruction_start(),
+ code->instruction_size());
+#endif
+ }
+
+ // Create a boilerplate function.
+ Handle<JSFunction> function =
+ Factory::NewFunctionBoilerplate(literal->name(),
+ literal->materialized_literal_count(),
+ code);
+ SetFunctionInfo(function, literal, false, script);
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ // Notify debugger that a new function has been added.
+ Debugger::OnNewFunction(function);
+#endif
+
+ // Set the expected number of properties for instances and return
+ // the resulting function.
+ SetExpectedNofPropertiesFromEstimate(function,
+ literal->expected_property_count());
+ return function;
+}
+
+
+// Sets the function info on a function.
+// The start_position points to the first '(' character after the function name
+// in the full script source. When counting characters in the script source the
+// the first character is number 0 (not 1).
+void Compiler::SetFunctionInfo(Handle<JSFunction> fun,
+ FunctionLiteral* lit,
+ bool is_toplevel,
+ Handle<Script> script) {
+ fun->shared()->set_length(lit->num_parameters());
+ fun->shared()->set_formal_parameter_count(lit->num_parameters());
+ fun->shared()->set_script(*script);
+ fun->shared()->set_function_token_position(lit->function_token_position());
+ fun->shared()->set_start_position(lit->start_position());
+ fun->shared()->set_end_position(lit->end_position());
+ fun->shared()->set_is_expression(lit->is_expression());
+ fun->shared()->set_is_toplevel(is_toplevel);
+ fun->shared()->set_inferred_name(*lit->inferred_name());
+ fun->shared()->SetThisPropertyAssignmentsInfo(
+ lit->has_only_simple_this_property_assignments(),
+ *lit->this_property_assignments());
+ fun->shared()->set_try_fast_codegen(lit->try_fast_codegen());
+}
+
+
CodeGenSelector::CodeGenTag CodeGenSelector::Select(FunctionLiteral* fun) {
Scope* scope = fun->scope();
- if (!scope->is_global_scope()) {
- if (FLAG_trace_bailout) PrintF("Non-global scope\n");
+ if (scope->num_heap_slots() > 0) {
+ // We support functions with a local context if they do not have
+ // parameters that need to be copied into the context.
+ for (int i = 0, len = scope->num_parameters(); i < len; i++) {
+ Slot* slot = scope->parameter(i)->slot();
+ if (slot != NULL && slot->type() == Slot::CONTEXT) {
+ if (FLAG_trace_bailout) {
+ PrintF("function has context-allocated parameters");
+ }
+ return NORMAL;
+ }
+ }
+ }
+
+ if (scope->arguments() != NULL) {
+ if (FLAG_trace_bailout) PrintF("function uses 'arguments'\n");
return NORMAL;
}
- ASSERT(scope->num_heap_slots() == 0);
- ASSERT(scope->arguments() == NULL);
has_supported_syntax_ = true;
- VisitDeclarations(fun->scope()->declarations());
+ VisitDeclarations(scope->declarations());
if (!has_supported_syntax_) return NORMAL;
VisitStatements(fun->body());
@@ -512,34 +645,9 @@
}
-void CodeGenSelector::VisitAsEffect(Expression* expr) {
- if (location_.is_effect()) {
- Visit(expr);
- } else {
- Location saved = location_;
- location_ = Location::Effect();
- Visit(expr);
- location_ = saved;
- }
-}
-
-
-void CodeGenSelector::VisitAsValue(Expression* expr) {
- if (location_.is_value()) {
- Visit(expr);
- } else {
- Location saved = location_;
- location_ = Location::Value();
- Visit(expr);
- location_ = saved;
- }
-}
-
-
void CodeGenSelector::VisitDeclaration(Declaration* decl) {
- Variable* var = decl->proxy()->var();
- if (!var->is_global() || var->mode() == Variable::CONST) {
- BAILOUT("Non-global declaration");
+ if (decl->fun() != NULL) {
+ ProcessExpression(decl->fun(), Expression::kValue);
}
}
@@ -550,7 +658,7 @@
void CodeGenSelector::VisitExpressionStatement(ExpressionStatement* stmt) {
- VisitAsEffect(stmt->expression());
+ ProcessExpression(stmt->expression(), Expression::kEffect);
}
@@ -560,7 +668,11 @@
void CodeGenSelector::VisitIfStatement(IfStatement* stmt) {
- BAILOUT("IfStatement");
+ ProcessExpression(stmt->condition(), Expression::kTest);
+ CHECK_BAILOUT;
+ Visit(stmt->then_statement());
+ CHECK_BAILOUT;
+ Visit(stmt->else_statement());
}
@@ -575,7 +687,7 @@
void CodeGenSelector::VisitReturnStatement(ReturnStatement* stmt) {
- VisitAsValue(stmt->expression());
+ ProcessExpression(stmt->expression(), Expression::kValue);
}
@@ -595,17 +707,39 @@
void CodeGenSelector::VisitDoWhileStatement(DoWhileStatement* stmt) {
- BAILOUT("DoWhileStatement");
+ // We do not handle loops with breaks or continue statements in their
+ // body. We will bailout when we hit those statements in the body.
+ ProcessExpression(stmt->cond(), Expression::kTest);
+ CHECK_BAILOUT;
+ Visit(stmt->body());
}
void CodeGenSelector::VisitWhileStatement(WhileStatement* stmt) {
- BAILOUT("WhileStatement");
+ // We do not handle loops with breaks or continue statements in their
+ // body. We will bailout when we hit those statements in the body.
+ ProcessExpression(stmt->cond(), Expression::kTest);
+ CHECK_BAILOUT;
+ Visit(stmt->body());
}
void CodeGenSelector::VisitForStatement(ForStatement* stmt) {
- BAILOUT("ForStatement");
+ // We do not handle loops with breaks or continue statements in their
+ // body. We will bailout when we hit those statements in the body.
+ if (stmt->init() != NULL) {
+ Visit(stmt->init());
+ CHECK_BAILOUT;
+ }
+ if (stmt->cond() != NULL) {
+ ProcessExpression(stmt->cond(), Expression::kTest);
+ CHECK_BAILOUT;
+ }
+ Visit(stmt->body());
+ if (stmt->next() != NULL) {
+ CHECK_BAILOUT;
+ Visit(stmt->next());
+ }
}
@@ -625,15 +759,12 @@
void CodeGenSelector::VisitDebuggerStatement(DebuggerStatement* stmt) {
- BAILOUT("DebuggerStatement");
+ // Debugger statement is supported.
}
void CodeGenSelector::VisitFunctionLiteral(FunctionLiteral* expr) {
- if (!expr->AllowsLazyCompilation()) {
- BAILOUT("FunctionLiteral does not allow lazy compilation");
- }
- expr->set_location(location_);
+ // Function literal is supported.
}
@@ -644,7 +775,11 @@
void CodeGenSelector::VisitConditional(Conditional* expr) {
- BAILOUT("Conditional");
+ ProcessExpression(expr->condition(), Expression::kTest);
+ CHECK_BAILOUT;
+ ProcessExpression(expr->then_expression(), context_);
+ CHECK_BAILOUT;
+ ProcessExpression(expr->else_expression(), context_);
}
@@ -659,28 +794,27 @@
if (rewrite != NULL) {
// Non-global.
Slot* slot = rewrite->AsSlot();
- if (slot == NULL) {
- // This is a variable rewritten to an explicit property access
- // on the arguments object.
- BAILOUT("non-global/non-slot variable reference");
- }
-
- Slot::Type type = slot->type();
- if (type != Slot::PARAMETER && type != Slot::LOCAL) {
- BAILOUT("non-parameter/non-local slot reference");
+ if (slot != NULL) {
+ Slot::Type type = slot->type();
+ // When LOOKUP slots are enabled, some currently dead code
+ // implementing unary typeof will become live.
+ if (type == Slot::LOOKUP) {
+ BAILOUT("Lookup slot");
+ }
+ } else {
+ BAILOUT("access to arguments object");
}
}
- expr->set_location(location_);
}
void CodeGenSelector::VisitLiteral(Literal* expr) {
- expr->set_location(location_);
+ /* Nothing to do. */
}
void CodeGenSelector::VisitRegExpLiteral(RegExpLiteral* expr) {
- expr->set_location(location_);
+ /* Nothing to do. */
}
@@ -710,14 +844,13 @@
case ObjectLiteral::Property::GETTER: // Fall through.
case ObjectLiteral::Property::SETTER: // Fall through.
case ObjectLiteral::Property::PROTOTYPE:
- VisitAsValue(property->key());
+ ProcessExpression(property->key(), Expression::kValue);
CHECK_BAILOUT;
break;
}
- VisitAsValue(property->value());
+ ProcessExpression(property->value(), Expression::kValue);
CHECK_BAILOUT;
}
- expr->set_location(location_);
}
@@ -727,10 +860,9 @@
Expression* subexpr = subexprs->at(i);
if (subexpr->AsLiteral() != NULL) continue;
if (CompileTimeValue::IsCompileTimeValue(subexpr)) continue;
- VisitAsValue(subexpr);
+ ProcessExpression(subexpr, Expression::kValue);
CHECK_BAILOUT;
}
- expr->set_location(location_);
}
@@ -740,13 +872,8 @@
void CodeGenSelector::VisitAssignment(Assignment* expr) {
- // We support plain non-compound assignments to parameters and
- // non-context (stack-allocated) locals.
- if (expr->starts_initialization_block() ||
- expr->ends_initialization_block()) {
- BAILOUT("initialization block start");
- }
-
+ // We support plain non-compound assignments to properties, parameters and
+ // non-context (stack-allocated) locals, and global variables.
Token::Value op = expr->op();
if (op == Token::INIT_CONST) BAILOUT("initialize constant");
if (op != Token::ASSIGN && op != Token::INIT_VAR) {
@@ -754,18 +881,39 @@
}
Variable* var = expr->target()->AsVariableProxy()->AsVariable();
- if (var == NULL) BAILOUT("non-variable assignment");
-
- if (!var->is_global()) {
- ASSERT(var->slot() != NULL);
- Slot::Type type = var->slot()->type();
- if (type != Slot::PARAMETER && type != Slot::LOCAL) {
- BAILOUT("non-parameter/non-local slot assignment");
+ Property* prop = expr->target()->AsProperty();
+ if (var != NULL) {
+ // All global variables are supported.
+ if (!var->is_global()) {
+ if (var->slot() == NULL) {
+ // This is a parameter that has rewritten to an arguments access.
+ BAILOUT("non-global/non-slot assignment");
+ }
+ Slot::Type type = var->slot()->type();
+ if (type == Slot::LOOKUP) {
+ BAILOUT("Lookup slot");
+ }
}
+ } else if (prop != NULL) {
+ ProcessExpression(prop->obj(), Expression::kValue);
+ CHECK_BAILOUT;
+ // We will only visit the key during code generation for keyed property
+ // stores. Leave its expression context uninitialized for named
+ // property stores.
+ Literal* lit = prop->key()->AsLiteral();
+ uint32_t ignored;
+ if (lit == NULL ||
+ !lit->handle()->IsSymbol() ||
+ String::cast(*(lit->handle()))->AsArrayIndex(&ignored)) {
+ ProcessExpression(prop->key(), Expression::kValue);
+ CHECK_BAILOUT;
+ }
+ } else {
+ // This is a throw reference error.
+ BAILOUT("non-variable/non-property assignment");
}
- VisitAsValue(expr->value());
- expr->set_location(location_);
+ ProcessExpression(expr->value(), Expression::kValue);
}
@@ -775,10 +923,9 @@
void CodeGenSelector::VisitProperty(Property* expr) {
- VisitAsValue(expr->obj());
+ ProcessExpression(expr->obj(), Expression::kValue);
CHECK_BAILOUT;
- VisitAsValue(expr->key());
- expr->set_location(location_);
+ ProcessExpression(expr->key(), Expression::kValue);
}
@@ -789,33 +936,45 @@
// Check for supported calls
if (var != NULL && var->is_possibly_eval()) {
- BAILOUT("Call to a function named 'eval'");
+ BAILOUT("call to the identifier 'eval'");
} else if (var != NULL && !var->is_this() && var->is_global()) {
- // ----------------------------------
- // JavaScript example: 'foo(1, 2, 3)' // foo is global
- // ----------------------------------
+ // Calls to global variables are supported.
+ } else if (var != NULL && var->slot() != NULL &&
+ var->slot()->type() == Slot::LOOKUP) {
+ BAILOUT("call to a lookup slot");
+ } else if (fun->AsProperty() != NULL) {
+ Property* prop = fun->AsProperty();
+ Literal* literal_key = prop->key()->AsLiteral();
+ if (literal_key != NULL && literal_key->handle()->IsSymbol()) {
+ ProcessExpression(prop->obj(), Expression::kValue);
+ CHECK_BAILOUT;
+ } else {
+ ProcessExpression(prop->obj(), Expression::kValue);
+ CHECK_BAILOUT;
+ ProcessExpression(prop->key(), Expression::kValue);
+ CHECK_BAILOUT;
+ }
} else {
- BAILOUT("Call to a non-global function");
+ // Otherwise the call is supported if the function expression is.
+ ProcessExpression(fun, Expression::kValue);
}
- // Check all arguments to the call. (Relies on TEMP meaning STACK.)
+ // Check all arguments to the call.
for (int i = 0; i < args->length(); i++) {
- VisitAsValue(args->at(i));
+ ProcessExpression(args->at(i), Expression::kValue);
CHECK_BAILOUT;
}
- expr->set_location(location_);
}
void CodeGenSelector::VisitCallNew(CallNew* expr) {
- VisitAsValue(expr->expression());
+ ProcessExpression(expr->expression(), Expression::kValue);
CHECK_BAILOUT;
ZoneList<Expression*>* args = expr->arguments();
// Check all arguments to the call
for (int i = 0; i < args->length(); i++) {
- VisitAsValue(args->at(i));
+ ProcessExpression(args->at(i), Expression::kValue);
CHECK_BAILOUT;
}
- expr->set_location(location_);
}
@@ -829,37 +988,88 @@
}
// Check all arguments to the call. (Relies on TEMP meaning STACK.)
for (int i = 0; i < expr->arguments()->length(); i++) {
- VisitAsValue(expr->arguments()->at(i));
+ ProcessExpression(expr->arguments()->at(i), Expression::kValue);
CHECK_BAILOUT;
}
- expr->set_location(location_);
}
void CodeGenSelector::VisitUnaryOperation(UnaryOperation* expr) {
- BAILOUT("UnaryOperation");
+ switch (expr->op()) {
+ case Token::VOID:
+ ProcessExpression(expr->expression(), Expression::kEffect);
+ break;
+ case Token::NOT:
+ ProcessExpression(expr->expression(), Expression::kTest);
+ break;
+ case Token::TYPEOF:
+ ProcessExpression(expr->expression(), Expression::kValue);
+ break;
+ default:
+ BAILOUT("UnaryOperation");
+ }
}
void CodeGenSelector::VisitCountOperation(CountOperation* expr) {
- BAILOUT("CountOperation");
+ // We support postfix count operations on global variables.
+ if (expr->is_prefix()) BAILOUT("Prefix CountOperation");
+ Variable* var = expr->expression()->AsVariableProxy()->AsVariable();
+ if (var == NULL || !var->is_global()) BAILOUT("non-global postincrement");
+ ProcessExpression(expr->expression(), Expression::kValue);
}
void CodeGenSelector::VisitBinaryOperation(BinaryOperation* expr) {
switch (expr->op()) {
case Token::COMMA:
- VisitAsEffect(expr->left());
+ ProcessExpression(expr->left(), Expression::kEffect);
CHECK_BAILOUT;
- Visit(expr->right()); // Location is the same as the parent location.
+ ProcessExpression(expr->right(), context_);
break;
case Token::OR:
- VisitAsValue(expr->left());
+ switch (context_) {
+ case Expression::kUninitialized:
+ UNREACHABLE();
+ case Expression::kEffect: // Fall through.
+ case Expression::kTest: // Fall through.
+ case Expression::kTestValue:
+ // The left subexpression's value is not needed, it is in a pure
+ // test context.
+ ProcessExpression(expr->left(), Expression::kTest);
+ break;
+ case Expression::kValue: // Fall through.
+ case Expression::kValueTest:
+ // The left subexpression's value is needed, it is in a hybrid
+ // value/test context.
+ ProcessExpression(expr->left(), Expression::kValueTest);
+ break;
+ }
CHECK_BAILOUT;
- // The location for the right subexpression is the same as for the
- // whole expression so we call Visit directly.
- Visit(expr->right());
+ ProcessExpression(expr->right(), context_);
+ break;
+
+ case Token::AND:
+ switch (context_) {
+ case Expression::kUninitialized:
+ UNREACHABLE();
+ case Expression::kEffect: // Fall through.
+ case Expression::kTest: // Fall through.
+ case Expression::kValueTest:
+ // The left subexpression's value is not needed, it is in a pure
+ // test context.
+ ProcessExpression(expr->left(), Expression::kTest);
+ break;
+ case Expression::kValue: // Fall through.
+ case Expression::kTestValue:
+ // The left subexpression's value is needed, it is in a hybrid
+ // test/value context.
+ ProcessExpression(expr->left(), Expression::kTestValue);
+ break;
+ }
+ CHECK_BAILOUT;
+ ProcessExpression(expr->right(), context_);
break;
case Token::ADD:
@@ -873,20 +1083,21 @@
case Token::SHL:
case Token::SHR:
case Token::SAR:
- VisitAsValue(expr->left());
+ ProcessExpression(expr->left(), Expression::kValue);
CHECK_BAILOUT;
- VisitAsValue(expr->right());
+ ProcessExpression(expr->right(), Expression::kValue);
break;
default:
BAILOUT("Unsupported binary operation");
}
- expr->set_location(location_);
}
void CodeGenSelector::VisitCompareOperation(CompareOperation* expr) {
- BAILOUT("CompareOperation");
+ ProcessExpression(expr->left(), Expression::kValue);
+ CHECK_BAILOUT;
+ ProcessExpression(expr->right(), Expression::kValue);
}