mirror of https://github.com/lukechilds/node.git
Ryan
16 years ago
53 changed files with 2256 additions and 231 deletions
@ -0,0 +1,124 @@ |
|||
// 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 "cfg.h" |
|||
#include "codegen-inl.h" |
|||
#include "macro-assembler-arm.h" |
|||
|
|||
namespace v8 { |
|||
namespace internal { |
|||
|
|||
#define __ ACCESS_MASM(masm) |
|||
|
|||
void InstructionBlock::Compile(MacroAssembler* masm) { |
|||
ASSERT(!is_marked()); |
|||
is_marked_ = true; |
|||
{ |
|||
Comment cmt(masm, "[ InstructionBlock"); |
|||
for (int i = 0, len = instructions_.length(); i < len; i++) { |
|||
instructions_[i]->Compile(masm); |
|||
} |
|||
} |
|||
successor_->Compile(masm); |
|||
} |
|||
|
|||
|
|||
void EntryNode::Compile(MacroAssembler* masm) { |
|||
ASSERT(!is_marked()); |
|||
is_marked_ = true; |
|||
{ |
|||
Comment cmnt(masm, "[ EntryNode"); |
|||
__ stm(db_w, sp, r1.bit() | cp.bit() | fp.bit() | lr.bit()); |
|||
__ add(fp, sp, Operand(2 * kPointerSize)); |
|||
int count = CfgGlobals::current()->fun()->scope()->num_stack_slots(); |
|||
if (count > 0) { |
|||
__ mov(ip, Operand(Factory::undefined_value())); |
|||
for (int i = 0; i < count; i++) { |
|||
__ push(ip); |
|||
} |
|||
} |
|||
if (FLAG_trace) { |
|||
__ CallRuntime(Runtime::kTraceEnter, 0); |
|||
} |
|||
if (FLAG_check_stack) { |
|||
StackCheckStub stub; |
|||
__ CallStub(&stub); |
|||
} |
|||
} |
|||
successor_->Compile(masm); |
|||
} |
|||
|
|||
|
|||
void ExitNode::Compile(MacroAssembler* masm) { |
|||
ASSERT(!is_marked()); |
|||
is_marked_ = true; |
|||
Comment cmnt(masm, "[ ExitNode"); |
|||
if (FLAG_trace) { |
|||
__ push(r0); |
|||
__ CallRuntime(Runtime::kTraceExit, 1); |
|||
} |
|||
__ mov(sp, fp); |
|||
__ ldm(ia_w, sp, fp.bit() | lr.bit()); |
|||
int count = CfgGlobals::current()->fun()->scope()->num_parameters(); |
|||
__ add(sp, sp, Operand((count + 1) * kPointerSize)); |
|||
__ Jump(lr); |
|||
} |
|||
|
|||
|
|||
void ReturnInstr::Compile(MacroAssembler* masm) { |
|||
Comment cmnt(masm, "[ ReturnInstr"); |
|||
value_->ToRegister(masm, r0); |
|||
} |
|||
|
|||
|
|||
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
|
@ -0,0 +1,485 @@ |
|||
// 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()), |
|||
#ifdef DEBUG |
|||
node_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()->arguments() != NULL) { |
|||
BAILOUT("function uses .arguments"); |
|||
} |
|||
|
|||
ZoneList<Statement*>* body = fun->body(); |
|||
if (body->is_empty()) { |
|||
BAILOUT("empty function body"); |
|||
} |
|||
|
|||
StatementBuilder builder; |
|||
builder.VisitStatements(body); |
|||
Cfg* cfg = builder.cfg(); |
|||
if (cfg == NULL) { |
|||
BAILOUT("unsupported statement type"); |
|||
} |
|||
if (cfg->has_exit()) { |
|||
BAILOUT("control path without explicit return"); |
|||
} |
|||
cfg->PrependEntryNode(); |
|||
return cfg; |
|||
} |
|||
|
|||
#undef BAILOUT |
|||
|
|||
|
|||
void Cfg::PrependEntryNode() { |
|||
ASSERT(!is_empty()); |
|||
entry_ = new EntryNode(InstructionBlock::cast(entry())); |
|||
} |
|||
|
|||
|
|||
void Cfg::Append(Instruction* instr) { |
|||
ASSERT(has_exit()); |
|||
ASSERT(!is_empty()); |
|||
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 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; |
|||
} |
|||
|
|||
|
|||
// The expression builder should not be used for declarations or statements.
|
|||
void ExpressionBuilder::VisitDeclaration(Declaration* decl) { UNREACHABLE(); } |
|||
|
|||
#define DEFINE_VISIT(type) \ |
|||
void ExpressionBuilder::Visit##type(type* stmt) { UNREACHABLE(); } |
|||
STATEMENT_NODE_LIST(DEFINE_VISIT) |
|||
#undef DEFINE_VISIT |
|||
|
|||
|
|||
// Macros (temporarily) handling unsupported expression types.
|
|||
#define BAILOUT(reason) \ |
|||
do { \ |
|||
value_ = NULL; \ |
|||
return; \ |
|||
} while (false) |
|||
|
|||
#define CHECK_BAILOUT() \ |
|||
if (value_ == NULL) { return; } else {} |
|||
|
|||
void ExpressionBuilder::VisitFunctionLiteral(FunctionLiteral* expr) { |
|||
BAILOUT("FunctionLiteral"); |
|||
} |
|||
|
|||
|
|||
void ExpressionBuilder::VisitFunctionBoilerplateLiteral( |
|||
FunctionBoilerplateLiteral* expr) { |
|||
BAILOUT("FunctionBoilerplateLiteral"); |
|||
} |
|||
|
|||
|
|||
void ExpressionBuilder::VisitConditional(Conditional* expr) { |
|||
BAILOUT("Conditional"); |
|||
} |
|||
|
|||
|
|||
void ExpressionBuilder::VisitSlot(Slot* expr) { |
|||
BAILOUT("Slot"); |
|||
} |
|||
|
|||
|
|||
void ExpressionBuilder::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)"); |
|||
} |
|||
value_ = new SlotLocation(slot->type(), slot->index()); |
|||
} |
|||
|
|||
|
|||
void ExpressionBuilder::VisitLiteral(Literal* expr) { |
|||
value_ = new Constant(expr->handle()); |
|||
} |
|||
|
|||
|
|||
void ExpressionBuilder::VisitRegExpLiteral(RegExpLiteral* expr) { |
|||
BAILOUT("RegExpLiteral"); |
|||
} |
|||
|
|||
|
|||
void ExpressionBuilder::VisitObjectLiteral(ObjectLiteral* expr) { |
|||
BAILOUT("ObjectLiteral"); |
|||
} |
|||
|
|||
|
|||
void ExpressionBuilder::VisitArrayLiteral(ArrayLiteral* expr) { |
|||
BAILOUT("ArrayLiteral"); |
|||
} |
|||
|
|||
|
|||
void ExpressionBuilder::VisitCatchExtensionObject(CatchExtensionObject* expr) { |
|||
BAILOUT("CatchExtensionObject"); |
|||
} |
|||
|
|||
|
|||
void ExpressionBuilder::VisitAssignment(Assignment* expr) { |
|||
BAILOUT("Assignment"); |
|||
} |
|||
|
|||
|
|||
void ExpressionBuilder::VisitThrow(Throw* expr) { |
|||
BAILOUT("Throw"); |
|||
} |
|||
|
|||
|
|||
void ExpressionBuilder::VisitProperty(Property* expr) { |
|||
BAILOUT("Property"); |
|||
} |
|||
|
|||
|
|||
void ExpressionBuilder::VisitCall(Call* expr) { |
|||
BAILOUT("Call"); |
|||
} |
|||
|
|||
|
|||
void ExpressionBuilder::VisitCallEval(CallEval* expr) { |
|||
BAILOUT("CallEval"); |
|||
} |
|||
|
|||
|
|||
void ExpressionBuilder::VisitCallNew(CallNew* expr) { |
|||
BAILOUT("CallNew"); |
|||
} |
|||
|
|||
|
|||
void ExpressionBuilder::VisitCallRuntime(CallRuntime* expr) { |
|||
BAILOUT("CallRuntime"); |
|||
} |
|||
|
|||
|
|||
void ExpressionBuilder::VisitUnaryOperation(UnaryOperation* expr) { |
|||
BAILOUT("UnaryOperation"); |
|||
} |
|||
|
|||
|
|||
void ExpressionBuilder::VisitCountOperation(CountOperation* expr) { |
|||
BAILOUT("CountOperation"); |
|||
} |
|||
|
|||
|
|||
void ExpressionBuilder::VisitBinaryOperation(BinaryOperation* expr) { |
|||
BAILOUT("BinaryOperation"); |
|||
} |
|||
|
|||
|
|||
void ExpressionBuilder::VisitCompareOperation(CompareOperation* expr) { |
|||
BAILOUT("CompareOperation"); |
|||
} |
|||
|
|||
|
|||
void ExpressionBuilder::VisitThisFunction(ThisFunction* expr) { |
|||
BAILOUT("ThisFunction"); |
|||
} |
|||
|
|||
#undef BAILOUT |
|||
#undef CHECK_BAILOUT |
|||
|
|||
|
|||
// Macros (temporarily) handling unsupported statement types.
|
|||
#define BAILOUT(reason) \ |
|||
do { \ |
|||
cfg_ = NULL; \ |
|||
return; \ |
|||
} while (false) |
|||
|
|||
#define CHECK_BAILOUT() \ |
|||
if (cfg_ == NULL) { return; } else {} |
|||
|
|||
void StatementBuilder::VisitStatements(ZoneList<Statement*>* stmts) { |
|||
for (int i = 0, len = stmts->length(); i < len; i++) { |
|||
Visit(stmts->at(i)); |
|||
CHECK_BAILOUT(); |
|||
if (!cfg_->has_exit()) return; |
|||
} |
|||
} |
|||
|
|||
|
|||
// The statement builder should not be used for declarations or expressions.
|
|||
void StatementBuilder::VisitDeclaration(Declaration* decl) { UNREACHABLE(); } |
|||
|
|||
#define DEFINE_VISIT(type) \ |
|||
void StatementBuilder::Visit##type(type* expr) { UNREACHABLE(); } |
|||
EXPRESSION_NODE_LIST(DEFINE_VISIT) |
|||
#undef DEFINE_VISIT |
|||
|
|||
|
|||
void StatementBuilder::VisitBlock(Block* stmt) { |
|||
VisitStatements(stmt->statements()); |
|||
} |
|||
|
|||
|
|||
void StatementBuilder::VisitExpressionStatement(ExpressionStatement* stmt) { |
|||
BAILOUT("ExpressionStatement"); |
|||
} |
|||
|
|||
|
|||
void StatementBuilder::VisitEmptyStatement(EmptyStatement* stmt) { |
|||
// Nothing to do.
|
|||
} |
|||
|
|||
|
|||
void StatementBuilder::VisitIfStatement(IfStatement* stmt) { |
|||
BAILOUT("IfStatement"); |
|||
} |
|||
|
|||
|
|||
void StatementBuilder::VisitContinueStatement(ContinueStatement* stmt) { |
|||
BAILOUT("ContinueStatement"); |
|||
} |
|||
|
|||
|
|||
void StatementBuilder::VisitBreakStatement(BreakStatement* stmt) { |
|||
BAILOUT("BreakStatement"); |
|||
} |
|||
|
|||
|
|||
void StatementBuilder::VisitReturnStatement(ReturnStatement* stmt) { |
|||
ExpressionBuilder builder; |
|||
builder.Visit(stmt->expression()); |
|||
Value* value = builder.value(); |
|||
if (value == NULL) BAILOUT("unsupported expression type"); |
|||
cfg_->AppendReturnInstruction(value); |
|||
} |
|||
|
|||
|
|||
void StatementBuilder::VisitWithEnterStatement(WithEnterStatement* stmt) { |
|||
BAILOUT("WithEnterStatement"); |
|||
} |
|||
|
|||
|
|||
void StatementBuilder::VisitWithExitStatement(WithExitStatement* stmt) { |
|||
BAILOUT("WithExitStatement"); |
|||
} |
|||
|
|||
|
|||
void StatementBuilder::VisitSwitchStatement(SwitchStatement* stmt) { |
|||
BAILOUT("SwitchStatement"); |
|||
} |
|||
|
|||
|
|||
void StatementBuilder::VisitLoopStatement(LoopStatement* stmt) { |
|||
BAILOUT("LoopStatement"); |
|||
} |
|||
|
|||
|
|||
void StatementBuilder::VisitForInStatement(ForInStatement* stmt) { |
|||
BAILOUT("ForInStatement"); |
|||
} |
|||
|
|||
|
|||
void StatementBuilder::VisitTryCatch(TryCatch* stmt) { |
|||
BAILOUT("TryCatch"); |
|||
} |
|||
|
|||
|
|||
void StatementBuilder::VisitTryFinally(TryFinally* stmt) { |
|||
BAILOUT("TryFinally"); |
|||
} |
|||
|
|||
|
|||
void StatementBuilder::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(); |
|||
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(); |
|||
} |
|||
} |
|||
|
|||
|
|||
void ReturnInstr::Print() { |
|||
PrintF("Return "); |
|||
value_->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
|
@ -0,0 +1,385 @@ |
|||
// 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.
|
|||
|
|||
#ifndef V8_CFG_H_ |
|||
#define V8_CFG_H_ |
|||
|
|||
#include "ast.h" |
|||
|
|||
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.
|
|||
class Value : public ZoneObject { |
|||
public: |
|||
virtual ~Value() {} |
|||
|
|||
virtual void ToRegister(MacroAssembler* masm, Register reg) = 0; |
|||
|
|||
#ifdef DEBUG |
|||
virtual void Print() = 0; |
|||
#endif |
|||
}; |
|||
|
|||
|
|||
// A compile-time constant that appeared as a literal in the source AST.
|
|||
class Constant : public Value { |
|||
public: |
|||
explicit Constant(Handle<Object> handle) : handle_(handle) {} |
|||
|
|||
virtual ~Constant() {} |
|||
|
|||
void ToRegister(MacroAssembler* masm, Register reg); |
|||
|
|||
#ifdef DEBUG |
|||
void Print(); |
|||
#endif |
|||
|
|||
private: |
|||
Handle<Object> handle_; |
|||
}; |
|||
|
|||
|
|||
// 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.
|
|||
class Instruction : public ZoneObject { |
|||
public: |
|||
virtual ~Instruction() {} |
|||
|
|||
virtual void Compile(MacroAssembler* masm) = 0; |
|||
|
|||
#ifdef DEBUG |
|||
virtual void Print() = 0; |
|||
#endif |
|||
}; |
|||
|
|||
|
|||
// Return a value.
|
|||
class ReturnInstr : public Instruction { |
|||
public: |
|||
explicit ReturnInstr(Value* value) : value_(value) {} |
|||
|
|||
virtual ~ReturnInstr() {} |
|||
|
|||
void Compile(MacroAssembler* masm); |
|||
|
|||
#ifdef DEBUG |
|||
void Print(); |
|||
#endif |
|||
|
|||
private: |
|||
Value* value_; |
|||
}; |
|||
|
|||
|
|||
// Nodes make up control-flow graphs. They contain single-entry,
|
|||
// single-exit blocks of instructions and administrative nodes making up the
|
|||
// graph structure.
|
|||
class CfgNode : public ZoneObject { |
|||
public: |
|||
CfgNode() : is_marked_(false) { |
|||
#ifdef DEBUG |
|||
number_ = -1; |
|||
#endif |
|||
} |
|||
|
|||
virtual ~CfgNode() {} |
|||
|
|||
bool is_marked() { return is_marked_; } |
|||
|
|||
virtual bool is_block() { return false; } |
|||
|
|||
virtual void Unmark() = 0; |
|||
|
|||
virtual void Compile(MacroAssembler* masm) = 0; |
|||
|
|||
#ifdef DEBUG |
|||
int number() { |
|||
if (number_ == -1) number_ = CfgGlobals::current()->next_number(); |
|||
return number_; |
|||
} |
|||
|
|||
virtual void Print() = 0; |
|||
#endif |
|||
|
|||
protected: |
|||
bool is_marked_; |
|||
|
|||
#ifdef DEBUG |
|||
int number_; |
|||
#endif |
|||
}; |
|||
|
|||
|
|||
// A block is a single-entry, single-exit block of instructions.
|
|||
class InstructionBlock : public CfgNode { |
|||
public: |
|||
InstructionBlock() : successor_(NULL), instructions_(4) {} |
|||
|
|||
virtual ~InstructionBlock() {} |
|||
|
|||
static InstructionBlock* cast(CfgNode* node) { |
|||
ASSERT(node->is_block()); |
|||
return reinterpret_cast<InstructionBlock*>(node); |
|||
} |
|||
|
|||
void set_successor(CfgNode* succ) { |
|||
ASSERT(successor_ == NULL); |
|||
successor_ = succ; |
|||
} |
|||
|
|||
bool is_block() { return true; } |
|||
|
|||
void Unmark(); |
|||
|
|||
void Compile(MacroAssembler* masm); |
|||
|
|||
void Append(Instruction* instr) { instructions_.Add(instr); } |
|||
|
|||
#ifdef DEBUG |
|||
void Print(); |
|||
#endif |
|||
|
|||
private: |
|||
CfgNode* successor_; |
|||
ZoneList<Instruction*> instructions_; |
|||
}; |
|||
|
|||
|
|||
// The CFG for a function has a distinguished entry node. It has no
|
|||
// predecessors and a single successor. The successor is the block
|
|||
// containing the function's first instruction.
|
|||
class EntryNode : public CfgNode { |
|||
public: |
|||
explicit EntryNode(InstructionBlock* succ) : successor_(succ) {} |
|||
|
|||
virtual ~EntryNode() {} |
|||
|
|||
void Unmark(); |
|||
|
|||
void Compile(MacroAssembler* masm); |
|||
|
|||
#ifdef DEBUG |
|||
void Print(); |
|||
#endif |
|||
|
|||
private: |
|||
InstructionBlock* successor_; |
|||
}; |
|||
|
|||
|
|||
// The CFG for a function has a distinguished exit node. It has no
|
|||
// successor and arbitrarily many predecessors. The predecessors are all
|
|||
// the blocks returning from the function.
|
|||
class ExitNode : public CfgNode { |
|||
public: |
|||
ExitNode() {} |
|||
|
|||
virtual ~ExitNode() {} |
|||
|
|||
void Unmark(); |
|||
|
|||
void Compile(MacroAssembler* masm); |
|||
|
|||
#ifdef DEBUG |
|||
void Print(); |
|||
#endif |
|||
}; |
|||
|
|||
|
|||
// 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.
|
|||
// They have instruction blocks as both their entry and exit (if there is
|
|||
// one). Instructions can always be prepended or appended to fragments, and
|
|||
// fragments can always be concatenated.
|
|||
//
|
|||
// A singleton CFG fragment (i.e., with only one node) has the same node as
|
|||
// both entry and exit (if the exit is available).
|
|||
class Cfg : public ZoneObject { |
|||
public: |
|||
// Create a singleton CFG fragment.
|
|||
explicit Cfg(InstructionBlock* block) : entry_(block), exit_(block) {} |
|||
|
|||
// Build the CFG for a function.
|
|||
static Cfg* Build(); |
|||
|
|||
// The entry and exit nodes.
|
|||
CfgNode* entry() { return entry_; } |
|||
CfgNode* exit() { return exit_; } |
|||
|
|||
// True if the CFG has no nodes.
|
|||
bool is_empty() { return entry_ == NULL; } |
|||
|
|||
// True if the CFG has an available exit node (i.e., it can be appended or
|
|||
// concatenated to).
|
|||
bool has_exit() { return exit_ != NULL; } |
|||
|
|||
// Add an entry node to a CFG fragment. It is no longer a fragment
|
|||
// (instructions cannot be prepended).
|
|||
void PrependEntryNode(); |
|||
|
|||
// Append an instruction to the end of a CFG fragment. Assumes it has an
|
|||
// available exit.
|
|||
void Append(Instruction* instr); |
|||
|
|||
// Appends a return instruction to the end of a CFG fragment. It no
|
|||
// longer has an available exit node.
|
|||
void AppendReturnInstruction(Value* value); |
|||
|
|||
Handle<Code> Compile(Handle<Script> script); |
|||
|
|||
#ifdef DEBUG |
|||
// Support for printing.
|
|||
void Print(); |
|||
#endif |
|||
|
|||
private: |
|||
// Entry and exit nodes.
|
|||
CfgNode* entry_; |
|||
CfgNode* exit_; |
|||
}; |
|||
|
|||
|
|||
// An Expression Builder traverses a trivial expression and returns a value.
|
|||
class ExpressionBuilder : public AstVisitor { |
|||
public: |
|||
ExpressionBuilder() : value_(new Constant(Handle<Object>::null())) {} |
|||
|
|||
Value* value() { return value_; } |
|||
|
|||
// AST node visitors.
|
|||
#define DECLARE_VISIT(type) void Visit##type(type* node); |
|||
AST_NODE_LIST(DECLARE_VISIT) |
|||
#undef DECLARE_VISIT |
|||
|
|||
private: |
|||
Value* value_; |
|||
}; |
|||
|
|||
|
|||
// A StatementBuilder traverses a statement and returns a CFG.
|
|||
class StatementBuilder : public AstVisitor { |
|||
public: |
|||
StatementBuilder() : cfg_(new Cfg(new InstructionBlock())) {} |
|||
|
|||
Cfg* cfg() { return cfg_; } |
|||
|
|||
void VisitStatements(ZoneList<Statement*>* stmts); |
|||
|
|||
// AST node visitors.
|
|||
#define DECLARE_VISIT(type) void Visit##type(type* node); |
|||
AST_NODE_LIST(DECLARE_VISIT) |
|||
#undef DECLARE_VISIT |
|||
|
|||
private: |
|||
Cfg* cfg_; |
|||
}; |
|||
|
|||
|
|||
} } // namespace v8::internal
|
|||
|
|||
#endif // V8_CFG_H_
|
@ -0,0 +1,137 @@ |
|||
// 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 "cfg.h" |
|||
#include "codegen-inl.h" |
|||
#include "macro-assembler-ia32.h" |
|||
|
|||
namespace v8 { |
|||
namespace internal { |
|||
|
|||
#define __ ACCESS_MASM(masm) |
|||
|
|||
void InstructionBlock::Compile(MacroAssembler* masm) { |
|||
ASSERT(!is_marked()); |
|||
is_marked_ = true; |
|||
{ |
|||
Comment cmt(masm, "[ InstructionBlock"); |
|||
for (int i = 0, len = instructions_.length(); i < len; i++) { |
|||
instructions_[i]->Compile(masm); |
|||
} |
|||
} |
|||
successor_->Compile(masm); |
|||
} |
|||
|
|||
|
|||
void EntryNode::Compile(MacroAssembler* masm) { |
|||
ASSERT(!is_marked()); |
|||
is_marked_ = true; |
|||
Label deferred_enter, deferred_exit; |
|||
{ |
|||
Comment cmnt(masm, "[ EntryNode"); |
|||
__ push(ebp); |
|||
__ mov(ebp, esp); |
|||
__ push(esi); |
|||
__ push(edi); |
|||
int count = CfgGlobals::current()->fun()->scope()->num_stack_slots(); |
|||
if (count > 0) { |
|||
__ Set(eax, Immediate(Factory::undefined_value())); |
|||
for (int i = 0; i < count; i++) { |
|||
__ push(eax); |
|||
} |
|||
} |
|||
if (FLAG_trace) { |
|||
__ CallRuntime(Runtime::kTraceEnter, 0); |
|||
} |
|||
if (FLAG_check_stack) { |
|||
ExternalReference stack_limit = |
|||
ExternalReference::address_of_stack_guard_limit(); |
|||
__ cmp(esp, Operand::StaticVariable(stack_limit)); |
|||
__ j(below, &deferred_enter); |
|||
__ bind(&deferred_exit); |
|||
} |
|||
} |
|||
successor_->Compile(masm); |
|||
if (FLAG_check_stack) { |
|||
__ bind(&deferred_enter); |
|||
StackCheckStub stub; |
|||
__ CallStub(&stub); |
|||
__ jmp(&deferred_exit); |
|||
} |
|||
} |
|||
|
|||
|
|||
void ExitNode::Compile(MacroAssembler* masm) { |
|||
ASSERT(!is_marked()); |
|||
is_marked_ = true; |
|||
Comment cmnt(masm, "[ ExitNode"); |
|||
if (FLAG_trace) { |
|||
__ push(eax); |
|||
__ CallRuntime(Runtime::kTraceExit, 1); |
|||
} |
|||
__ RecordJSReturn(); |
|||
__ mov(esp, ebp); |
|||
__ pop(ebp); |
|||
int count = CfgGlobals::current()->fun()->scope()->num_parameters(); |
|||
__ ret((count + 1) * kPointerSize); |
|||
} |
|||
|
|||
|
|||
void ReturnInstr::Compile(MacroAssembler* masm) { |
|||
Comment cmnt(masm, "[ ReturnInstr"); |
|||
value_->ToRegister(masm, eax); |
|||
} |
|||
|
|||
|
|||
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
|
@ -0,0 +1,146 @@ |
|||
// 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 "cfg.h" |
|||
#include "codegen-inl.h" |
|||
#include "debug.h" |
|||
#include "macro-assembler-x64.h" |
|||
|
|||
namespace v8 { |
|||
namespace internal { |
|||
|
|||
#define __ ACCESS_MASM(masm) |
|||
|
|||
void InstructionBlock::Compile(MacroAssembler* masm) { |
|||
ASSERT(!is_marked()); |
|||
is_marked_ = true; |
|||
{ |
|||
Comment cmt(masm, "[ InstructionBlock"); |
|||
for (int i = 0, len = instructions_.length(); i < len; i++) { |
|||
instructions_[i]->Compile(masm); |
|||
} |
|||
} |
|||
successor_->Compile(masm); |
|||
} |
|||
|
|||
|
|||
void EntryNode::Compile(MacroAssembler* masm) { |
|||
ASSERT(!is_marked()); |
|||
is_marked_ = true; |
|||
Label deferred_enter, deferred_exit; |
|||
{ |
|||
Comment cmnt(masm, "[ EntryNode"); |
|||
__ push(rbp); |
|||
__ movq(rbp, rsp); |
|||
__ push(rsi); |
|||
__ push(rdi); |
|||
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 < count; i++) { |
|||
__ push(kScratchRegister); |
|||
} |
|||
} |
|||
if (FLAG_trace) { |
|||
__ CallRuntime(Runtime::kTraceEnter, 0); |
|||
} |
|||
if (FLAG_check_stack) { |
|||
ExternalReference stack_limit = |
|||
ExternalReference::address_of_stack_guard_limit(); |
|||
__ movq(kScratchRegister, stack_limit); |
|||
__ cmpq(rsp, Operand(kScratchRegister, 0)); |
|||
__ j(below, &deferred_enter); |
|||
__ bind(&deferred_exit); |
|||
} |
|||
} |
|||
successor_->Compile(masm); |
|||
if (FLAG_check_stack) { |
|||
__ bind(&deferred_enter); |
|||
StackCheckStub stub; |
|||
__ CallStub(&stub); |
|||
__ jmp(&deferred_exit); |
|||
} |
|||
} |
|||
|
|||
|
|||
void ExitNode::Compile(MacroAssembler* masm) { |
|||
ASSERT(!is_marked()); |
|||
is_marked_ = true; |
|||
|
|||
Comment cmnt(masm, "[ ExitNode"); |
|||
if (FLAG_trace) { |
|||
__ push(rax); |
|||
__ CallRuntime(Runtime::kTraceExit, 1); |
|||
} |
|||
__ RecordJSReturn(); |
|||
__ movq(rsp, rbp); |
|||
__ pop(rbp); |
|||
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; |
|||
for (int i = 0; i < kPadding; ++i) { |
|||
__ int3(); |
|||
} |
|||
} |
|||
|
|||
|
|||
void ReturnInstr::Compile(MacroAssembler* masm) { |
|||
Comment cmnt(masm, "[ ReturnInstr"); |
|||
value_->ToRegister(masm, rax); |
|||
} |
|||
|
|||
|
|||
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
|
@ -0,0 +1,376 @@ |
|||
// Copyright 2006-2009 the V8 project authors. All rights reserved.
|
|||
//
|
|||
// Tests of profiler-related functions from log.h
|
|||
|
|||
#ifdef ENABLE_LOGGING_AND_PROFILING |
|||
|
|||
#include <stdlib.h> |
|||
|
|||
#include "v8.h" |
|||
|
|||
#include "codegen.h" |
|||
#include "log.h" |
|||
#include "top.h" |
|||
#include "cctest.h" |
|||
#include "disassembler.h" |
|||
#include "register-allocator-inl.h" |
|||
|
|||
using v8::Function; |
|||
using v8::Local; |
|||
using v8::Object; |
|||
using v8::Script; |
|||
using v8::String; |
|||
using v8::Value; |
|||
|
|||
using v8::internal::byte; |
|||
using v8::internal::Address; |
|||
using v8::internal::Handle; |
|||
using v8::internal::JSFunction; |
|||
using v8::internal::StackTracer; |
|||
using v8::internal::TickSample; |
|||
using v8::internal::Top; |
|||
|
|||
namespace i = v8::internal; |
|||
|
|||
|
|||
static v8::Persistent<v8::Context> env; |
|||
|
|||
|
|||
static struct { |
|||
TickSample* sample; |
|||
} trace_env = { NULL }; |
|||
|
|||
|
|||
static void InitTraceEnv(TickSample* sample) { |
|||
trace_env.sample = sample; |
|||
} |
|||
|
|||
|
|||
static void DoTrace(Address fp) { |
|||
trace_env.sample->fp = reinterpret_cast<uintptr_t>(fp); |
|||
// sp is only used to define stack high bound
|
|||
trace_env.sample->sp = |
|||
reinterpret_cast<uintptr_t>(trace_env.sample) - 10240; |
|||
StackTracer::Trace(trace_env.sample); |
|||
} |
|||
|
|||
|
|||
// Hide c_entry_fp to emulate situation when sampling is done while
|
|||
// pure JS code is being executed
|
|||
static void DoTraceHideCEntryFPAddress(Address fp) { |
|||
v8::internal::Address saved_c_frame_fp = *(Top::c_entry_fp_address()); |
|||
CHECK(saved_c_frame_fp); |
|||
*(Top::c_entry_fp_address()) = 0; |
|||
DoTrace(fp); |
|||
*(Top::c_entry_fp_address()) = saved_c_frame_fp; |
|||
} |
|||
|
|||
|
|||
static void CheckRetAddrIsInFunction(const char* func_name, |
|||
Address ret_addr, |
|||
Address func_start_addr, |
|||
unsigned int func_len) { |
|||
printf("CheckRetAddrIsInFunction \"%s\": %p %p %p\n", |
|||
func_name, func_start_addr, ret_addr, func_start_addr + func_len); |
|||
CHECK_GE(ret_addr, func_start_addr); |
|||
CHECK_GE(func_start_addr + func_len, ret_addr); |
|||
} |
|||
|
|||
|
|||
static void CheckRetAddrIsInJSFunction(const char* func_name, |
|||
Address ret_addr, |
|||
Handle<JSFunction> func) { |
|||
v8::internal::Code* func_code = func->code(); |
|||
CheckRetAddrIsInFunction( |
|||
func_name, ret_addr, |
|||
func_code->instruction_start(), |
|||
func_code->ExecutableSize()); |
|||
} |
|||
|
|||
|
|||
// --- T r a c e E x t e n s i o n ---
|
|||
|
|||
class TraceExtension : public v8::Extension { |
|||
public: |
|||
TraceExtension() : v8::Extension("v8/trace", kSource) { } |
|||
virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction( |
|||
v8::Handle<String> name); |
|||
static v8::Handle<v8::Value> Trace(const v8::Arguments& args); |
|||
static v8::Handle<v8::Value> JSTrace(const v8::Arguments& args); |
|||
static v8::Handle<v8::Value> JSEntrySP(const v8::Arguments& args); |
|||
static v8::Handle<v8::Value> JSEntrySPLevel2(const v8::Arguments& args); |
|||
private: |
|||
static Address GetFP(const v8::Arguments& args); |
|||
static const char* kSource; |
|||
}; |
|||
|
|||
|
|||
const char* TraceExtension::kSource = |
|||
"native function trace();" |
|||
"native function js_trace();" |
|||
"native function js_entry_sp();" |
|||
"native function js_entry_sp_level2();"; |
|||
|
|||
v8::Handle<v8::FunctionTemplate> TraceExtension::GetNativeFunction( |
|||
v8::Handle<String> name) { |
|||
if (name->Equals(String::New("trace"))) { |
|||
return v8::FunctionTemplate::New(TraceExtension::Trace); |
|||
} else if (name->Equals(String::New("js_trace"))) { |
|||
return v8::FunctionTemplate::New(TraceExtension::JSTrace); |
|||
} else if (name->Equals(String::New("js_entry_sp"))) { |
|||
return v8::FunctionTemplate::New(TraceExtension::JSEntrySP); |
|||
} else if (name->Equals(String::New("js_entry_sp_level2"))) { |
|||
return v8::FunctionTemplate::New(TraceExtension::JSEntrySPLevel2); |
|||
} else { |
|||
CHECK(false); |
|||
return v8::Handle<v8::FunctionTemplate>(); |
|||
} |
|||
} |
|||
|
|||
|
|||
Address TraceExtension::GetFP(const v8::Arguments& args) { |
|||
CHECK_EQ(1, args.Length()); |
|||
// CodeGenerator::GenerateGetFramePointer pushes EBP / RBP value
|
|||
// on stack. In 64-bit mode we can't use Smi operations code because
|
|||
// they check that value is within Smi bounds.
|
|||
Address fp = *reinterpret_cast<Address*>(*args[0]); |
|||
printf("Trace: %p\n", fp); |
|||
return fp; |
|||
} |
|||
|
|||
|
|||
v8::Handle<v8::Value> TraceExtension::Trace(const v8::Arguments& args) { |
|||
DoTrace(GetFP(args)); |
|||
return v8::Undefined(); |
|||
} |
|||
|
|||
|
|||
v8::Handle<v8::Value> TraceExtension::JSTrace(const v8::Arguments& args) { |
|||
DoTraceHideCEntryFPAddress(GetFP(args)); |
|||
return v8::Undefined(); |
|||
} |
|||
|
|||
|
|||
static Address GetJsEntrySp() { |
|||
CHECK_NE(NULL, Top::GetCurrentThread()); |
|||
return Top::js_entry_sp(Top::GetCurrentThread()); |
|||
} |
|||
|
|||
|
|||
v8::Handle<v8::Value> TraceExtension::JSEntrySP(const v8::Arguments& args) { |
|||
CHECK_NE(0, GetJsEntrySp()); |
|||
return v8::Undefined(); |
|||
} |
|||
|
|||
|
|||
static void CompileRun(const char* source) { |
|||
Script::Compile(String::New(source))->Run(); |
|||
} |
|||
|
|||
|
|||
v8::Handle<v8::Value> TraceExtension::JSEntrySPLevel2( |
|||
const v8::Arguments& args) { |
|||
v8::HandleScope scope; |
|||
const Address js_entry_sp = GetJsEntrySp(); |
|||
CHECK_NE(0, js_entry_sp); |
|||
CompileRun("js_entry_sp();"); |
|||
CHECK_EQ(js_entry_sp, GetJsEntrySp()); |
|||
return v8::Undefined(); |
|||
} |
|||
|
|||
|
|||
static TraceExtension kTraceExtension; |
|||
v8::DeclareExtension kTraceExtensionDeclaration(&kTraceExtension); |
|||
|
|||
|
|||
static void InitializeVM() { |
|||
if (env.IsEmpty()) { |
|||
v8::HandleScope scope; |
|||
const char* extensions[] = { "v8/trace" }; |
|||
v8::ExtensionConfiguration config(1, extensions); |
|||
env = v8::Context::New(&config); |
|||
} |
|||
v8::HandleScope scope; |
|||
env->Enter(); |
|||
} |
|||
|
|||
|
|||
static Handle<JSFunction> CompileFunction(const char* source) { |
|||
return v8::Utils::OpenHandle(*Script::Compile(String::New(source))); |
|||
} |
|||
|
|||
|
|||
static Local<Value> GetGlobalProperty(const char* name) { |
|||
return env->Global()->Get(String::New(name)); |
|||
} |
|||
|
|||
|
|||
static Handle<JSFunction> GetGlobalJSFunction(const char* name) { |
|||
Handle<JSFunction> js_func(JSFunction::cast( |
|||
*(v8::Utils::OpenHandle( |
|||
*GetGlobalProperty(name))))); |
|||
return js_func; |
|||
} |
|||
|
|||
|
|||
static void CheckRetAddrIsInJSFunction(const char* func_name, |
|||
Address ret_addr) { |
|||
CheckRetAddrIsInJSFunction(func_name, ret_addr, |
|||
GetGlobalJSFunction(func_name)); |
|||
} |
|||
|
|||
|
|||
static void SetGlobalProperty(const char* name, Local<Value> value) { |
|||
env->Global()->Set(String::New(name), value); |
|||
} |
|||
|
|||
|
|||
static Handle<v8::internal::String> NewString(const char* s) { |
|||
return i::Factory::NewStringFromAscii(i::CStrVector(s)); |
|||
} |
|||
|
|||
|
|||
namespace v8 { |
|||
namespace internal { |
|||
|
|||
class CodeGeneratorPatcher { |
|||
public: |
|||
CodeGeneratorPatcher() { |
|||
CodeGenerator::InlineRuntimeLUT genGetFramePointer = |
|||
{&CodeGenerator::GenerateGetFramePointer, "_GetFramePointer"}; |
|||
// _FastCharCodeAt is not used in our tests.
|
|||
bool result = CodeGenerator::PatchInlineRuntimeEntry( |
|||
NewString("_FastCharCodeAt"), |
|||
genGetFramePointer, &oldInlineEntry); |
|||
CHECK(result); |
|||
} |
|||
|
|||
~CodeGeneratorPatcher() { |
|||
CHECK(CodeGenerator::PatchInlineRuntimeEntry( |
|||
NewString("_GetFramePointer"), |
|||
oldInlineEntry, NULL)); |
|||
} |
|||
|
|||
private: |
|||
CodeGenerator::InlineRuntimeLUT oldInlineEntry; |
|||
}; |
|||
|
|||
} } // namespace v8::internal
|
|||
|
|||
|
|||
// Creates a global function named 'func_name' that calls the tracing
|
|||
// function 'trace_func_name' with an actual EBP register value,
|
|||
// shifted right to be presented as Smi.
|
|||
static void CreateTraceCallerFunction(const char* func_name, |
|||
const char* trace_func_name) { |
|||
i::EmbeddedVector<char, 256> trace_call_buf; |
|||
i::OS::SNPrintF(trace_call_buf, "%s(%%_GetFramePointer());", trace_func_name); |
|||
|
|||
// Compile the script.
|
|||
i::CodeGeneratorPatcher patcher; |
|||
bool allow_natives_syntax = i::FLAG_allow_natives_syntax; |
|||
i::FLAG_allow_natives_syntax = true; |
|||
Handle<JSFunction> func = CompileFunction(trace_call_buf.start()); |
|||
CHECK(!func.is_null()); |
|||
i::FLAG_allow_natives_syntax = allow_natives_syntax; |
|||
|
|||
#ifdef DEBUG |
|||
v8::internal::Code* func_code = func->code(); |
|||
CHECK(func_code->IsCode()); |
|||
func_code->Print(); |
|||
#endif |
|||
|
|||
SetGlobalProperty(func_name, v8::ToApi<Value>(func)); |
|||
} |
|||
|
|||
|
|||
TEST(CFromJSStackTrace) { |
|||
TickSample sample; |
|||
InitTraceEnv(&sample); |
|||
|
|||
InitializeVM(); |
|||
v8::HandleScope scope; |
|||
CreateTraceCallerFunction("JSFuncDoTrace", "trace"); |
|||
CompileRun( |
|||
"function JSTrace() {" |
|||
" JSFuncDoTrace();" |
|||
"};\n" |
|||
"JSTrace();"); |
|||
CHECK_GT(sample.frames_count, 1); |
|||
// Stack sampling will start from the first JS function, i.e. "JSFuncDoTrace"
|
|||
CheckRetAddrIsInJSFunction("JSFuncDoTrace", |
|||
sample.stack[0]); |
|||
CheckRetAddrIsInJSFunction("JSTrace", |
|||
sample.stack[1]); |
|||
} |
|||
|
|||
|
|||
TEST(PureJSStackTrace) { |
|||
TickSample sample; |
|||
InitTraceEnv(&sample); |
|||
|
|||
InitializeVM(); |
|||
v8::HandleScope scope; |
|||
CreateTraceCallerFunction("JSFuncDoTrace", "js_trace"); |
|||
CompileRun( |
|||
"function JSTrace() {" |
|||
" JSFuncDoTrace();" |
|||
"};\n" |
|||
"function OuterJSTrace() {" |
|||
" JSTrace();" |
|||
"};\n" |
|||
"OuterJSTrace();"); |
|||
CHECK_GT(sample.frames_count, 1); |
|||
// Stack sampling will start from the caller of JSFuncDoTrace, i.e. "JSTrace"
|
|||
CheckRetAddrIsInJSFunction("JSTrace", |
|||
sample.stack[0]); |
|||
CheckRetAddrIsInJSFunction("OuterJSTrace", |
|||
sample.stack[1]); |
|||
} |
|||
|
|||
|
|||
static void CFuncDoTrace() { |
|||
Address fp; |
|||
#ifdef __GNUC__ |
|||
fp = reinterpret_cast<Address>(__builtin_frame_address(0)); |
|||
#elif defined _MSC_VER && defined V8_TARGET_ARCH_IA32 |
|||
__asm mov [fp], ebp // NOLINT
|
|||
#elif defined _MSC_VER && defined V8_TARGET_ARCH_X64 |
|||
// FIXME: I haven't really tried to compile it.
|
|||
__asm movq [fp], rbp // NOLINT
|
|||
#endif |
|||
DoTrace(fp); |
|||
} |
|||
|
|||
|
|||
static int CFunc(int depth) { |
|||
if (depth <= 0) { |
|||
CFuncDoTrace(); |
|||
return 0; |
|||
} else { |
|||
return CFunc(depth - 1) + 1; |
|||
} |
|||
} |
|||
|
|||
|
|||
TEST(PureCStackTrace) { |
|||
TickSample sample; |
|||
InitTraceEnv(&sample); |
|||
// Check that sampler doesn't crash
|
|||
CHECK_EQ(10, CFunc(10)); |
|||
} |
|||
|
|||
|
|||
TEST(JsEntrySp) { |
|||
InitializeVM(); |
|||
v8::HandleScope scope; |
|||
CHECK_EQ(0, GetJsEntrySp()); |
|||
CompileRun("a = 1; b = a + 1;"); |
|||
CHECK_EQ(0, GetJsEntrySp()); |
|||
CompileRun("js_entry_sp();"); |
|||
CHECK_EQ(0, GetJsEntrySp()); |
|||
CompileRun("js_entry_sp_level2();"); |
|||
CHECK_EQ(0, GetJsEntrySp()); |
|||
} |
|||
|
|||
#endif // ENABLE_LOGGING_AND_PROFILING
|
@ -0,0 +1,38 @@ |
|||
// 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.
|
|||
|
|||
// Test of invalid Date construction, and TimeClip function.
|
|||
|
|||
// See http://code.google.com/p/v8/issues/detail?id=416
|
|||
|
|||
assertTrue(isNaN(new Date(1e81).getTime()), "new Date(1e81)"); |
|||
assertTrue(isNaN(new Date(-1e81).getTime()), "new Date(-1e81)"); |
|||
assertTrue(isNaN(new Date(1e81, "").getTime()), "new Date(1e81, \"\")"); |
|||
assertTrue(isNaN(new Date(-1e81, "").getTime()), "new Date(-1e81, \"\")"); |
|||
assertTrue(isNaN(new Date(Number.NaN).getTime()), "new Date(Number.NaN)"); |
|||
assertTrue(isNaN(new Date(Number.NaN, "").getTime()), |
|||
"new Date(Number.NaN, \"\")"); |
Loading…
Reference in new issue