mirror of https://github.com/lukechilds/node.git
Ryan Dahl
15 years ago
123 changed files with 3495 additions and 4380 deletions
@ -1,301 +0,0 @@ |
|||||
// 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 "codegen-arm.h" // Include after 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++) { |
|
||||
// If the location of the current instruction is a temp, then the
|
|
||||
// instruction cannot be in tail position in the block. Allocate the
|
|
||||
// temp based on peeking ahead to the next instruction.
|
|
||||
Instruction* instr = instructions_[i]; |
|
||||
Location* loc = instr->location(); |
|
||||
if (loc->is_temporary()) { |
|
||||
instructions_[i+1]->FastAllocate(TempLocation::cast(loc)); |
|
||||
} |
|
||||
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) { |
|
||||
__ LoadRoot(ip, Heap::kUndefinedValueRootIndex); |
|
||||
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 PropLoadInstr::Compile(MacroAssembler* masm) { |
|
||||
// The key should not be on the stack---if it is a compiler-generated
|
|
||||
// temporary it is in the accumulator.
|
|
||||
ASSERT(!key()->is_on_stack()); |
|
||||
|
|
||||
Comment cmnt(masm, "[ Load from Property"); |
|
||||
// If the key is known at compile-time we may be able to use a load IC.
|
|
||||
bool is_keyed_load = true; |
|
||||
if (key()->is_constant()) { |
|
||||
// Still use the keyed load IC if the key can be parsed as an integer so
|
|
||||
// we will get into the case that handles [] on string objects.
|
|
||||
Handle<Object> key_val = Constant::cast(key())->handle(); |
|
||||
uint32_t ignored; |
|
||||
if (key_val->IsSymbol() && |
|
||||
!String::cast(*key_val)->AsArrayIndex(&ignored)) { |
|
||||
is_keyed_load = false; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
if (!object()->is_on_stack()) object()->Push(masm); |
|
||||
|
|
||||
if (is_keyed_load) { |
|
||||
key()->Push(masm); |
|
||||
Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize)); |
|
||||
__ Call(ic, RelocInfo::CODE_TARGET); |
|
||||
// Discard key and receiver.
|
|
||||
__ add(sp, sp, Operand(2 * kPointerSize)); |
|
||||
} else { |
|
||||
key()->Get(masm, r2); |
|
||||
Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize)); |
|
||||
__ Call(ic, RelocInfo::CODE_TARGET); |
|
||||
__ pop(); // Discard receiver.
|
|
||||
} |
|
||||
location()->Set(masm, r0); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
void BinaryOpInstr::Compile(MacroAssembler* masm) { |
|
||||
// The right-hand value should not be on the stack---if it is a
|
|
||||
// compiler-generated temporary it is in the accumulator.
|
|
||||
ASSERT(!right()->is_on_stack()); |
|
||||
|
|
||||
Comment cmnt(masm, "[ BinaryOpInstr"); |
|
||||
// We can overwrite one of the operands if it is a temporary.
|
|
||||
OverwriteMode mode = NO_OVERWRITE; |
|
||||
if (left()->is_temporary()) { |
|
||||
mode = OVERWRITE_LEFT; |
|
||||
} else if (right()->is_temporary()) { |
|
||||
mode = OVERWRITE_RIGHT; |
|
||||
} |
|
||||
|
|
||||
// Move left to r1 and right to r0.
|
|
||||
left()->Get(masm, r1); |
|
||||
right()->Get(masm, r0); |
|
||||
GenericBinaryOpStub stub(op(), mode); |
|
||||
__ CallStub(&stub); |
|
||||
location()->Set(masm, r0); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
void ReturnInstr::Compile(MacroAssembler* masm) { |
|
||||
// The location should be 'Effect'. As a side effect, move the value to
|
|
||||
// the accumulator.
|
|
||||
Comment cmnt(masm, "[ ReturnInstr"); |
|
||||
value()->Get(masm, r0); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
void Constant::Get(MacroAssembler* masm, Register reg) { |
|
||||
__ mov(reg, Operand(handle_)); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
void Constant::Push(MacroAssembler* masm) { |
|
||||
__ mov(ip, Operand(handle_)); |
|
||||
__ push(ip); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
static MemOperand ToMemOperand(SlotLocation* loc) { |
|
||||
switch (loc->type()) { |
|
||||
case Slot::PARAMETER: { |
|
||||
int count = CfgGlobals::current()->fun()->scope()->num_parameters(); |
|
||||
return MemOperand(fp, (1 + count - loc->index()) * kPointerSize); |
|
||||
} |
|
||||
case Slot::LOCAL: { |
|
||||
const int kOffset = JavaScriptFrameConstants::kLocal0Offset; |
|
||||
return MemOperand(fp, kOffset - loc->index() * kPointerSize); |
|
||||
} |
|
||||
default: |
|
||||
UNREACHABLE(); |
|
||||
return MemOperand(r0); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
|
|
||||
void Constant::MoveToSlot(MacroAssembler* masm, SlotLocation* loc) { |
|
||||
__ mov(ip, Operand(handle_)); |
|
||||
__ str(ip, ToMemOperand(loc)); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
void SlotLocation::Get(MacroAssembler* masm, Register reg) { |
|
||||
__ ldr(reg, ToMemOperand(this)); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
void SlotLocation::Set(MacroAssembler* masm, Register reg) { |
|
||||
__ str(reg, ToMemOperand(this)); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
void SlotLocation::Push(MacroAssembler* masm) { |
|
||||
__ ldr(ip, ToMemOperand(this)); |
|
||||
__ push(ip); // Push will not destroy ip.
|
|
||||
} |
|
||||
|
|
||||
|
|
||||
void SlotLocation::Move(MacroAssembler* masm, Value* value) { |
|
||||
// Double dispatch.
|
|
||||
value->MoveToSlot(masm, this); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
void SlotLocation::MoveToSlot(MacroAssembler* masm, SlotLocation* loc) { |
|
||||
__ ldr(ip, ToMemOperand(this)); |
|
||||
__ str(ip, ToMemOperand(loc)); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
void TempLocation::Get(MacroAssembler* masm, Register reg) { |
|
||||
switch (where_) { |
|
||||
case ACCUMULATOR: |
|
||||
if (!reg.is(r0)) __ mov(reg, r0); |
|
||||
break; |
|
||||
case STACK: |
|
||||
__ pop(reg); |
|
||||
break; |
|
||||
case NOT_ALLOCATED: |
|
||||
UNREACHABLE(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
|
|
||||
void TempLocation::Set(MacroAssembler* masm, Register reg) { |
|
||||
switch (where_) { |
|
||||
case ACCUMULATOR: |
|
||||
if (!reg.is(r0)) __ mov(r0, reg); |
|
||||
break; |
|
||||
case STACK: |
|
||||
__ push(reg); |
|
||||
break; |
|
||||
case NOT_ALLOCATED: |
|
||||
UNREACHABLE(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
|
|
||||
void TempLocation::Push(MacroAssembler* masm) { |
|
||||
switch (where_) { |
|
||||
case ACCUMULATOR: |
|
||||
__ push(r0); |
|
||||
break; |
|
||||
case STACK: |
|
||||
case NOT_ALLOCATED: |
|
||||
UNREACHABLE(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
|
|
||||
void TempLocation::Move(MacroAssembler* masm, Value* value) { |
|
||||
switch (where_) { |
|
||||
case ACCUMULATOR: |
|
||||
value->Get(masm, r0); |
|
||||
case STACK: |
|
||||
value->Push(masm); |
|
||||
break; |
|
||||
case NOT_ALLOCATED: |
|
||||
UNREACHABLE(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
|
|
||||
void TempLocation::MoveToSlot(MacroAssembler* masm, SlotLocation* loc) { |
|
||||
switch (where_) { |
|
||||
case ACCUMULATOR: |
|
||||
__ str(r0, ToMemOperand(loc)); |
|
||||
case STACK: |
|
||||
__ pop(ip); |
|
||||
__ str(ip, ToMemOperand(loc)); |
|
||||
break; |
|
||||
case NOT_ALLOCATED: |
|
||||
UNREACHABLE(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
#undef __ |
|
||||
|
|
||||
} } // namespace v8::internal
|
|
@ -1,763 +0,0 @@ |
|||||
// 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
|
|
@ -1,871 +0,0 @@ |
|||||
// 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; |
|
||||
class Location; |
|
||||
|
|
||||
// Translate a source AST into a control-flow graph (CFG). The CFG contains
|
|
||||
// single-entry, single-exit blocks of straight-line instructions and
|
|
||||
// administrative nodes.
|
|
||||
//
|
|
||||
// Instructions are described by the following grammar.
|
|
||||
//
|
|
||||
// <Instruction> ::=
|
|
||||
// Move <Location> <Value>
|
|
||||
// | PropLoad <Location> <Value> <Value>
|
|
||||
// | BinaryOp <Location> Token::Value <Value> <Value>
|
|
||||
// | Return Nowhere <Value>
|
|
||||
// | Position <Int>
|
|
||||
//
|
|
||||
// Values are trivial expressions:
|
|
||||
//
|
|
||||
// <Value> ::= Constant | <Location>
|
|
||||
//
|
|
||||
// Locations are storable values ('lvalues'). They can be slots,
|
|
||||
// compiler-generated temporaries, or the special location 'Nowhere'
|
|
||||
// indicating that no value is needed.
|
|
||||
//
|
|
||||
// <Location> ::=
|
|
||||
// SlotLocation Slot::Type <Index>
|
|
||||
// | TempLocation
|
|
||||
// | Nowhere
|
|
||||
|
|
||||
|
|
||||
// Administrative nodes: There are several types of 'administrative' nodes
|
|
||||
// that do not contain instructions and do not necessarily have a single
|
|
||||
// predecessor and a single successor.
|
|
||||
//
|
|
||||
// EntryNode: there is a distinguished entry node that has no predecessors
|
|
||||
// and a single successor.
|
|
||||
//
|
|
||||
// ExitNode: there is a distinguished exit node that has arbitrarily many
|
|
||||
// predecessors and no successor.
|
|
||||
//
|
|
||||
// JoinNode: join nodes have multiple predecessors and a single successor.
|
|
||||
//
|
|
||||
// BranchNode: branch nodes have a single predecessor and multiple
|
|
||||
// successors.
|
|
||||
|
|
||||
|
|
||||
// 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_; |
|
||||
} |
|
||||
|
|
||||
// The function currently being compiled.
|
|
||||
FunctionLiteral* fun() { return global_fun_; } |
|
||||
|
|
||||
// The shared global exit node for all exits from the function.
|
|
||||
ExitNode* exit() { return global_exit_; } |
|
||||
|
|
||||
// A singleton.
|
|
||||
Location* nowhere() { return nowhere_; } |
|
||||
|
|
||||
#ifdef DEBUG |
|
||||
int next_node_number() { return node_counter_++; } |
|
||||
int next_temp_number() { return temp_counter_++; } |
|
||||
#endif |
|
||||
|
|
||||
private: |
|
||||
static CfgGlobals* top_; |
|
||||
FunctionLiteral* global_fun_; |
|
||||
ExitNode* global_exit_; |
|
||||
Location* nowhere_; |
|
||||
|
|
||||
#ifdef DEBUG |
|
||||
// Used to number nodes and temporaries when printing.
|
|
||||
int node_counter_; |
|
||||
int temp_counter_; |
|
||||
#endif |
|
||||
|
|
||||
CfgGlobals* previous_; |
|
||||
}; |
|
||||
|
|
||||
|
|
||||
class SlotLocation; |
|
||||
|
|
||||
// Values 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() {} |
|
||||
|
|
||||
// Predicates:
|
|
||||
|
|
||||
virtual bool is_temporary() { return false; } |
|
||||
virtual bool is_slot() { return false; } |
|
||||
virtual bool is_constant() { return false; } |
|
||||
|
|
||||
// True if the value is a temporary allocated to the stack in
|
|
||||
// fast-compilation mode.
|
|
||||
virtual bool is_on_stack() { return false; } |
|
||||
|
|
||||
// Support for fast-compilation mode:
|
|
||||
|
|
||||
// Move the value into a register.
|
|
||||
virtual void Get(MacroAssembler* masm, Register reg) = 0; |
|
||||
|
|
||||
// Push the value on the stack.
|
|
||||
virtual void Push(MacroAssembler* masm) = 0; |
|
||||
|
|
||||
// Move the value into a slot location.
|
|
||||
virtual void MoveToSlot(MacroAssembler* masm, SlotLocation* loc) = 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) {} |
|
||||
|
|
||||
// Cast accessor.
|
|
||||
static Constant* cast(Value* value) { |
|
||||
ASSERT(value->is_constant()); |
|
||||
return reinterpret_cast<Constant*>(value); |
|
||||
} |
|
||||
|
|
||||
// Accessors.
|
|
||||
Handle<Object> handle() { return handle_; } |
|
||||
|
|
||||
// Predicates.
|
|
||||
bool is_constant() { return true; } |
|
||||
|
|
||||
// Support for fast-compilation mode.
|
|
||||
void Get(MacroAssembler* masm, Register reg); |
|
||||
void Push(MacroAssembler* masm); |
|
||||
void MoveToSlot(MacroAssembler* masm, SlotLocation* loc); |
|
||||
|
|
||||
#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() {} |
|
||||
|
|
||||
// Static factory function returning the singleton nowhere location.
|
|
||||
static Location* Nowhere() { |
|
||||
return CfgGlobals::current()->nowhere(); |
|
||||
} |
|
||||
|
|
||||
// Support for fast-compilation mode:
|
|
||||
|
|
||||
// Assumes temporaries have been allocated.
|
|
||||
virtual void Get(MacroAssembler* masm, Register reg) = 0; |
|
||||
|
|
||||
// Store the value in a register to the location. Assumes temporaries
|
|
||||
// have been allocated.
|
|
||||
virtual void Set(MacroAssembler* masm, Register reg) = 0; |
|
||||
|
|
||||
// Assumes temporaries have been allocated, and if the value is a
|
|
||||
// temporary it was not allocated to the stack.
|
|
||||
virtual void Push(MacroAssembler* masm) = 0; |
|
||||
|
|
||||
// Emit code to move a value into this location.
|
|
||||
virtual void Move(MacroAssembler* masm, Value* value) = 0; |
|
||||
|
|
||||
#ifdef DEBUG |
|
||||
virtual void Print() = 0; |
|
||||
#endif |
|
||||
}; |
|
||||
|
|
||||
|
|
||||
// Nowhere is a special (singleton) location that indicates the value of a
|
|
||||
// computation is not needed (though its side effects are).
|
|
||||
class Nowhere : public Location { |
|
||||
public: |
|
||||
// We should not try to emit code to read Nowhere.
|
|
||||
void Get(MacroAssembler* masm, Register reg) { UNREACHABLE(); } |
|
||||
void Push(MacroAssembler* masm) { UNREACHABLE(); } |
|
||||
void MoveToSlot(MacroAssembler* masm, SlotLocation* loc) { UNREACHABLE(); } |
|
||||
|
|
||||
// Setting Nowhere is ignored.
|
|
||||
void Set(MacroAssembler* masm, Register reg) {} |
|
||||
void Move(MacroAssembler* masm, Value* value) {} |
|
||||
|
|
||||
#ifdef DEBUG |
|
||||
void Print(); |
|
||||
#endif |
|
||||
|
|
||||
private: |
|
||||
Nowhere() {} |
|
||||
|
|
||||
friend class CfgGlobals; |
|
||||
}; |
|
||||
|
|
||||
|
|
||||
// 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) {} |
|
||||
|
|
||||
// Cast accessor.
|
|
||||
static SlotLocation* cast(Value* value) { |
|
||||
ASSERT(value->is_slot()); |
|
||||
return reinterpret_cast<SlotLocation*>(value); |
|
||||
} |
|
||||
|
|
||||
// Accessors.
|
|
||||
Slot::Type type() { return type_; } |
|
||||
int index() { return index_; } |
|
||||
|
|
||||
// Predicates.
|
|
||||
bool is_slot() { return true; } |
|
||||
|
|
||||
// Support for fast-compilation mode.
|
|
||||
void Get(MacroAssembler* masm, Register reg); |
|
||||
void Set(MacroAssembler* masm, Register reg); |
|
||||
void Push(MacroAssembler* masm); |
|
||||
void Move(MacroAssembler* masm, Value* value); |
|
||||
void MoveToSlot(MacroAssembler* masm, SlotLocation* loc); |
|
||||
|
|
||||
#ifdef DEBUG |
|
||||
void Print(); |
|
||||
#endif |
|
||||
|
|
||||
private: |
|
||||
Slot::Type type_; |
|
||||
int index_; |
|
||||
}; |
|
||||
|
|
||||
|
|
||||
// TempLocations represent compiler generated temporaries. They are
|
|
||||
// allocated to registers or memory either before code generation (in the
|
|
||||
// optimized-for-speed compiler) or on the fly during code generation (in
|
|
||||
// the optimized-for-space compiler).
|
|
||||
class TempLocation : public Location { |
|
||||
public: |
|
||||
// Fast-compilation mode allocation decisions.
|
|
||||
enum Where { |
|
||||
NOT_ALLOCATED, // Not yet allocated.
|
|
||||
ACCUMULATOR, // Allocated to the dedicated accumulator register.
|
|
||||
STACK // " " " " stack.
|
|
||||
}; |
|
||||
|
|
||||
TempLocation() : where_(NOT_ALLOCATED) { |
|
||||
#ifdef DEBUG |
|
||||
number_ = -1; |
|
||||
#endif |
|
||||
} |
|
||||
|
|
||||
// Cast accessor.
|
|
||||
static TempLocation* cast(Value* value) { |
|
||||
ASSERT(value->is_temporary()); |
|
||||
return reinterpret_cast<TempLocation*>(value); |
|
||||
} |
|
||||
|
|
||||
// Accessors.
|
|
||||
Where where() { return where_; } |
|
||||
void set_where(Where where) { |
|
||||
ASSERT(where_ == TempLocation::NOT_ALLOCATED); |
|
||||
where_ = where; |
|
||||
} |
|
||||
|
|
||||
// Predicates.
|
|
||||
bool is_on_stack() { return where_ == STACK; } |
|
||||
bool is_temporary() { return true; } |
|
||||
|
|
||||
// Support for fast-compilation mode. Assume the temp has been allocated.
|
|
||||
void Get(MacroAssembler* masm, Register reg); |
|
||||
void Set(MacroAssembler* masm, Register reg); |
|
||||
void Push(MacroAssembler* masm); |
|
||||
void Move(MacroAssembler* masm, Value* value); |
|
||||
void MoveToSlot(MacroAssembler* masm, SlotLocation* loc); |
|
||||
|
|
||||
#ifdef DEBUG |
|
||||
int number() { |
|
||||
if (number_ == -1) number_ = CfgGlobals::current()->next_temp_number(); |
|
||||
return number_; |
|
||||
} |
|
||||
|
|
||||
void Print(); |
|
||||
#endif |
|
||||
|
|
||||
private: |
|
||||
Where where_; |
|
||||
|
|
||||
#ifdef DEBUG |
|
||||
int number_; |
|
||||
#endif |
|
||||
}; |
|
||||
|
|
||||
|
|
||||
// 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: |
|
||||
// Accessors.
|
|
||||
Location* location() { return location_; } |
|
||||
void set_location(Location* location) { location_ = location; } |
|
||||
|
|
||||
// Support for fast-compilation mode:
|
|
||||
|
|
||||
// Emit code to perform the instruction.
|
|
||||
virtual void Compile(MacroAssembler* masm) = 0; |
|
||||
|
|
||||
// Allocate a temporary which is the result of the immediate predecessor
|
|
||||
// instruction. It is allocated to the accumulator register if it is used
|
|
||||
// as an operand to this instruction, otherwise to the stack.
|
|
||||
virtual void FastAllocate(TempLocation* temp) = 0; |
|
||||
|
|
||||
#ifdef DEBUG |
|
||||
virtual void Print() = 0; |
|
||||
#endif |
|
||||
|
|
||||
protected: |
|
||||
// Every instruction has a location where its result is stored (which may
|
|
||||
// be Nowhere).
|
|
||||
explicit Instruction(Location* location) : location_(location) {} |
|
||||
|
|
||||
virtual ~Instruction() {} |
|
||||
|
|
||||
Location* location_; |
|
||||
}; |
|
||||
|
|
||||
|
|
||||
// Base class of instructions that have no input operands.
|
|
||||
class ZeroOperandInstruction : public Instruction { |
|
||||
public: |
|
||||
// Support for fast-compilation mode:
|
|
||||
virtual void Compile(MacroAssembler* masm) = 0; |
|
||||
void FastAllocate(TempLocation* temp); |
|
||||
|
|
||||
#ifdef DEBUG |
|
||||
// Printing support: print the operands (nothing).
|
|
||||
virtual void Print() {} |
|
||||
#endif |
|
||||
|
|
||||
protected: |
|
||||
explicit ZeroOperandInstruction(Location* loc) : Instruction(loc) {} |
|
||||
}; |
|
||||
|
|
||||
|
|
||||
// Base class of instructions that have a single input operand.
|
|
||||
class OneOperandInstruction : public Instruction { |
|
||||
public: |
|
||||
// Support for fast-compilation mode:
|
|
||||
virtual void Compile(MacroAssembler* masm) = 0; |
|
||||
void FastAllocate(TempLocation* temp); |
|
||||
|
|
||||
#ifdef DEBUG |
|
||||
// Printing support: print the operands.
|
|
||||
virtual void Print(); |
|
||||
#endif |
|
||||
|
|
||||
protected: |
|
||||
OneOperandInstruction(Location* loc, Value* value) |
|
||||
: Instruction(loc), value_(value) { |
|
||||
} |
|
||||
|
|
||||
Value* value_; |
|
||||
}; |
|
||||
|
|
||||
|
|
||||
// Base class of instructions that have two input operands.
|
|
||||
class TwoOperandInstruction : public Instruction { |
|
||||
public: |
|
||||
// Support for fast-compilation mode:
|
|
||||
virtual void Compile(MacroAssembler* masm) = 0; |
|
||||
void FastAllocate(TempLocation* temp); |
|
||||
|
|
||||
#ifdef DEBUG |
|
||||
// Printing support: print the operands.
|
|
||||
virtual void Print(); |
|
||||
#endif |
|
||||
|
|
||||
protected: |
|
||||
TwoOperandInstruction(Location* loc, Value* value0, Value* value1) |
|
||||
: Instruction(loc), value0_(value0), value1_(value1) { |
|
||||
} |
|
||||
|
|
||||
Value* value0_; |
|
||||
Value* value1_; |
|
||||
}; |
|
||||
|
|
||||
|
|
||||
// A phantom instruction that indicates the start of a statement. It
|
|
||||
// causes the statement position to be recorded in the relocation
|
|
||||
// information but generates no code.
|
|
||||
class PositionInstr : public ZeroOperandInstruction { |
|
||||
public: |
|
||||
explicit PositionInstr(int pos) |
|
||||
: ZeroOperandInstruction(CfgGlobals::current()->nowhere()), pos_(pos) { |
|
||||
} |
|
||||
|
|
||||
// Support for fast-compilation mode.
|
|
||||
void Compile(MacroAssembler* masm); |
|
||||
|
|
||||
// This should not be called. The last instruction of the previous
|
|
||||
// statement should not have a temporary as its location.
|
|
||||
void FastAllocate(TempLocation* temp) { UNREACHABLE(); } |
|
||||
|
|
||||
#ifdef DEBUG |
|
||||
// Printing support. Print nothing.
|
|
||||
void Print() {} |
|
||||
#endif |
|
||||
|
|
||||
private: |
|
||||
int pos_; |
|
||||
}; |
|
||||
|
|
||||
|
|
||||
// Move a value to a location.
|
|
||||
class MoveInstr : public OneOperandInstruction { |
|
||||
public: |
|
||||
MoveInstr(Location* loc, Value* value) |
|
||||
: OneOperandInstruction(loc, value) { |
|
||||
} |
|
||||
|
|
||||
// Accessors.
|
|
||||
Value* value() { return value_; } |
|
||||
|
|
||||
// Support for fast-compilation mode.
|
|
||||
void Compile(MacroAssembler* masm); |
|
||||
|
|
||||
#ifdef DEBUG |
|
||||
// Printing support.
|
|
||||
void Print(); |
|
||||
#endif |
|
||||
}; |
|
||||
|
|
||||
|
|
||||
// Load a property from a receiver, leaving the result in a location.
|
|
||||
class PropLoadInstr : public TwoOperandInstruction { |
|
||||
public: |
|
||||
PropLoadInstr(Location* loc, Value* object, Value* key) |
|
||||
: TwoOperandInstruction(loc, object, key) { |
|
||||
} |
|
||||
|
|
||||
// Accessors.
|
|
||||
Value* object() { return value0_; } |
|
||||
Value* key() { return value1_; } |
|
||||
|
|
||||
// Support for fast-compilation mode.
|
|
||||
void Compile(MacroAssembler* masm); |
|
||||
|
|
||||
#ifdef DEBUG |
|
||||
void Print(); |
|
||||
#endif |
|
||||
}; |
|
||||
|
|
||||
|
|
||||
// Perform a (non-short-circuited) binary operation on a pair of values,
|
|
||||
// leaving the result in a location.
|
|
||||
class BinaryOpInstr : public TwoOperandInstruction { |
|
||||
public: |
|
||||
BinaryOpInstr(Location* loc, Token::Value op, Value* left, Value* right) |
|
||||
: TwoOperandInstruction(loc, left, right), op_(op) { |
|
||||
} |
|
||||
|
|
||||
// Accessors.
|
|
||||
Value* left() { return value0_; } |
|
||||
Value* right() { return value1_; } |
|
||||
Token::Value op() { return op_; } |
|
||||
|
|
||||
// Support for fast-compilation mode.
|
|
||||
void Compile(MacroAssembler* masm); |
|
||||
|
|
||||
#ifdef DEBUG |
|
||||
void Print(); |
|
||||
#endif |
|
||||
|
|
||||
private: |
|
||||
Token::Value op_; |
|
||||
}; |
|
||||
|
|
||||
|
|
||||
// Return a value. Has the side effect of moving its value into the return
|
|
||||
// value register. Can only occur as the last instruction in an instruction
|
|
||||
// block, and implies that the block is closed (cannot have instructions
|
|
||||
// appended or graph fragments concatenated to the end) and that the block's
|
|
||||
// successor is the global exit node for the current function.
|
|
||||
class ReturnInstr : public OneOperandInstruction { |
|
||||
public: |
|
||||
explicit ReturnInstr(Value* value) |
|
||||
: OneOperandInstruction(CfgGlobals::current()->nowhere(), value) { |
|
||||
} |
|
||||
|
|
||||
virtual ~ReturnInstr() {} |
|
||||
|
|
||||
// Accessors.
|
|
||||
Value* value() { return value_; } |
|
||||
|
|
||||
// Support for fast-compilation mode.
|
|
||||
void Compile(MacroAssembler* masm); |
|
||||
|
|
||||
#ifdef DEBUG |
|
||||
void Print(); |
|
||||
#endif |
|
||||
}; |
|
||||
|
|
||||
|
|
||||
// Nodes make up control-flow graphs.
|
|
||||
class CfgNode : public ZoneObject { |
|
||||
public: |
|
||||
CfgNode() : is_marked_(false) { |
|
||||
#ifdef DEBUG |
|
||||
number_ = -1; |
|
||||
#endif |
|
||||
} |
|
||||
|
|
||||
virtual ~CfgNode() {} |
|
||||
|
|
||||
// Because CFGs contain cycles, nodes support marking during traversal
|
|
||||
// (e.g., for printing or compilation). The traversal functions will mark
|
|
||||
// unmarked nodes and backtrack if they encounter a marked one. After a
|
|
||||
// traversal, the graph should be explicitly unmarked by calling Unmark on
|
|
||||
// the entry node.
|
|
||||
bool is_marked() { return is_marked_; } |
|
||||
virtual void Unmark() = 0; |
|
||||
|
|
||||
// Predicates:
|
|
||||
|
|
||||
// True if the node is an instruction block.
|
|
||||
virtual bool is_block() { return false; } |
|
||||
|
|
||||
// Support for fast-compilation mode. Emit the instructions or control
|
|
||||
// flow represented by the node.
|
|
||||
virtual void Compile(MacroAssembler* masm) = 0; |
|
||||
|
|
||||
#ifdef DEBUG |
|
||||
int number() { |
|
||||
if (number_ == -1) number_ = CfgGlobals::current()->next_node_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() {} |
|
||||
|
|
||||
void Unmark(); |
|
||||
|
|
||||
// Cast accessor.
|
|
||||
static InstructionBlock* cast(CfgNode* node) { |
|
||||
ASSERT(node->is_block()); |
|
||||
return reinterpret_cast<InstructionBlock*>(node); |
|
||||
} |
|
||||
|
|
||||
bool is_block() { return true; } |
|
||||
|
|
||||
// Accessors.
|
|
||||
CfgNode* successor() { return successor_; } |
|
||||
|
|
||||
void set_successor(CfgNode* succ) { |
|
||||
ASSERT(successor_ == NULL); |
|
||||
successor_ = succ; |
|
||||
} |
|
||||
|
|
||||
ZoneList<Instruction*>* instructions() { return &instructions_; } |
|
||||
|
|
||||
// Support for fast-compilation mode.
|
|
||||
void Compile(MacroAssembler* masm); |
|
||||
|
|
||||
// Add an instruction to the end of the block.
|
|
||||
void Append(Instruction* instr) { instructions_.Add(instr); } |
|
||||
|
|
||||
#ifdef DEBUG |
|
||||
void Print(); |
|
||||
#endif |
|
||||
|
|
||||
private: |
|
||||
CfgNode* successor_; |
|
||||
ZoneList<Instruction*> instructions_; |
|
||||
}; |
|
||||
|
|
||||
|
|
||||
// An entry node (one per function).
|
|
||||
class EntryNode : public CfgNode { |
|
||||
public: |
|
||||
explicit EntryNode(InstructionBlock* succ) : successor_(succ) {} |
|
||||
|
|
||||
virtual ~EntryNode() {} |
|
||||
|
|
||||
void Unmark(); |
|
||||
|
|
||||
// Support for fast-compilation mode.
|
|
||||
void Compile(MacroAssembler* masm); |
|
||||
|
|
||||
#ifdef DEBUG |
|
||||
void Print(); |
|
||||
#endif |
|
||||
|
|
||||
private: |
|
||||
InstructionBlock* successor_; |
|
||||
}; |
|
||||
|
|
||||
|
|
||||
// An exit node (one per function).
|
|
||||
class ExitNode : public CfgNode { |
|
||||
public: |
|
||||
ExitNode() {} |
|
||||
|
|
||||
virtual ~ExitNode() {} |
|
||||
|
|
||||
void Unmark(); |
|
||||
|
|
||||
// Support for fast-compilation mode.
|
|
||||
void Compile(MacroAssembler* masm); |
|
||||
|
|
||||
#ifdef DEBUG |
|
||||
void Print(); |
|
||||
#endif |
|
||||
}; |
|
||||
|
|
||||
|
|
||||
// A CFG consists of a linked structure of nodes. Nodes are linked by
|
|
||||
// pointing to their successors, always beginning with a (single) entry node
|
|
||||
// (not necessarily of type EntryNode). If it is still possible to add
|
|
||||
// nodes to the end of the graph (i.e., there is a (single) path that does
|
|
||||
// not end with the global exit node), then the CFG has an exit node as
|
|
||||
// well.
|
|
||||
//
|
|
||||
// The empty CFG is represented by a NULL entry and a NULL exit.
|
|
||||
//
|
|
||||
// We use the term 'open fragment' to mean a CFG whose entry and exits are
|
|
||||
// both instruction blocks. It is always possible to add instructions and
|
|
||||
// nodes to the beginning or end of an open fragment.
|
|
||||
//
|
|
||||
// We use the term 'closed fragment' to mean a CFG whose entry is an
|
|
||||
// instruction block and whose exit is NULL (all paths go to the global
|
|
||||
// exit).
|
|
||||
//
|
|
||||
// We use the term 'fragment' to refer to a CFG that is known to be an open
|
|
||||
// or closed fragment.
|
|
||||
class Cfg : public ZoneObject { |
|
||||
public: |
|
||||
// Create an empty CFG fragment.
|
|
||||
Cfg() : entry_(NULL), exit_(NULL) {} |
|
||||
|
|
||||
// Build the CFG for a function. The returned CFG begins with an
|
|
||||
// EntryNode and all paths end with the ExitNode.
|
|
||||
static Cfg* Build(); |
|
||||
|
|
||||
// The entry and exit nodes of the CFG (not necessarily EntryNode and
|
|
||||
// ExitNode).
|
|
||||
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 EntryNode to a CFG fragment. It is no longer a fragment
|
|
||||
// (instructions can no longer be prepended).
|
|
||||
void PrependEntryNode(); |
|
||||
|
|
||||
// Append an instruction to the end of an open fragment.
|
|
||||
void Append(Instruction* instr); |
|
||||
|
|
||||
// Appends a return instruction to the end of an open fragment and make
|
|
||||
// it a closed fragment (the exit's successor becomes global exit node).
|
|
||||
void AppendReturnInstruction(Value* value); |
|
||||
|
|
||||
// Glue an other CFG fragment to the end of this (open) fragment.
|
|
||||
void Concatenate(Cfg* other); |
|
||||
|
|
||||
// Support for compilation. Compile the entire CFG.
|
|
||||
Handle<Code> Compile(Handle<Script> script); |
|
||||
|
|
||||
#ifdef DEBUG |
|
||||
// Support for printing.
|
|
||||
void Print(); |
|
||||
#endif |
|
||||
|
|
||||
private: |
|
||||
// Entry and exit nodes.
|
|
||||
CfgNode* entry_; |
|
||||
CfgNode* exit_; |
|
||||
}; |
|
||||
|
|
||||
|
|
||||
// An implementation of a set of locations (currently slot locations), most
|
|
||||
// of the operations are destructive.
|
|
||||
class LocationSet BASE_EMBEDDED { |
|
||||
public: |
|
||||
// Construct an empty location set.
|
|
||||
LocationSet() : parameters_(0), locals_(0) {} |
|
||||
|
|
||||
// Raw accessors.
|
|
||||
uintptr_t parameters() { return parameters_; } |
|
||||
uintptr_t locals() { return locals_; } |
|
||||
|
|
||||
// Make this the empty set.
|
|
||||
void Empty() { |
|
||||
parameters_ = locals_ = 0; |
|
||||
} |
|
||||
|
|
||||
// Insert an element.
|
|
||||
void AddElement(SlotLocation* location) { |
|
||||
if (location->type() == Slot::PARAMETER) { |
|
||||
// Parameter indexes begin with -1 ('this').
|
|
||||
ASSERT(location->index() < kBitsPerPointer - 1); |
|
||||
parameters_ |= (1 << (location->index() + 1)); |
|
||||
} else { |
|
||||
ASSERT(location->type() == Slot::LOCAL); |
|
||||
ASSERT(location->index() < kBitsPerPointer); |
|
||||
locals_ |= (1 << location->index()); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// (Destructively) compute the union with another set.
|
|
||||
void Union(LocationSet* other) { |
|
||||
parameters_ |= other->parameters(); |
|
||||
locals_ |= other->locals(); |
|
||||
} |
|
||||
|
|
||||
bool Contains(SlotLocation* location) { |
|
||||
if (location->type() == Slot::PARAMETER) { |
|
||||
ASSERT(location->index() < kBitsPerPointer - 1); |
|
||||
return (parameters_ & (1 << (location->index() + 1))); |
|
||||
} else { |
|
||||
ASSERT(location->type() == Slot::LOCAL); |
|
||||
ASSERT(location->index() < kBitsPerPointer); |
|
||||
return (locals_ & (1 << location->index())); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
private: |
|
||||
uintptr_t parameters_; |
|
||||
uintptr_t locals_; |
|
||||
}; |
|
||||
|
|
||||
|
|
||||
// An ExpressionCfgBuilder traverses an expression and returns an open CFG
|
|
||||
// fragment (currently a possibly empty list of instructions represented by
|
|
||||
// a singleton instruction block) and the expression's value.
|
|
||||
//
|
|
||||
// Failure to build the CFG is indicated by a NULL CFG.
|
|
||||
class ExpressionCfgBuilder : public AstVisitor { |
|
||||
public: |
|
||||
ExpressionCfgBuilder() : destination_(NULL), value_(NULL), graph_(NULL) {} |
|
||||
|
|
||||
// Result accessors.
|
|
||||
Value* value() { return value_; } |
|
||||
Cfg* graph() { return graph_; } |
|
||||
LocationSet* assigned_vars() { return &assigned_vars_; } |
|
||||
|
|
||||
// Build the cfg for an expression and remember its value. The
|
|
||||
// destination is a 'hint' where the value should go which may be ignored.
|
|
||||
// NULL is used to indicate no preference.
|
|
||||
//
|
|
||||
// Concretely, if the expression needs to generate a temporary for its
|
|
||||
// value, it should use the passed destination or generate one if NULL.
|
|
||||
void Build(Expression* expr, Location* destination) { |
|
||||
value_ = NULL; |
|
||||
graph_ = new Cfg(); |
|
||||
assigned_vars_.Empty(); |
|
||||
destination_ = destination; |
|
||||
Visit(expr); |
|
||||
} |
|
||||
|
|
||||
// AST node visitors.
|
|
||||
#define DECLARE_VISIT(type) void Visit##type(type* node); |
|
||||
AST_NODE_LIST(DECLARE_VISIT) |
|
||||
#undef DECLARE_VISIT |
|
||||
|
|
||||
private: |
|
||||
// State for the visitor. Input parameter:
|
|
||||
Location* destination_; |
|
||||
|
|
||||
// Output parameters:
|
|
||||
Value* value_; |
|
||||
Cfg* graph_; |
|
||||
LocationSet assigned_vars_; |
|
||||
}; |
|
||||
|
|
||||
|
|
||||
// A StatementCfgBuilder maintains a CFG fragment accumulator. When it
|
|
||||
// visits a statement, it concatenates the CFG for the statement to the end
|
|
||||
// of the accumulator.
|
|
||||
class StatementCfgBuilder : public AstVisitor { |
|
||||
public: |
|
||||
StatementCfgBuilder() : graph_(new Cfg()) {} |
|
||||
|
|
||||
Cfg* graph() { return graph_; } |
|
||||
|
|
||||
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: |
|
||||
// State for the visitor. Input/output parameter:
|
|
||||
Cfg* graph_; |
|
||||
}; |
|
||||
|
|
||||
|
|
||||
} } // namespace v8::internal
|
|
||||
|
|
||||
#endif // V8_CFG_H_
|
|
@ -1,315 +0,0 @@ |
|||||
// 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 "codegen-ia32.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++) { |
|
||||
// If the location of the current instruction is a temp, then the
|
|
||||
// instruction cannot be in tail position in the block. Allocate the
|
|
||||
// temp based on peeking ahead to the next instruction.
|
|
||||
Instruction* instr = instructions_[i]; |
|
||||
Location* loc = instr->location(); |
|
||||
if (loc->is_temporary()) { |
|
||||
instructions_[i+1]->FastAllocate(TempLocation::cast(loc)); |
|
||||
} |
|
||||
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) { |
|
||||
Comment cmnt(masm, "[ Deferred Stack Check"); |
|
||||
__ 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 PropLoadInstr::Compile(MacroAssembler* masm) { |
|
||||
// The key should not be on the stack---if it is a compiler-generated
|
|
||||
// temporary it is in the accumulator.
|
|
||||
ASSERT(!key()->is_on_stack()); |
|
||||
|
|
||||
Comment cmnt(masm, "[ Load from Property"); |
|
||||
// If the key is known at compile-time we may be able to use a load IC.
|
|
||||
bool is_keyed_load = true; |
|
||||
if (key()->is_constant()) { |
|
||||
// Still use the keyed load IC if the key can be parsed as an integer so
|
|
||||
// we will get into the case that handles [] on string objects.
|
|
||||
Handle<Object> key_val = Constant::cast(key())->handle(); |
|
||||
uint32_t ignored; |
|
||||
if (key_val->IsSymbol() && |
|
||||
!String::cast(*key_val)->AsArrayIndex(&ignored)) { |
|
||||
is_keyed_load = false; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
if (!object()->is_on_stack()) object()->Push(masm); |
|
||||
// A test eax instruction after the call indicates to the IC code that it
|
|
||||
// was inlined. Ensure there is not one here.
|
|
||||
if (is_keyed_load) { |
|
||||
key()->Push(masm); |
|
||||
Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize)); |
|
||||
__ call(ic, RelocInfo::CODE_TARGET); |
|
||||
__ pop(ebx); // Discard key.
|
|
||||
} else { |
|
||||
key()->Get(masm, ecx); |
|
||||
Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize)); |
|
||||
__ call(ic, RelocInfo::CODE_TARGET); |
|
||||
} |
|
||||
__ pop(ebx); // Discard receiver.
|
|
||||
location()->Set(masm, eax); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
void BinaryOpInstr::Compile(MacroAssembler* masm) { |
|
||||
// The right-hand value should not be on the stack---if it is a
|
|
||||
// compiler-generated temporary it is in the accumulator.
|
|
||||
ASSERT(!right()->is_on_stack()); |
|
||||
|
|
||||
Comment cmnt(masm, "[ BinaryOpInstr"); |
|
||||
// We can overwrite one of the operands if it is a temporary.
|
|
||||
OverwriteMode mode = NO_OVERWRITE; |
|
||||
if (left()->is_temporary()) { |
|
||||
mode = OVERWRITE_LEFT; |
|
||||
} else if (right()->is_temporary()) { |
|
||||
mode = OVERWRITE_RIGHT; |
|
||||
} |
|
||||
|
|
||||
// Push both operands and call the specialized stub.
|
|
||||
if (!left()->is_on_stack()) left()->Push(masm); |
|
||||
right()->Push(masm); |
|
||||
GenericBinaryOpStub stub(op(), mode, SMI_CODE_IN_STUB); |
|
||||
__ CallStub(&stub); |
|
||||
location()->Set(masm, eax); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
void ReturnInstr::Compile(MacroAssembler* masm) { |
|
||||
// The location should be 'Effect'. As a side effect, move the value to
|
|
||||
// the accumulator.
|
|
||||
Comment cmnt(masm, "[ ReturnInstr"); |
|
||||
value_->Get(masm, eax); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
void Constant::Get(MacroAssembler* masm, Register reg) { |
|
||||
__ mov(reg, Immediate(handle_)); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
void Constant::Push(MacroAssembler* masm) { |
|
||||
__ push(Immediate(handle_)); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
static Operand ToOperand(SlotLocation* loc) { |
|
||||
switch (loc->type()) { |
|
||||
case Slot::PARAMETER: { |
|
||||
int count = CfgGlobals::current()->fun()->scope()->num_parameters(); |
|
||||
return Operand(ebp, (1 + count - loc->index()) * kPointerSize); |
|
||||
} |
|
||||
case Slot::LOCAL: { |
|
||||
const int kOffset = JavaScriptFrameConstants::kLocal0Offset; |
|
||||
return Operand(ebp, kOffset - loc->index() * kPointerSize); |
|
||||
} |
|
||||
default: |
|
||||
UNREACHABLE(); |
|
||||
return Operand(eax); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
|
|
||||
void Constant::MoveToSlot(MacroAssembler* masm, SlotLocation* loc) { |
|
||||
__ mov(ToOperand(loc), Immediate(handle_)); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
void SlotLocation::Get(MacroAssembler* masm, Register reg) { |
|
||||
__ mov(reg, ToOperand(this)); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
void SlotLocation::Set(MacroAssembler* masm, Register reg) { |
|
||||
__ mov(ToOperand(this), reg); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
void SlotLocation::Push(MacroAssembler* masm) { |
|
||||
__ push(ToOperand(this)); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
void SlotLocation::Move(MacroAssembler* masm, Value* value) { |
|
||||
// We dispatch to the value because in some cases (temp or constant)
|
|
||||
// we can use a single instruction.
|
|
||||
value->MoveToSlot(masm, this); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
void SlotLocation::MoveToSlot(MacroAssembler* masm, SlotLocation* loc) { |
|
||||
// The accumulator is not live across a MoveInstr.
|
|
||||
__ mov(eax, ToOperand(this)); |
|
||||
__ mov(ToOperand(loc), eax); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
void TempLocation::Get(MacroAssembler* masm, Register reg) { |
|
||||
switch (where_) { |
|
||||
case ACCUMULATOR: |
|
||||
if (!reg.is(eax)) __ mov(reg, eax); |
|
||||
break; |
|
||||
case STACK: |
|
||||
__ pop(reg); |
|
||||
break; |
|
||||
case NOT_ALLOCATED: |
|
||||
UNREACHABLE(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
|
|
||||
void TempLocation::Set(MacroAssembler* masm, Register reg) { |
|
||||
switch (where_) { |
|
||||
case ACCUMULATOR: |
|
||||
if (!reg.is(eax)) __ mov(eax, reg); |
|
||||
break; |
|
||||
case STACK: |
|
||||
__ push(reg); |
|
||||
break; |
|
||||
case NOT_ALLOCATED: |
|
||||
UNREACHABLE(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
|
|
||||
void TempLocation::Push(MacroAssembler* masm) { |
|
||||
switch (where_) { |
|
||||
case ACCUMULATOR: |
|
||||
__ push(eax); |
|
||||
break; |
|
||||
case STACK: |
|
||||
case NOT_ALLOCATED: |
|
||||
UNREACHABLE(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
|
|
||||
void TempLocation::Move(MacroAssembler* masm, Value* value) { |
|
||||
switch (where_) { |
|
||||
case ACCUMULATOR: |
|
||||
value->Get(masm, eax); |
|
||||
break; |
|
||||
case STACK: |
|
||||
value->Push(masm); |
|
||||
break; |
|
||||
case NOT_ALLOCATED: |
|
||||
UNREACHABLE(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
|
|
||||
void TempLocation::MoveToSlot(MacroAssembler* masm, SlotLocation* loc) { |
|
||||
switch (where_) { |
|
||||
case ACCUMULATOR: |
|
||||
__ mov(ToOperand(loc), eax); |
|
||||
break; |
|
||||
case STACK: |
|
||||
__ pop(ToOperand(loc)); |
|
||||
break; |
|
||||
case NOT_ALLOCATED: |
|
||||
UNREACHABLE(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
|
|
||||
#undef __ |
|
||||
|
|
||||
} } // namespace v8::internal
|
|
@ -1,324 +0,0 @@ |
|||||
// 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 "codegen-x64.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++) { |
|
||||
// If the location of the current instruction is a temp, then the
|
|
||||
// instruction cannot be in tail position in the block. Allocate the
|
|
||||
// temp based on peeking ahead to the next instruction.
|
|
||||
Instruction* instr = instructions_[i]; |
|
||||
Location* loc = instr->location(); |
|
||||
if (loc->is_temporary()) { |
|
||||
instructions_[i+1]->FastAllocate(TempLocation::cast(loc)); |
|
||||
} |
|
||||
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) { |
|
||||
__ LoadRoot(kScratchRegister, Heap::kUndefinedValueRootIndex); |
|
||||
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) { |
|
||||
Comment cmnt(masm, "[ Deferred Stack Check"); |
|
||||
__ 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); |
|
||||
#ifdef ENABLE_DEBUGGER_SUPPORT |
|
||||
// Add padding that will be overwritten by a debugger breakpoint.
|
|
||||
// "movq rsp, rbp; pop rbp" has length 4. "ret k" has length 3.
|
|
||||
const int kPadding = Debug::kX64JSReturnSequenceLength - 4 - 3; |
|
||||
for (int i = 0; i < kPadding; ++i) { |
|
||||
__ int3(); |
|
||||
} |
|
||||
#endif |
|
||||
} |
|
||||
|
|
||||
|
|
||||
void PropLoadInstr::Compile(MacroAssembler* masm) { |
|
||||
// The key should not be on the stack---if it is a compiler-generated
|
|
||||
// temporary it is in the accumulator.
|
|
||||
ASSERT(!key()->is_on_stack()); |
|
||||
|
|
||||
Comment cmnt(masm, "[ Load from Property"); |
|
||||
// If the key is known at compile-time we may be able to use a load IC.
|
|
||||
bool is_keyed_load = true; |
|
||||
if (key()->is_constant()) { |
|
||||
// Still use the keyed load IC if the key can be parsed as an integer so
|
|
||||
// we will get into the case that handles [] on string objects.
|
|
||||
Handle<Object> key_val = Constant::cast(key())->handle(); |
|
||||
uint32_t ignored; |
|
||||
if (key_val->IsSymbol() && |
|
||||
!String::cast(*key_val)->AsArrayIndex(&ignored)) { |
|
||||
is_keyed_load = false; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
if (!object()->is_on_stack()) object()->Push(masm); |
|
||||
// A test rax instruction after the call indicates to the IC code that it
|
|
||||
// was inlined. Ensure there is not one after the call below.
|
|
||||
if (is_keyed_load) { |
|
||||
key()->Push(masm); |
|
||||
Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize)); |
|
||||
__ Call(ic, RelocInfo::CODE_TARGET); |
|
||||
__ pop(rbx); // Discard key.
|
|
||||
} else { |
|
||||
key()->Get(masm, rcx); |
|
||||
Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize)); |
|
||||
__ Call(ic, RelocInfo::CODE_TARGET); |
|
||||
} |
|
||||
__ pop(rbx); // Discard receiver.
|
|
||||
location()->Set(masm, rax); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
void BinaryOpInstr::Compile(MacroAssembler* masm) { |
|
||||
// The right-hand value should not be on the stack---if it is a
|
|
||||
// compiler-generated temporary it is in the accumulator.
|
|
||||
ASSERT(!right()->is_on_stack()); |
|
||||
|
|
||||
Comment cmnt(masm, "[ BinaryOpInstr"); |
|
||||
// We can overwrite one of the operands if it is a temporary.
|
|
||||
OverwriteMode mode = NO_OVERWRITE; |
|
||||
if (left()->is_temporary()) { |
|
||||
mode = OVERWRITE_LEFT; |
|
||||
} else if (right()->is_temporary()) { |
|
||||
mode = OVERWRITE_RIGHT; |
|
||||
} |
|
||||
|
|
||||
// Push both operands and call the specialized stub.
|
|
||||
if (!left()->is_on_stack()) left()->Push(masm); |
|
||||
right()->Push(masm); |
|
||||
GenericBinaryOpStub stub(op(), mode, SMI_CODE_IN_STUB); |
|
||||
__ CallStub(&stub); |
|
||||
location()->Set(masm, rax); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
void ReturnInstr::Compile(MacroAssembler* masm) { |
|
||||
// The location should be 'Effect'. As a side effect, move the value to
|
|
||||
// the accumulator.
|
|
||||
Comment cmnt(masm, "[ ReturnInstr"); |
|
||||
value()->Get(masm, rax); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
void Constant::Get(MacroAssembler* masm, Register reg) { |
|
||||
__ Move(reg, handle_); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
void Constant::Push(MacroAssembler* masm) { |
|
||||
__ Push(handle_); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
static Operand ToOperand(SlotLocation* loc) { |
|
||||
switch (loc->type()) { |
|
||||
case Slot::PARAMETER: { |
|
||||
int count = CfgGlobals::current()->fun()->scope()->num_parameters(); |
|
||||
return Operand(rbp, (1 + count - loc->index()) * kPointerSize); |
|
||||
} |
|
||||
case Slot::LOCAL: { |
|
||||
const int kOffset = JavaScriptFrameConstants::kLocal0Offset; |
|
||||
return Operand(rbp, kOffset - loc->index() * kPointerSize); |
|
||||
} |
|
||||
default: |
|
||||
UNREACHABLE(); |
|
||||
return Operand(rax, 0); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
|
|
||||
void Constant::MoveToSlot(MacroAssembler* masm, SlotLocation* loc) { |
|
||||
__ Move(ToOperand(loc), handle_); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
void SlotLocation::Get(MacroAssembler* masm, Register reg) { |
|
||||
__ movq(reg, ToOperand(this)); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
void SlotLocation::Set(MacroAssembler* masm, Register reg) { |
|
||||
__ movq(ToOperand(this), reg); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
void SlotLocation::Push(MacroAssembler* masm) { |
|
||||
__ push(ToOperand(this)); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
void SlotLocation::Move(MacroAssembler* masm, Value* value) { |
|
||||
// We dispatch to the value because in some cases (temp or constant) we
|
|
||||
// can use special instruction sequences.
|
|
||||
value->MoveToSlot(masm, this); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
void SlotLocation::MoveToSlot(MacroAssembler* masm, SlotLocation* loc) { |
|
||||
__ movq(kScratchRegister, ToOperand(this)); |
|
||||
__ movq(ToOperand(loc), kScratchRegister); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
void TempLocation::Get(MacroAssembler* masm, Register reg) { |
|
||||
switch (where_) { |
|
||||
case ACCUMULATOR: |
|
||||
if (!reg.is(rax)) __ movq(reg, rax); |
|
||||
break; |
|
||||
case STACK: |
|
||||
__ pop(reg); |
|
||||
break; |
|
||||
case NOT_ALLOCATED: |
|
||||
UNREACHABLE(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
|
|
||||
void TempLocation::Set(MacroAssembler* masm, Register reg) { |
|
||||
switch (where_) { |
|
||||
case ACCUMULATOR: |
|
||||
if (!reg.is(rax)) __ movq(rax, reg); |
|
||||
break; |
|
||||
case STACK: |
|
||||
__ push(reg); |
|
||||
break; |
|
||||
case NOT_ALLOCATED: |
|
||||
UNREACHABLE(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
|
|
||||
void TempLocation::Push(MacroAssembler* masm) { |
|
||||
switch (where_) { |
|
||||
case ACCUMULATOR: |
|
||||
__ push(rax); |
|
||||
break; |
|
||||
case STACK: |
|
||||
case NOT_ALLOCATED: |
|
||||
UNREACHABLE(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
|
|
||||
void TempLocation::Move(MacroAssembler* masm, Value* value) { |
|
||||
switch (where_) { |
|
||||
case ACCUMULATOR: |
|
||||
value->Get(masm, rax); |
|
||||
break; |
|
||||
case STACK: |
|
||||
value->Push(masm); |
|
||||
break; |
|
||||
case NOT_ALLOCATED: |
|
||||
UNREACHABLE(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
|
|
||||
void TempLocation::MoveToSlot(MacroAssembler* masm, SlotLocation* loc) { |
|
||||
switch (where_) { |
|
||||
case ACCUMULATOR: |
|
||||
__ movq(ToOperand(loc), rax); |
|
||||
break; |
|
||||
case STACK: |
|
||||
__ pop(ToOperand(loc)); |
|
||||
break; |
|
||||
case NOT_ALLOCATED: |
|
||||
UNREACHABLE(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
|
|
||||
#undef __ |
|
||||
|
|
||||
} } // namespace v8::internal
|
|
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue