|
|
|
// Copyright 2009 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.
|
|
|
|
|
|
|
|
#include "v8.h"
|
|
|
|
|
|
|
|
#include "bootstrapper.h"
|
|
|
|
#include "cfg.h"
|
|
|
|
#include "scopeinfo.h"
|
|
|
|
#include "scopes.h"
|
|
|
|
|
|
|
|
namespace v8 {
|
|
|
|
namespace internal {
|
|
|
|
|
|
|
|
|
|
|
|
CfgGlobals* CfgGlobals::top_ = NULL;
|
|
|
|
|
|
|
|
|
|
|
|
CfgGlobals::CfgGlobals(FunctionLiteral* fun)
|
|
|
|
: global_fun_(fun),
|
|
|
|
global_exit_(new ExitNode()),
|
|
|
|
nowhere_(new Nowhere()),
|
|
|
|
#ifdef DEBUG
|
|
|
|
node_counter_(0),
|
|
|
|
temp_counter_(0),
|
|
|
|
#endif
|
|
|
|
previous_(top_) {
|
|
|
|
top_ = this;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#define BAILOUT(reason) \
|
|
|
|
do { return NULL; } while (false)
|
|
|
|
|
|
|
|
Cfg* Cfg::Build() {
|
|
|
|
FunctionLiteral* fun = CfgGlobals::current()->fun();
|
|
|
|
if (fun->scope()->num_heap_slots() > 0) {
|
|
|
|
BAILOUT("function has context slots");
|
|
|
|
}
|
|
|
|
if (fun->scope()->num_stack_slots() > kBitsPerPointer) {
|
|
|
|
BAILOUT("function has too many locals");
|
|
|
|
}
|
|
|
|
if (fun->scope()->num_parameters() > kBitsPerPointer - 1) {
|
|
|
|
BAILOUT("function has too many parameters");
|
|
|
|
}
|
|
|
|
if (fun->scope()->arguments() != NULL) {
|
|
|
|
BAILOUT("function uses .arguments");
|
|
|
|
}
|
|
|
|
|
|
|
|
ZoneList<Statement*>* body = fun->body();
|
|
|
|
if (body->is_empty()) {
|
|
|
|
BAILOUT("empty function body");
|
|
|
|
}
|
|
|
|
|
|
|
|
StatementCfgBuilder builder;
|
|
|
|
builder.VisitStatements(body);
|
|
|
|
Cfg* graph = builder.graph();
|
|
|
|
if (graph == NULL) {
|
|
|
|
BAILOUT("unsupported statement type");
|
|
|
|
}
|
|
|
|
if (graph->is_empty()) {
|
|
|
|
BAILOUT("function body produces empty cfg");
|
|
|
|
}
|
|
|
|
if (graph->has_exit()) {
|
|
|
|
BAILOUT("control path without explicit return");
|
|
|
|
}
|
|
|
|
graph->PrependEntryNode();
|
|
|
|
return graph;
|
|
|
|
}
|
|
|
|
|
|
|
|
#undef BAILOUT
|
|
|
|
|
|
|
|
|
|
|
|
void Cfg::PrependEntryNode() {
|
|
|
|
ASSERT(!is_empty());
|
|
|
|
entry_ = new EntryNode(InstructionBlock::cast(entry()));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Cfg::Append(Instruction* instr) {
|
|
|
|
ASSERT(is_empty() || has_exit());
|
|
|
|
if (is_empty()) {
|
|
|
|
entry_ = exit_ = new InstructionBlock();
|
|
|
|
}
|
|
|
|
InstructionBlock::cast(exit_)->Append(instr);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Cfg::AppendReturnInstruction(Value* value) {
|
|
|
|
Append(new ReturnInstr(value));
|
|
|
|
ExitNode* global_exit = CfgGlobals::current()->exit();
|
|
|
|
InstructionBlock::cast(exit_)->set_successor(global_exit);
|
|
|
|
exit_ = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Cfg::Concatenate(Cfg* other) {
|
|
|
|
ASSERT(is_empty() || has_exit());
|
|
|
|
if (other->is_empty()) return;
|
|
|
|
|
|
|
|
if (is_empty()) {
|
|
|
|
entry_ = other->entry();
|
|
|
|
exit_ = other->exit();
|
|
|
|
} else {
|
|
|
|
// We have a pair of nonempty fragments and this has an available exit.
|
|
|
|
// Destructively glue the fragments together.
|
|
|
|
InstructionBlock* first = InstructionBlock::cast(exit_);
|
|
|
|
InstructionBlock* second = InstructionBlock::cast(other->entry());
|
|
|
|
first->instructions()->AddAll(*second->instructions());
|
|
|
|
if (second->successor() != NULL) {
|
|
|
|
first->set_successor(second->successor());
|
|
|
|
exit_ = other->exit();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void InstructionBlock::Unmark() {
|
|
|
|
if (is_marked_) {
|
|
|
|
is_marked_ = false;
|
|
|
|
successor_->Unmark();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void EntryNode::Unmark() {
|
|
|
|
if (is_marked_) {
|
|
|
|
is_marked_ = false;
|
|
|
|
successor_->Unmark();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ExitNode::Unmark() {
|
|
|
|
is_marked_ = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Handle<Code> Cfg::Compile(Handle<Script> script) {
|
|
|
|
const int kInitialBufferSize = 4 * KB;
|
|
|
|
MacroAssembler* masm = new MacroAssembler(NULL, kInitialBufferSize);
|
|
|
|
entry()->Compile(masm);
|
|
|
|
entry()->Unmark();
|
|
|
|
CodeDesc desc;
|
|
|
|
masm->GetCode(&desc);
|
|
|
|
FunctionLiteral* fun = CfgGlobals::current()->fun();
|
|
|
|
ZoneScopeInfo info(fun->scope());
|
|
|
|
InLoopFlag in_loop = fun->loop_nesting() ? IN_LOOP : NOT_IN_LOOP;
|
|
|
|
Code::Flags flags = Code::ComputeFlags(Code::FUNCTION, in_loop);
|
|
|
|
Handle<Code> code = Factory::NewCode(desc, &info, flags, masm->CodeObject());
|
|
|
|
|
|
|
|
// Add unresolved entries in the code to the fixup list.
|
|
|
|
Bootstrapper::AddFixup(*code, masm);
|
|
|
|
|
|
|
|
#ifdef ENABLE_DISASSEMBLER
|
|
|
|
if (FLAG_print_code) {
|
|
|
|
// Print the source code if available.
|
|
|
|
if (!script->IsUndefined() && !script->source()->IsUndefined()) {
|
|
|
|
PrintF("--- Raw source ---\n");
|
|
|
|
StringInputBuffer stream(String::cast(script->source()));
|
|
|
|
stream.Seek(fun->start_position());
|
|
|
|
// fun->end_position() points to the last character in the
|
|
|
|
// stream. We need to compensate by adding one to calculate the
|
|
|
|
// length.
|
|
|
|
int source_len = fun->end_position() - fun->start_position() + 1;
|
|
|
|
for (int i = 0; i < source_len; i++) {
|
|
|
|
if (stream.has_more()) PrintF("%c", stream.GetNext());
|
|
|
|
}
|
|
|
|
PrintF("\n\n");
|
|
|
|
}
|
|
|
|
PrintF("--- Code ---\n");
|
|
|
|
code->Disassemble(*fun->name()->ToCString());
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return code;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ZeroOperandInstruction::FastAllocate(TempLocation* temp) {
|
|
|
|
temp->set_where(TempLocation::STACK);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void OneOperandInstruction::FastAllocate(TempLocation* temp) {
|
|
|
|
temp->set_where((temp == value_)
|
|
|
|
? TempLocation::ACCUMULATOR
|
|
|
|
: TempLocation::STACK);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void TwoOperandInstruction::FastAllocate(TempLocation* temp) {
|
|
|
|
temp->set_where((temp == value0_ || temp == value1_)
|
|
|
|
? TempLocation::ACCUMULATOR
|
|
|
|
: TempLocation::STACK);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void PositionInstr::Compile(MacroAssembler* masm) {
|
|
|
|
if (FLAG_debug_info && pos_ != RelocInfo::kNoPosition) {
|
|
|
|
masm->RecordStatementPosition(pos_);
|
|
|
|
masm->RecordPosition(pos_);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void MoveInstr::Compile(MacroAssembler* masm) {
|
|
|
|
location()->Move(masm, value());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// The expression builder should not be used for declarations or statements.
|
|
|
|
void ExpressionCfgBuilder::VisitDeclaration(Declaration* decl) {
|
|
|
|
UNREACHABLE();
|
|
|
|
}
|
|
|
|
|
|
|
|
#define DEFINE_VISIT(type) \
|
|
|
|
void ExpressionCfgBuilder::Visit##type(type* stmt) { UNREACHABLE(); }
|
|
|
|
STATEMENT_NODE_LIST(DEFINE_VISIT)
|
|
|
|
#undef DEFINE_VISIT
|
|
|
|
|
|
|
|
|
|
|
|
// Macros (temporarily) handling unsupported expression types.
|
|
|
|
#define BAILOUT(reason) \
|
|
|
|
do { \
|
|
|
|
graph_ = NULL; \
|
|
|
|
return; \
|
|
|
|
} while (false)
|
|
|
|
|
|
|
|
void ExpressionCfgBuilder::VisitFunctionLiteral(FunctionLiteral* expr) {
|
|
|
|
BAILOUT("FunctionLiteral");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ExpressionCfgBuilder::VisitFunctionBoilerplateLiteral(
|
|
|
|
FunctionBoilerplateLiteral* expr) {
|
|
|
|
BAILOUT("FunctionBoilerplateLiteral");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ExpressionCfgBuilder::VisitConditional(Conditional* expr) {
|
|
|
|
BAILOUT("Conditional");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ExpressionCfgBuilder::VisitSlot(Slot* expr) {
|
|
|
|
BAILOUT("Slot");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ExpressionCfgBuilder::VisitVariableProxy(VariableProxy* expr) {
|
|
|
|
Expression* rewrite = expr->var()->rewrite();
|
|
|
|
if (rewrite == NULL || rewrite->AsSlot() == NULL) {
|
|
|
|
BAILOUT("unsupported variable (not a slot)");
|
|
|
|
}
|
|
|
|
Slot* slot = rewrite->AsSlot();
|
|
|
|
if (slot->type() != Slot::PARAMETER && slot->type() != Slot::LOCAL) {
|
|
|
|
BAILOUT("unsupported slot type (not a parameter or local)");
|
|
|
|
}
|
|
|
|
// Ignore the passed destination.
|
|
|
|
value_ = new SlotLocation(slot->type(), slot->index());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ExpressionCfgBuilder::VisitLiteral(Literal* expr) {
|
|
|
|
// Ignore the passed destination.
|
|
|
|
value_ = new Constant(expr->handle());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ExpressionCfgBuilder::VisitRegExpLiteral(RegExpLiteral* expr) {
|
|
|
|
BAILOUT("RegExpLiteral");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ExpressionCfgBuilder::VisitObjectLiteral(ObjectLiteral* expr) {
|
|
|
|
BAILOUT("ObjectLiteral");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ExpressionCfgBuilder::VisitArrayLiteral(ArrayLiteral* expr) {
|
|
|
|
BAILOUT("ArrayLiteral");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ExpressionCfgBuilder::VisitCatchExtensionObject(
|
|
|
|
CatchExtensionObject* expr) {
|
|
|
|
BAILOUT("CatchExtensionObject");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ExpressionCfgBuilder::VisitAssignment(Assignment* expr) {
|
|
|
|
if (expr->op() != Token::ASSIGN && expr->op() != Token::INIT_VAR) {
|
|
|
|
BAILOUT("unsupported compound assignment");
|
|
|
|
}
|
|
|
|
Expression* lhs = expr->target();
|
|
|
|
if (lhs->AsProperty() != NULL) {
|
|
|
|
BAILOUT("unsupported property assignment");
|
|
|
|
}
|
|
|
|
|
|
|
|
Variable* var = lhs->AsVariableProxy()->AsVariable();
|
|
|
|
if (var == NULL) {
|
|
|
|
BAILOUT("unsupported invalid left-hand side");
|
|
|
|
}
|
|
|
|
if (var->is_global()) {
|
|
|
|
BAILOUT("unsupported global variable");
|
|
|
|
}
|
|
|
|
Slot* slot = var->slot();
|
|
|
|
ASSERT(slot != NULL);
|
|
|
|
if (slot->type() != Slot::PARAMETER && slot->type() != Slot::LOCAL) {
|
|
|
|
BAILOUT("unsupported slot lhs (not a parameter or local)");
|
|
|
|
}
|
|
|
|
|
|
|
|
// Parameter and local slot assignments.
|
|
|
|
ExpressionCfgBuilder builder;
|
|
|
|
SlotLocation* loc = new SlotLocation(slot->type(), slot->index());
|
|
|
|
builder.Build(expr->value(), loc);
|
|
|
|
if (builder.graph() == NULL) {
|
|
|
|
BAILOUT("unsupported expression in assignment");
|
|
|
|
}
|
|
|
|
// If the expression did not come back in the slot location, append
|
|
|
|
// a move to the CFG.
|
|
|
|
graph_ = builder.graph();
|
|
|
|
if (builder.value() != loc) {
|
|
|
|
graph()->Append(new MoveInstr(loc, builder.value()));
|
|
|
|
}
|
|
|
|
// Record the assignment.
|
|
|
|
assigned_vars_.AddElement(loc);
|
|
|
|
// Ignore the destination passed to us.
|
|
|
|
value_ = loc;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ExpressionCfgBuilder::VisitThrow(Throw* expr) {
|
|
|
|
BAILOUT("Throw");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ExpressionCfgBuilder::VisitProperty(Property* expr) {
|
|
|
|
ExpressionCfgBuilder object, key;
|
|
|
|
object.Build(expr->obj(), NULL);
|
|
|
|
if (object.graph() == NULL) {
|
|
|
|
BAILOUT("unsupported object subexpression in propload");
|
|
|
|
}
|
|
|
|
key.Build(expr->key(), NULL);
|
|
|
|
if (key.graph() == NULL) {
|
|
|
|
BAILOUT("unsupported key subexpression in propload");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (destination_ == NULL) destination_ = new TempLocation();
|
|
|
|
|
|
|
|
graph_ = object.graph();
|
|
|
|
// Insert a move to a fresh temporary if the object value is in a slot
|
|
|
|
// that's assigned in the key.
|
|
|
|
Location* temp = NULL;
|
|
|
|
if (object.value()->is_slot() &&
|
|
|
|
key.assigned_vars()->Contains(SlotLocation::cast(object.value()))) {
|
|
|
|
temp = new TempLocation();
|
|
|
|
graph()->Append(new MoveInstr(temp, object.value()));
|
|
|
|
}
|
|
|
|
graph()->Concatenate(key.graph());
|
|
|
|
graph()->Append(new PropLoadInstr(destination_,
|
|
|
|
temp == NULL ? object.value() : temp,
|
|
|
|
key.value()));
|
|
|
|
|
|
|
|
assigned_vars_ = *object.assigned_vars();
|
|
|
|
assigned_vars()->Union(key.assigned_vars());
|
|
|
|
|
|
|
|
value_ = destination_;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ExpressionCfgBuilder::VisitCall(Call* expr) {
|
|
|
|
BAILOUT("Call");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ExpressionCfgBuilder::VisitCallEval(CallEval* expr) {
|
|
|
|
BAILOUT("CallEval");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ExpressionCfgBuilder::VisitCallNew(CallNew* expr) {
|
|
|
|
BAILOUT("CallNew");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ExpressionCfgBuilder::VisitCallRuntime(CallRuntime* expr) {
|
|
|
|
BAILOUT("CallRuntime");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ExpressionCfgBuilder::VisitUnaryOperation(UnaryOperation* expr) {
|
|
|
|
BAILOUT("UnaryOperation");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ExpressionCfgBuilder::VisitCountOperation(CountOperation* expr) {
|
|
|
|
BAILOUT("CountOperation");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ExpressionCfgBuilder::VisitBinaryOperation(BinaryOperation* expr) {
|
|
|
|
Token::Value op = expr->op();
|
|
|
|
switch (op) {
|
|
|
|
case Token::COMMA:
|
|
|
|
case Token::OR:
|
|
|
|
case Token::AND:
|
|
|
|
BAILOUT("unsupported binary operation");
|
|
|
|
|
|
|
|
case Token::BIT_OR:
|
|
|
|
case Token::BIT_XOR:
|
|
|
|
case Token::BIT_AND:
|
|
|
|
case Token::SHL:
|
|
|
|
case Token::SAR:
|
|
|
|
case Token::SHR:
|
|
|
|
case Token::ADD:
|
|
|
|
case Token::SUB:
|
|
|
|
case Token::MUL:
|
|
|
|
case Token::DIV:
|
|
|
|
case Token::MOD: {
|
|
|
|
ExpressionCfgBuilder left, right;
|
|
|
|
left.Build(expr->left(), NULL);
|
|
|
|
if (left.graph() == NULL) {
|
|
|
|
BAILOUT("unsupported left subexpression in binop");
|
|
|
|
}
|
|
|
|
right.Build(expr->right(), NULL);
|
|
|
|
if (right.graph() == NULL) {
|
|
|
|
BAILOUT("unsupported right subexpression in binop");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (destination_ == NULL) destination_ = new TempLocation();
|
|
|
|
|
|
|
|
graph_ = left.graph();
|
|
|
|
// Insert a move to a fresh temporary if the left value is in a
|
|
|
|
// slot that's assigned on the right.
|
|
|
|
Location* temp = NULL;
|
|
|
|
if (left.value()->is_slot() &&
|
|
|
|
right.assigned_vars()->Contains(SlotLocation::cast(left.value()))) {
|
|
|
|
temp = new TempLocation();
|
|
|
|
graph()->Append(new MoveInstr(temp, left.value()));
|
|
|
|
}
|
|
|
|
graph()->Concatenate(right.graph());
|
|
|
|
graph()->Append(new BinaryOpInstr(destination_, op,
|
|
|
|
temp == NULL ? left.value() : temp,
|
|
|
|
right.value()));
|
|
|
|
|
|
|
|
assigned_vars_ = *left.assigned_vars();
|
|
|
|
assigned_vars()->Union(right.assigned_vars());
|
|
|
|
|
|
|
|
value_ = destination_;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
UNREACHABLE();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ExpressionCfgBuilder::VisitCompareOperation(CompareOperation* expr) {
|
|
|
|
BAILOUT("CompareOperation");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ExpressionCfgBuilder::VisitThisFunction(ThisFunction* expr) {
|
|
|
|
BAILOUT("ThisFunction");
|
|
|
|
}
|
|
|
|
|
|
|
|
#undef BAILOUT
|
|
|
|
|
|
|
|
|
|
|
|
// Macros (temporarily) handling unsupported statement types.
|
|
|
|
#define BAILOUT(reason) \
|
|
|
|
do { \
|
|
|
|
graph_ = NULL; \
|
|
|
|
return; \
|
|
|
|
} while (false)
|
|
|
|
|
|
|
|
#define CHECK_BAILOUT() \
|
|
|
|
if (graph() == NULL) { return; } else {}
|
|
|
|
|
|
|
|
void StatementCfgBuilder::VisitStatements(ZoneList<Statement*>* stmts) {
|
|
|
|
for (int i = 0, len = stmts->length(); i < len; i++) {
|
|
|
|
Visit(stmts->at(i));
|
|
|
|
CHECK_BAILOUT();
|
|
|
|
if (!graph()->has_exit()) return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// The statement builder should not be used for declarations or expressions.
|
|
|
|
void StatementCfgBuilder::VisitDeclaration(Declaration* decl) { UNREACHABLE(); }
|
|
|
|
|
|
|
|
#define DEFINE_VISIT(type) \
|
|
|
|
void StatementCfgBuilder::Visit##type(type* expr) { UNREACHABLE(); }
|
|
|
|
EXPRESSION_NODE_LIST(DEFINE_VISIT)
|
|
|
|
#undef DEFINE_VISIT
|
|
|
|
|
|
|
|
|
|
|
|
void StatementCfgBuilder::VisitBlock(Block* stmt) {
|
|
|
|
VisitStatements(stmt->statements());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void StatementCfgBuilder::VisitExpressionStatement(ExpressionStatement* stmt) {
|
|
|
|
ExpressionCfgBuilder builder;
|
|
|
|
builder.Build(stmt->expression(), CfgGlobals::current()->nowhere());
|
|
|
|
if (builder.graph() == NULL) {
|
|
|
|
BAILOUT("unsupported expression in expression statement");
|
|
|
|
}
|
|
|
|
graph()->Append(new PositionInstr(stmt->statement_pos()));
|
|
|
|
graph()->Concatenate(builder.graph());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void StatementCfgBuilder::VisitEmptyStatement(EmptyStatement* stmt) {
|
|
|
|
// Nothing to do.
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void StatementCfgBuilder::VisitIfStatement(IfStatement* stmt) {
|
|
|
|
BAILOUT("IfStatement");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void StatementCfgBuilder::VisitContinueStatement(ContinueStatement* stmt) {
|
|
|
|
BAILOUT("ContinueStatement");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void StatementCfgBuilder::VisitBreakStatement(BreakStatement* stmt) {
|
|
|
|
BAILOUT("BreakStatement");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void StatementCfgBuilder::VisitReturnStatement(ReturnStatement* stmt) {
|
|
|
|
ExpressionCfgBuilder builder;
|
|
|
|
builder.Build(stmt->expression(), NULL);
|
|
|
|
if (builder.graph() == NULL) {
|
|
|
|
BAILOUT("unsupported expression in return statement");
|
|
|
|
}
|
|
|
|
|
|
|
|
graph()->Append(new PositionInstr(stmt->statement_pos()));
|
|
|
|
graph()->Concatenate(builder.graph());
|
|
|
|
graph()->AppendReturnInstruction(builder.value());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void StatementCfgBuilder::VisitWithEnterStatement(WithEnterStatement* stmt) {
|
|
|
|
BAILOUT("WithEnterStatement");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void StatementCfgBuilder::VisitWithExitStatement(WithExitStatement* stmt) {
|
|
|
|
BAILOUT("WithExitStatement");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void StatementCfgBuilder::VisitSwitchStatement(SwitchStatement* stmt) {
|
|
|
|
BAILOUT("SwitchStatement");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void StatementCfgBuilder::VisitLoopStatement(LoopStatement* stmt) {
|
|
|
|
BAILOUT("LoopStatement");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void StatementCfgBuilder::VisitForInStatement(ForInStatement* stmt) {
|
|
|
|
BAILOUT("ForInStatement");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void StatementCfgBuilder::VisitTryCatch(TryCatch* stmt) {
|
|
|
|
BAILOUT("TryCatch");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void StatementCfgBuilder::VisitTryFinally(TryFinally* stmt) {
|
|
|
|
BAILOUT("TryFinally");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void StatementCfgBuilder::VisitDebuggerStatement(DebuggerStatement* stmt) {
|
|
|
|
BAILOUT("DebuggerStatement");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
// CFG printing support (via depth-first, preorder block traversal).
|
|
|
|
|
|
|
|
void Cfg::Print() {
|
|
|
|
entry_->Print();
|
|
|
|
entry_->Unmark();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Constant::Print() {
|
|
|
|
PrintF("Constant ");
|
|
|
|
handle_->Print();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Nowhere::Print() {
|
|
|
|
PrintF("Nowhere");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void SlotLocation::Print() {
|
|
|
|
PrintF("Slot ");
|
|
|
|
switch (type_) {
|
|
|
|
case Slot::PARAMETER:
|
|
|
|
PrintF("(PARAMETER, %d)", index_);
|
|
|
|
break;
|
|
|
|
case Slot::LOCAL:
|
|
|
|
PrintF("(LOCAL, %d)", index_);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
UNREACHABLE();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void TempLocation::Print() {
|
|
|
|
PrintF("Temp %d", number());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void OneOperandInstruction::Print() {
|
|
|
|
PrintF("(");
|
|
|
|
location()->Print();
|
|
|
|
PrintF(", ");
|
|
|
|
value_->Print();
|
|
|
|
PrintF(")");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void TwoOperandInstruction::Print() {
|
|
|
|
PrintF("(");
|
|
|
|
location()->Print();
|
|
|
|
PrintF(", ");
|
|
|
|
value0_->Print();
|
|
|
|
PrintF(", ");
|
|
|
|
value1_->Print();
|
|
|
|
PrintF(")");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void MoveInstr::Print() {
|
|
|
|
PrintF("Move ");
|
|
|
|
OneOperandInstruction::Print();
|
|
|
|
PrintF("\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void PropLoadInstr::Print() {
|
|
|
|
PrintF("PropLoad ");
|
|
|
|
TwoOperandInstruction::Print();
|
|
|
|
PrintF("\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void BinaryOpInstr::Print() {
|
|
|
|
switch (op()) {
|
|
|
|
case Token::OR:
|
|
|
|
// Two character operand.
|
|
|
|
PrintF("BinaryOp[OR] ");
|
|
|
|
break;
|
|
|
|
case Token::AND:
|
|
|
|
case Token::SHL:
|
|
|
|
case Token::SAR:
|
|
|
|
case Token::SHR:
|
|
|
|
case Token::ADD:
|
|
|
|
case Token::SUB:
|
|
|
|
case Token::MUL:
|
|
|
|
case Token::DIV:
|
|
|
|
case Token::MOD:
|
|
|
|
// Three character operands.
|
|
|
|
PrintF("BinaryOp[%s] ", Token::Name(op()));
|
|
|
|
break;
|
|
|
|
case Token::COMMA:
|
|
|
|
// Five character operand.
|
|
|
|
PrintF("BinaryOp[COMMA] ");
|
|
|
|
break;
|
|
|
|
case Token::BIT_OR:
|
|
|
|
// Six character operand.
|
|
|
|
PrintF("BinaryOp[BIT_OR] ");
|
|
|
|
break;
|
|
|
|
case Token::BIT_XOR:
|
|
|
|
case Token::BIT_AND:
|
|
|
|
// Seven character operands.
|
|
|
|
PrintF("BinaryOp[%s] ", Token::Name(op()));
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
UNREACHABLE();
|
|
|
|
}
|
|
|
|
TwoOperandInstruction::Print();
|
|
|
|
PrintF("\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ReturnInstr::Print() {
|
|
|
|
PrintF("Return ");
|
|
|
|
OneOperandInstruction::Print();
|
|
|
|
PrintF("\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void InstructionBlock::Print() {
|
|
|
|
if (!is_marked_) {
|
|
|
|
is_marked_ = true;
|
|
|
|
PrintF("L%d:\n", number());
|
|
|
|
for (int i = 0, len = instructions_.length(); i < len; i++) {
|
|
|
|
instructions_[i]->Print();
|
|
|
|
}
|
|
|
|
PrintF("Goto L%d\n\n", successor_->number());
|
|
|
|
successor_->Print();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void EntryNode::Print() {
|
|
|
|
if (!is_marked_) {
|
|
|
|
is_marked_ = true;
|
|
|
|
successor_->Print();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ExitNode::Print() {
|
|
|
|
if (!is_marked_) {
|
|
|
|
is_marked_ = true;
|
|
|
|
PrintF("L%d:\nExit\n\n", number());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif // DEBUG
|
|
|
|
|
|
|
|
} } // namespace v8::internal
|