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