Restructure to support recursive invocation of the CFG builder. Add

support for stack-allocated variables when run with multipass.

There is no liveness analysis and they are currently always allocated
to memory.

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

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@2604 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
kmillikin@chromium.org 2009-08-03 07:55:48 +00:00
parent 3d070f1f0c
commit 67a61e96c7
6 changed files with 210 additions and 68 deletions

View File

@ -56,9 +56,10 @@ void EntryNode::Compile(MacroAssembler* masm) {
Comment cmnt(masm, "[ EntryNode");
__ stm(db_w, sp, r1.bit() | cp.bit() | fp.bit() | lr.bit());
__ add(fp, sp, Operand(2 * kPointerSize));
if (local_count_ > 0) {
int count = CfgGlobals::current()->fun()->scope()->num_stack_slots();
if (count > 0) {
__ mov(ip, Operand(Factory::undefined_value()));
for (int i = 0; i < local_count_; i++) {
for (int i = 0; i < count; i++) {
__ push(ip);
}
}
@ -84,7 +85,8 @@ void ExitNode::Compile(MacroAssembler* masm) {
}
__ mov(sp, fp);
__ ldm(ia_w, sp, fp.bit() | lr.bit());
__ add(sp, sp, Operand((parameter_count_ + 1) * kPointerSize));
int count = CfgGlobals::current()->fun()->scope()->num_parameters();
__ add(sp, sp, Operand((count + 1) * kPointerSize));
__ Jump(lr);
}
@ -99,6 +101,24 @@ void Constant::ToRegister(MacroAssembler* masm, Register reg) {
__ mov(reg, Operand(handle_));
}
void SlotLocation::ToRegister(MacroAssembler* masm, Register reg) {
switch (type_) {
case Slot::PARAMETER: {
int count = CfgGlobals::current()->fun()->scope()->num_parameters();
__ ldr(reg, MemOperand(fp, (1 + count - index_) * kPointerSize));
break;
}
case Slot::LOCAL: {
const int kOffset = JavaScriptFrameConstants::kLocal0Offset;
__ ldr(reg, MemOperand(fp, kOffset - index_ * kPointerSize));
break;
}
default:
UNREACHABLE();
}
}
#undef __
} } // namespace v8::internal

View File

@ -36,32 +36,37 @@ namespace v8 {
namespace internal {
ExitNode* Cfg::global_exit_ = NULL;
CfgGlobals* CfgGlobals::top_ = NULL;
void CfgNode::Reset() {
CfgGlobals::CfgGlobals(FunctionLiteral* fun)
: global_fun_(fun),
global_exit_(new ExitNode()),
#ifdef DEBUG
node_counter_ = 0;
node_counter_(0),
#endif
}
void Cfg::Reset(FunctionLiteral* fun) {
global_exit_ = new ExitNode(fun);
CfgNode::Reset();
previous_(top_) {
top_ = this;
}
#define BAILOUT(reason) \
do { return NULL; } while (false)
Cfg* Cfg::Build(FunctionLiteral* fun) {
Cfg* Cfg::Build() {
FunctionLiteral* fun = CfgGlobals::current()->fun();
if (fun->scope()->num_heap_slots() > 0) {
BAILOUT("function has context slots");
}
if (fun->scope()->arguments() != NULL) {
BAILOUT("function uses .arguments");
}
ZoneList<Statement*>* body = fun->body();
if (body->is_empty()) {
BAILOUT("empty function body");
}
Cfg::Reset(fun);
StatementBuilder builder;
builder.VisitStatements(body);
Cfg* cfg = builder.cfg();
@ -71,16 +76,16 @@ Cfg* Cfg::Build(FunctionLiteral* fun) {
if (cfg->has_exit()) {
BAILOUT("control path without explicit return");
}
cfg->PrependEntryNode(fun);
cfg->PrependEntryNode();
return cfg;
}
#undef BAILOUT
void Cfg::PrependEntryNode(FunctionLiteral* fun) {
void Cfg::PrependEntryNode() {
ASSERT(!is_empty());
entry_ = new EntryNode(fun, InstructionBlock::cast(entry()));
entry_ = new EntryNode(InstructionBlock::cast(entry()));
}
@ -93,21 +98,12 @@ void Cfg::Append(Instruction* instr) {
void Cfg::AppendReturnInstruction(Value* value) {
Append(new ReturnInstr(value));
InstructionBlock::cast(exit_)->set_successor(global_exit_);
ExitNode* global_exit = CfgGlobals::current()->exit();
InstructionBlock::cast(exit_)->set_successor(global_exit);
exit_ = NULL;
}
EntryNode::EntryNode(FunctionLiteral* fun, InstructionBlock* succ)
: successor_(succ), local_count_(fun->scope()->num_stack_slots()) {
}
ExitNode::ExitNode(FunctionLiteral* fun)
: parameter_count_(fun->scope()->num_parameters()) {
}
void InstructionBlock::Unmark() {
if (is_marked_) {
is_marked_ = false;
@ -124,16 +120,19 @@ void EntryNode::Unmark() {
}
void ExitNode::Unmark() {}
void ExitNode::Unmark() {
is_marked_ = false;
}
Handle<Code> Cfg::Compile(FunctionLiteral* fun, Handle<Script> script) {
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);
@ -149,8 +148,9 @@ Handle<Code> Cfg::Compile(FunctionLiteral* fun, Handle<Script> script) {
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.
// 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());
@ -207,7 +207,15 @@ void ExpressionBuilder::VisitSlot(Slot* expr) {
void ExpressionBuilder::VisitVariableProxy(VariableProxy* expr) {
BAILOUT("VariableProxy");
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)");
}
value_ = new SlotLocation(slot->type(), slot->index());
}
@ -416,7 +424,24 @@ void Cfg::Print() {
void Constant::Print() {
PrintF("Constant(");
handle_->Print();
PrintF(")");
}
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();
}
}
@ -427,9 +452,6 @@ void ReturnInstr::Print() {
}
int CfgNode::node_counter_ = 0;
void InstructionBlock::Print() {
if (!is_marked_) {
is_marked_ = true;

106
src/cfg.h
View File

@ -33,6 +33,47 @@
namespace v8 {
namespace internal {
class ExitNode;
// A convenient class to keep 'global' values when building a CFG. Since
// CFG construction can be invoked recursively, CFG globals are stacked.
class CfgGlobals BASE_EMBEDDED {
public:
explicit CfgGlobals(FunctionLiteral* fun);
~CfgGlobals() { top_ = previous_; }
static CfgGlobals* current() {
ASSERT(top_ != NULL);
return top_;
}
FunctionLiteral* fun() { return global_fun_; }
ExitNode* exit() { return global_exit_; }
#ifdef DEBUG
int next_number() { return node_counter_++; }
#endif
private:
static CfgGlobals* top_;
// Function literal currently compiling.
FunctionLiteral* global_fun_;
// Shared global exit node for all returns from the same function.
ExitNode* global_exit_;
#ifdef DEBUG
// Used to number nodes when printing.
int node_counter_;
#endif
CfgGlobals* previous_;
};
// Values appear in instructions. They represent trivial source
// expressions: ones with no side effects and that do not require code to be
// generated.
@ -66,6 +107,37 @@ class Constant : public Value {
};
// Locations are values that can be stored into ('lvalues').
class Location : public Value {
public:
virtual ~Location() {}
virtual void ToRegister(MacroAssembler* masm, Register reg) = 0;
#ifdef DEBUG
virtual void Print() = 0;
#endif
};
// SlotLocations represent parameters and stack-allocated (i.e.,
// non-context) local variables.
class SlotLocation : public Location {
public:
SlotLocation(Slot::Type type, int index) : type_(type), index_(index) {}
void ToRegister(MacroAssembler* masm, Register reg);
#ifdef DEBUG
void Print();
#endif
private:
Slot::Type type_;
int index_;
};
// Instructions are computations. The represent non-trivial source
// expressions: typically ones that have side effects and require code to
// be generated.
@ -114,8 +186,6 @@ class CfgNode : public ZoneObject {
bool is_marked() { return is_marked_; }
static void Reset();
virtual bool is_block() { return false; }
virtual void Unmark() = 0;
@ -124,7 +194,7 @@ class CfgNode : public ZoneObject {
#ifdef DEBUG
int number() {
if (number_ == -1) number_ = node_counter_++;
if (number_ == -1) number_ = CfgGlobals::current()->next_number();
return number_;
}
@ -136,8 +206,6 @@ class CfgNode : public ZoneObject {
#ifdef DEBUG
int number_;
static int node_counter_;
#endif
};
@ -182,7 +250,7 @@ class InstructionBlock : public CfgNode {
// containing the function's first instruction.
class EntryNode : public CfgNode {
public:
EntryNode(FunctionLiteral* fun, InstructionBlock* succ);
explicit EntryNode(InstructionBlock* succ) : successor_(succ) {}
virtual ~EntryNode() {}
@ -196,7 +264,6 @@ class EntryNode : public CfgNode {
private:
InstructionBlock* successor_;
int local_count_;
};
@ -205,7 +272,7 @@ class EntryNode : public CfgNode {
// the blocks returning from the function.
class ExitNode : public CfgNode {
public:
explicit ExitNode(FunctionLiteral* fun);
ExitNode() {}
virtual ~ExitNode() {}
@ -216,16 +283,13 @@ class ExitNode : public CfgNode {
#ifdef DEBUG
void Print();
#endif
private:
int parameter_count_;
};
// A CFG is a consists of a linked structure of nodes. It has a single
// entry node and optionally an exit node. There is a distinguished global
// exit node that is used as the successor of all blocks that return from
// the function.
// A CFG consists of a linked structure of nodes. It has a single entry
// node and optionally an exit node. There is a distinguished global exit
// node that is used as the successor of all blocks that return from the
// function.
//
// Fragments of control-flow graphs, produced when traversing the statements
// and expressions in the source AST, are represented by the same class.
@ -241,7 +305,7 @@ class Cfg : public ZoneObject {
explicit Cfg(InstructionBlock* block) : entry_(block), exit_(block) {}
// Build the CFG for a function.
static Cfg* Build(FunctionLiteral* fun);
static Cfg* Build();
// The entry and exit nodes.
CfgNode* entry() { return entry_; }
@ -256,7 +320,7 @@ class Cfg : public ZoneObject {
// Add an entry node to a CFG fragment. It is no longer a fragment
// (instructions cannot be prepended).
void PrependEntryNode(FunctionLiteral* fun);
void PrependEntryNode();
// Append an instruction to the end of a CFG fragment. Assumes it has an
// available exit.
@ -266,7 +330,7 @@ class Cfg : public ZoneObject {
// longer has an available exit node.
void AppendReturnInstruction(Value* value);
Handle<Code> Compile(FunctionLiteral* fun, Handle<Script> script);
Handle<Code> Compile(Handle<Script> script);
#ifdef DEBUG
// Support for printing.
@ -274,12 +338,6 @@ class Cfg : public ZoneObject {
#endif
private:
// Reset static variables before building the CFG for a function.
static void Reset(FunctionLiteral* fun);
// Shared global exit nodes for all returns from the same function.
static ExitNode* global_exit_;
// Entry and exit nodes.
CfgNode* entry_;
CfgNode* exit_;

View File

@ -80,7 +80,8 @@ static Handle<Code> MakeCode(FunctionLiteral* literal,
}
if (FLAG_multipass) {
Cfg* cfg = Cfg::Build(literal);
CfgGlobals scope(literal);
Cfg* cfg = Cfg::Build();
#ifdef DEBUG
if (FLAG_print_cfg && cfg != NULL) {
SmartPointer<char> name = literal->name()->ToCString();
@ -90,7 +91,7 @@ static Handle<Code> MakeCode(FunctionLiteral* literal,
}
#endif
if (cfg != NULL) {
return cfg->Compile(literal, script);
return cfg->Compile(script);
}
}

View File

@ -59,9 +59,10 @@ void EntryNode::Compile(MacroAssembler* masm) {
__ mov(ebp, esp);
__ push(esi);
__ push(edi);
if (local_count_ > 0) {
int count = CfgGlobals::current()->fun()->scope()->num_stack_slots();
if (count > 0) {
__ Set(eax, Immediate(Factory::undefined_value()));
for (int i = 0; i < local_count_; i++) {
for (int i = 0; i < count; i++) {
__ push(eax);
}
}
@ -97,7 +98,8 @@ void ExitNode::Compile(MacroAssembler* masm) {
__ RecordJSReturn();
__ mov(esp, ebp);
__ pop(ebp);
__ ret((parameter_count_ + 1) * kPointerSize);
int count = CfgGlobals::current()->fun()->scope()->num_parameters();
__ ret((count + 1) * kPointerSize);
}
@ -111,6 +113,25 @@ void Constant::ToRegister(MacroAssembler* masm, Register reg) {
__ mov(reg, Immediate(handle_));
}
void SlotLocation::ToRegister(MacroAssembler* masm, Register reg) {
switch (type_) {
case Slot::PARAMETER: {
int count = CfgGlobals::current()->fun()->scope()->num_parameters();
__ mov(reg, Operand(ebp, (1 + count - index_) * kPointerSize));
break;
}
case Slot::LOCAL: {
const int kOffset = JavaScriptFrameConstants::kLocal0Offset;
__ mov(reg, Operand(ebp, kOffset - index_ * kPointerSize));
break;
}
default:
UNREACHABLE();
}
}
#undef __
} } // namespace v8::internal

View File

@ -60,10 +60,11 @@ void EntryNode::Compile(MacroAssembler* masm) {
__ movq(rbp, rsp);
__ push(rsi);
__ push(rdi);
if (local_count_ > 0) {
int count = CfgGlobals::current()->fun()->scope()->num_stack_slots();
if (count > 0) {
__ movq(kScratchRegister, Factory::undefined_value(),
RelocInfo::EMBEDDED_OBJECT);
for (int i = 0; i < local_count_; i++) {
for (int i = 0; i < count; i++) {
__ push(kScratchRegister);
}
}
@ -101,7 +102,8 @@ void ExitNode::Compile(MacroAssembler* masm) {
__ RecordJSReturn();
__ movq(rsp, rbp);
__ pop(rbp);
__ ret((parameter_count_ + 1) * kPointerSize);
int count = CfgGlobals::current()->fun()->scope()->num_parameters();
__ ret((count + 1) * kPointerSize);
// Add padding that will be overwritten by a debugger breakpoint.
// "movq rsp, rbp; pop rbp" has length 5. "ret k" has length 2.
const int kPadding = Debug::kX64JSReturnSequenceLength - 5 - 2;
@ -121,6 +123,24 @@ void Constant::ToRegister(MacroAssembler* masm, Register reg) {
__ Move(reg, handle_);
}
void SlotLocation::ToRegister(MacroAssembler* masm, Register reg) {
switch (type_) {
case Slot::PARAMETER: {
int count = CfgGlobals::current()->fun()->scope()->num_parameters();
__ movq(reg, Operand(rbp, (1 + count - index_) * kPointerSize));
break;
}
case Slot::LOCAL: {
const int kOffset = JavaScriptFrameConstants::kLocal0Offset;
__ movq(reg, Operand(rbp, kOffset - index_ * kPointerSize));
break;
}
default:
UNREACHABLE();
}
}
#undef __
} } // namespace v8::internal