mirror of https://github.com/lukechilds/node.git
Ryan
16 years ago
251 changed files with 15652 additions and 2807 deletions
@ -0,0 +1,633 @@ |
|||||
|
// Copyright 2006-2008 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_IA32_CODEGEN_IA32_H_ |
||||
|
#define V8_IA32_CODEGEN_IA32_H_ |
||||
|
|
||||
|
namespace v8 { namespace internal { |
||||
|
|
||||
|
// Forward declarations
|
||||
|
class DeferredCode; |
||||
|
class RegisterAllocator; |
||||
|
class RegisterFile; |
||||
|
|
||||
|
enum InitState { CONST_INIT, NOT_CONST_INIT }; |
||||
|
enum TypeofState { INSIDE_TYPEOF, NOT_INSIDE_TYPEOF }; |
||||
|
|
||||
|
|
||||
|
// -------------------------------------------------------------------------
|
||||
|
// Reference support
|
||||
|
|
||||
|
// A reference is a C++ stack-allocated object that keeps an ECMA
|
||||
|
// reference on the execution stack while in scope. For variables
|
||||
|
// the reference is empty, indicating that it isn't necessary to
|
||||
|
// store state on the stack for keeping track of references to those.
|
||||
|
// For properties, we keep either one (named) or two (indexed) values
|
||||
|
// on the execution stack to represent the reference.
|
||||
|
|
||||
|
class Reference BASE_EMBEDDED { |
||||
|
public: |
||||
|
// The values of the types is important, see size().
|
||||
|
enum Type { ILLEGAL = -1, SLOT = 0, NAMED = 1, KEYED = 2 }; |
||||
|
Reference(CodeGenerator* cgen, Expression* expression); |
||||
|
~Reference(); |
||||
|
|
||||
|
Expression* expression() const { return expression_; } |
||||
|
Type type() const { return type_; } |
||||
|
void set_type(Type value) { |
||||
|
ASSERT(type_ == ILLEGAL); |
||||
|
type_ = value; |
||||
|
} |
||||
|
|
||||
|
// The size the reference takes up on the stack.
|
||||
|
int size() const { return (type_ == ILLEGAL) ? 0 : type_; } |
||||
|
|
||||
|
bool is_illegal() const { return type_ == ILLEGAL; } |
||||
|
bool is_slot() const { return type_ == SLOT; } |
||||
|
bool is_property() const { return type_ == NAMED || type_ == KEYED; } |
||||
|
|
||||
|
// Return the name. Only valid for named property references.
|
||||
|
Handle<String> GetName(); |
||||
|
|
||||
|
// Generate code to push the value of the reference on top of the
|
||||
|
// expression stack. The reference is expected to be already on top of
|
||||
|
// the expression stack, and it is left in place with its value above it.
|
||||
|
void GetValue(TypeofState typeof_state); |
||||
|
|
||||
|
// Like GetValue except that the slot is expected to be written to before
|
||||
|
// being read from again. Thae value of the reference may be invalidated,
|
||||
|
// causing subsequent attempts to read it to fail.
|
||||
|
void TakeValue(TypeofState typeof_state); |
||||
|
|
||||
|
// Generate code to store the value on top of the expression stack in the
|
||||
|
// reference. The reference is expected to be immediately below the value
|
||||
|
// on the expression stack. The stored value is left in place (with the
|
||||
|
// reference intact below it) to support chained assignments.
|
||||
|
void SetValue(InitState init_state); |
||||
|
|
||||
|
private: |
||||
|
CodeGenerator* cgen_; |
||||
|
Expression* expression_; |
||||
|
Type type_; |
||||
|
}; |
||||
|
|
||||
|
|
||||
|
// -------------------------------------------------------------------------
|
||||
|
// Control destinations.
|
||||
|
|
||||
|
// A control destination encapsulates a pair of jump targets and a
|
||||
|
// flag indicating which one is the preferred fall-through. The
|
||||
|
// preferred fall-through must be unbound, the other may be already
|
||||
|
// bound (ie, a backward target).
|
||||
|
//
|
||||
|
// The true and false targets may be jumped to unconditionally or
|
||||
|
// control may split conditionally. Unconditional jumping and
|
||||
|
// splitting should be emitted in tail position (as the last thing
|
||||
|
// when compiling an expression) because they can cause either label
|
||||
|
// to be bound or the non-fall through to be jumped to leaving an
|
||||
|
// invalid virtual frame.
|
||||
|
//
|
||||
|
// The labels in the control destination can be extracted and
|
||||
|
// manipulated normally without affecting the state of the
|
||||
|
// destination.
|
||||
|
|
||||
|
class ControlDestination BASE_EMBEDDED { |
||||
|
public: |
||||
|
ControlDestination(JumpTarget* true_target, |
||||
|
JumpTarget* false_target, |
||||
|
bool true_is_fall_through) |
||||
|
: true_target_(true_target), |
||||
|
false_target_(false_target), |
||||
|
true_is_fall_through_(true_is_fall_through), |
||||
|
is_used_(false) { |
||||
|
ASSERT(true_is_fall_through ? !true_target->is_bound() |
||||
|
: !false_target->is_bound()); |
||||
|
} |
||||
|
|
||||
|
// Accessors for the jump targets. Directly jumping or branching to
|
||||
|
// or binding the targets will not update the destination's state.
|
||||
|
JumpTarget* true_target() const { return true_target_; } |
||||
|
JumpTarget* false_target() const { return false_target_; } |
||||
|
|
||||
|
// True if the the destination has been jumped to unconditionally or
|
||||
|
// control has been split to both targets. This predicate does not
|
||||
|
// test whether the targets have been extracted and manipulated as
|
||||
|
// raw jump targets.
|
||||
|
bool is_used() const { return is_used_; } |
||||
|
|
||||
|
// True if the destination is used and the true target (respectively
|
||||
|
// false target) was the fall through. If the target is backward,
|
||||
|
// "fall through" included jumping unconditionally to it.
|
||||
|
bool true_was_fall_through() const { |
||||
|
return is_used_ && true_is_fall_through_; |
||||
|
} |
||||
|
|
||||
|
bool false_was_fall_through() const { |
||||
|
return is_used_ && !true_is_fall_through_; |
||||
|
} |
||||
|
|
||||
|
// Emit a branch to one of the true or false targets, and bind the
|
||||
|
// other target. Because this binds the fall-through target, it
|
||||
|
// should be emitted in tail position (as the last thing when
|
||||
|
// compiling an expression).
|
||||
|
void Split(Condition cc) { |
||||
|
ASSERT(!is_used_); |
||||
|
if (true_is_fall_through_) { |
||||
|
false_target_->Branch(NegateCondition(cc)); |
||||
|
true_target_->Bind(); |
||||
|
} else { |
||||
|
true_target_->Branch(cc); |
||||
|
false_target_->Bind(); |
||||
|
} |
||||
|
is_used_ = true; |
||||
|
} |
||||
|
|
||||
|
// Emit an unconditional jump in tail position, to the true target
|
||||
|
// (if the argument is true) or the false target. The "jump" will
|
||||
|
// actually bind the jump target if it is forward, jump to it if it
|
||||
|
// is backward.
|
||||
|
void Goto(bool where) { |
||||
|
ASSERT(!is_used_); |
||||
|
JumpTarget* target = where ? true_target_ : false_target_; |
||||
|
if (target->is_bound()) { |
||||
|
target->Jump(); |
||||
|
} else { |
||||
|
target->Bind(); |
||||
|
} |
||||
|
is_used_ = true; |
||||
|
true_is_fall_through_ = where; |
||||
|
} |
||||
|
|
||||
|
// Mark this jump target as used as if Goto had been called, but
|
||||
|
// without generating a jump or binding a label (the control effect
|
||||
|
// should have already happened). This is used when the left
|
||||
|
// subexpression of the short-circuit boolean operators are
|
||||
|
// compiled.
|
||||
|
void Use(bool where) { |
||||
|
ASSERT(!is_used_); |
||||
|
ASSERT((where ? true_target_ : false_target_)->is_bound()); |
||||
|
is_used_ = true; |
||||
|
true_is_fall_through_ = where; |
||||
|
} |
||||
|
|
||||
|
// Swap the true and false targets but keep the same actual label as
|
||||
|
// the fall through. This is used when compiling negated
|
||||
|
// expressions, where we want to swap the targets but preserve the
|
||||
|
// state.
|
||||
|
void Invert() { |
||||
|
JumpTarget* temp_target = true_target_; |
||||
|
true_target_ = false_target_; |
||||
|
false_target_ = temp_target; |
||||
|
|
||||
|
true_is_fall_through_ = !true_is_fall_through_; |
||||
|
} |
||||
|
|
||||
|
private: |
||||
|
// True and false jump targets.
|
||||
|
JumpTarget* true_target_; |
||||
|
JumpTarget* false_target_; |
||||
|
|
||||
|
// Before using the destination: true if the true target is the
|
||||
|
// preferred fall through, false if the false target is. After
|
||||
|
// using the destination: true if the true target was actually used
|
||||
|
// as the fall through, false if the false target was.
|
||||
|
bool true_is_fall_through_; |
||||
|
|
||||
|
// True if the Split or Goto functions have been called.
|
||||
|
bool is_used_; |
||||
|
}; |
||||
|
|
||||
|
|
||||
|
// -------------------------------------------------------------------------
|
||||
|
// Code generation state
|
||||
|
|
||||
|
// The state is passed down the AST by the code generator (and back up, in
|
||||
|
// the form of the state of the jump target pair). It is threaded through
|
||||
|
// the call stack. Constructing a state implicitly pushes it on the owning
|
||||
|
// code generator's stack of states, and destroying one implicitly pops it.
|
||||
|
//
|
||||
|
// The code generator state is only used for expressions, so statements have
|
||||
|
// the initial state.
|
||||
|
|
||||
|
class CodeGenState BASE_EMBEDDED { |
||||
|
public: |
||||
|
// Create an initial code generator state. Destroying the initial state
|
||||
|
// leaves the code generator with a NULL state.
|
||||
|
explicit CodeGenState(CodeGenerator* owner); |
||||
|
|
||||
|
// Create a code generator state based on a code generator's current
|
||||
|
// state. The new state may or may not be inside a typeof, and has its
|
||||
|
// own control destination.
|
||||
|
CodeGenState(CodeGenerator* owner, |
||||
|
TypeofState typeof_state, |
||||
|
ControlDestination* destination); |
||||
|
|
||||
|
// Destroy a code generator state and restore the owning code generator's
|
||||
|
// previous state.
|
||||
|
~CodeGenState(); |
||||
|
|
||||
|
// Accessors for the state.
|
||||
|
TypeofState typeof_state() const { return typeof_state_; } |
||||
|
ControlDestination* destination() const { return destination_; } |
||||
|
|
||||
|
private: |
||||
|
// The owning code generator.
|
||||
|
CodeGenerator* owner_; |
||||
|
|
||||
|
// A flag indicating whether we are compiling the immediate subexpression
|
||||
|
// of a typeof expression.
|
||||
|
TypeofState typeof_state_; |
||||
|
|
||||
|
// A control destination in case the expression has a control-flow
|
||||
|
// effect.
|
||||
|
ControlDestination* destination_; |
||||
|
|
||||
|
// The previous state of the owning code generator, restored when
|
||||
|
// this state is destroyed.
|
||||
|
CodeGenState* previous_; |
||||
|
}; |
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
// -------------------------------------------------------------------------
|
||||
|
// CodeGenerator
|
||||
|
|
||||
|
class CodeGenerator: public AstVisitor { |
||||
|
public: |
||||
|
// Takes a function literal, generates code for it. This function should only
|
||||
|
// be called by compiler.cc.
|
||||
|
static Handle<Code> MakeCode(FunctionLiteral* fun, |
||||
|
Handle<Script> script, |
||||
|
bool is_eval); |
||||
|
|
||||
|
#ifdef ENABLE_LOGGING_AND_PROFILING |
||||
|
static bool ShouldGenerateLog(Expression* type); |
||||
|
#endif |
||||
|
|
||||
|
static void SetFunctionInfo(Handle<JSFunction> fun, |
||||
|
int length, |
||||
|
int function_token_position, |
||||
|
int start_position, |
||||
|
int end_position, |
||||
|
bool is_expression, |
||||
|
bool is_toplevel, |
||||
|
Handle<Script> script, |
||||
|
Handle<String> inferred_name); |
||||
|
|
||||
|
// Accessors
|
||||
|
MacroAssembler* masm() { return masm_; } |
||||
|
|
||||
|
VirtualFrame* frame() const { return frame_; } |
||||
|
|
||||
|
bool has_valid_frame() const { return frame_ != NULL; } |
||||
|
|
||||
|
// Set the virtual frame to be new_frame, with non-frame register
|
||||
|
// reference counts given by non_frame_registers. The non-frame
|
||||
|
// register reference counts of the old frame are returned in
|
||||
|
// non_frame_registers.
|
||||
|
void SetFrame(VirtualFrame* new_frame, RegisterFile* non_frame_registers); |
||||
|
|
||||
|
void DeleteFrame(); |
||||
|
|
||||
|
RegisterAllocator* allocator() const { return allocator_; } |
||||
|
|
||||
|
CodeGenState* state() { return state_; } |
||||
|
void set_state(CodeGenState* state) { state_ = state; } |
||||
|
|
||||
|
void AddDeferred(DeferredCode* code) { deferred_.Add(code); } |
||||
|
|
||||
|
bool in_spilled_code() const { return in_spilled_code_; } |
||||
|
void set_in_spilled_code(bool flag) { in_spilled_code_ = flag; } |
||||
|
|
||||
|
private: |
||||
|
// Construction/Destruction
|
||||
|
CodeGenerator(int buffer_size, Handle<Script> script, bool is_eval); |
||||
|
virtual ~CodeGenerator() { delete masm_; } |
||||
|
|
||||
|
// Accessors
|
||||
|
Scope* scope() const { return scope_; } |
||||
|
|
||||
|
// Clearing and generating deferred code.
|
||||
|
void ClearDeferred(); |
||||
|
void ProcessDeferred(); |
||||
|
|
||||
|
bool is_eval() { return is_eval_; } |
||||
|
|
||||
|
// State
|
||||
|
TypeofState typeof_state() const { return state_->typeof_state(); } |
||||
|
ControlDestination* destination() const { return state_->destination(); } |
||||
|
|
||||
|
// Track loop nesting level.
|
||||
|
int loop_nesting() const { return loop_nesting_; } |
||||
|
void IncrementLoopNesting() { loop_nesting_++; } |
||||
|
void DecrementLoopNesting() { loop_nesting_--; } |
||||
|
|
||||
|
|
||||
|
// Node visitors.
|
||||
|
void VisitStatements(ZoneList<Statement*>* statements); |
||||
|
|
||||
|
#define DEF_VISIT(type) \ |
||||
|
void Visit##type(type* node); |
||||
|
NODE_LIST(DEF_VISIT) |
||||
|
#undef DEF_VISIT |
||||
|
|
||||
|
// Visit a statement and then spill the virtual frame if control flow can
|
||||
|
// reach the end of the statement (ie, it does not exit via break,
|
||||
|
// continue, return, or throw). This function is used temporarily while
|
||||
|
// the code generator is being transformed.
|
||||
|
void VisitAndSpill(Statement* statement); |
||||
|
|
||||
|
// Visit a list of statements and then spill the virtual frame if control
|
||||
|
// flow can reach the end of the list.
|
||||
|
void VisitStatementsAndSpill(ZoneList<Statement*>* statements); |
||||
|
|
||||
|
// Main code generation function
|
||||
|
void GenCode(FunctionLiteral* fun); |
||||
|
|
||||
|
// Generate the return sequence code. Should be called no more than
|
||||
|
// once per compiled function, immediately after binding the return
|
||||
|
// target (which can not be done more than once).
|
||||
|
void GenerateReturnSequence(Result* return_value); |
||||
|
|
||||
|
// The following are used by class Reference.
|
||||
|
void LoadReference(Reference* ref); |
||||
|
void UnloadReference(Reference* ref); |
||||
|
|
||||
|
Operand ContextOperand(Register context, int index) const { |
||||
|
return Operand(context, Context::SlotOffset(index)); |
||||
|
} |
||||
|
|
||||
|
Operand SlotOperand(Slot* slot, Register tmp); |
||||
|
|
||||
|
Operand ContextSlotOperandCheckExtensions(Slot* slot, |
||||
|
Result tmp, |
||||
|
JumpTarget* slow); |
||||
|
|
||||
|
// Expressions
|
||||
|
Operand GlobalObject() const { |
||||
|
return ContextOperand(esi, Context::GLOBAL_INDEX); |
||||
|
} |
||||
|
|
||||
|
void LoadCondition(Expression* x, |
||||
|
TypeofState typeof_state, |
||||
|
ControlDestination* destination, |
||||
|
bool force_control); |
||||
|
void Load(Expression* x, TypeofState typeof_state = NOT_INSIDE_TYPEOF); |
||||
|
void LoadGlobal(); |
||||
|
void LoadGlobalReceiver(); |
||||
|
|
||||
|
// Generate code to push the value of an expression on top of the frame
|
||||
|
// and then spill the frame fully to memory. This function is used
|
||||
|
// temporarily while the code generator is being transformed.
|
||||
|
void LoadAndSpill(Expression* expression, |
||||
|
TypeofState typeof_state = NOT_INSIDE_TYPEOF); |
||||
|
|
||||
|
// Read a value from a slot and leave it on top of the expression stack.
|
||||
|
void LoadFromSlot(Slot* slot, TypeofState typeof_state); |
||||
|
Result LoadFromGlobalSlotCheckExtensions(Slot* slot, |
||||
|
TypeofState typeof_state, |
||||
|
JumpTarget* slow); |
||||
|
|
||||
|
// Store the value on top of the expression stack into a slot, leaving the
|
||||
|
// value in place.
|
||||
|
void StoreToSlot(Slot* slot, InitState init_state); |
||||
|
|
||||
|
// Special code for typeof expressions: Unfortunately, we must
|
||||
|
// be careful when loading the expression in 'typeof'
|
||||
|
// expressions. We are not allowed to throw reference errors for
|
||||
|
// non-existing properties of the global object, so we must make it
|
||||
|
// look like an explicit property access, instead of an access
|
||||
|
// through the context chain.
|
||||
|
void LoadTypeofExpression(Expression* x); |
||||
|
|
||||
|
// Translate the value on top of the frame into control flow to the
|
||||
|
// control destination.
|
||||
|
void ToBoolean(ControlDestination* destination); |
||||
|
|
||||
|
void GenericBinaryOperation( |
||||
|
Token::Value op, |
||||
|
SmiAnalysis* type, |
||||
|
OverwriteMode overwrite_mode); |
||||
|
|
||||
|
// If possible, combine two constant smi values using op to produce
|
||||
|
// a smi result, and push it on the virtual frame, all at compile time.
|
||||
|
// Returns true if it succeeds. Otherwise it has no effect.
|
||||
|
bool FoldConstantSmis(Token::Value op, int left, int right); |
||||
|
|
||||
|
// Emit code to perform a binary operation on a constant
|
||||
|
// smi and a likely smi. Consumes the Result *operand.
|
||||
|
void ConstantSmiBinaryOperation(Token::Value op, |
||||
|
Result* operand, |
||||
|
Handle<Object> constant_operand, |
||||
|
SmiAnalysis* type, |
||||
|
bool reversed, |
||||
|
OverwriteMode overwrite_mode); |
||||
|
|
||||
|
// Emit code to perform a binary operation on two likely smis.
|
||||
|
// The code to handle smi arguments is produced inline.
|
||||
|
// Consumes the Results *left and *right.
|
||||
|
void LikelySmiBinaryOperation(Token::Value op, |
||||
|
Result* left, |
||||
|
Result* right, |
||||
|
OverwriteMode overwrite_mode); |
||||
|
|
||||
|
void Comparison(Condition cc, |
||||
|
bool strict, |
||||
|
ControlDestination* destination); |
||||
|
|
||||
|
// To prevent long attacker-controlled byte sequences, integer constants
|
||||
|
// from the JavaScript source are loaded in two parts if they are larger
|
||||
|
// than 16 bits.
|
||||
|
static const int kMaxSmiInlinedBits = 16; |
||||
|
bool IsUnsafeSmi(Handle<Object> value); |
||||
|
// Load an integer constant x into a register target using
|
||||
|
// at most 16 bits of user-controlled data per assembly operation.
|
||||
|
void LoadUnsafeSmi(Register target, Handle<Object> value); |
||||
|
|
||||
|
void CallWithArguments(ZoneList<Expression*>* arguments, int position); |
||||
|
|
||||
|
void CheckStack(); |
||||
|
|
||||
|
struct InlineRuntimeLUT { |
||||
|
void (CodeGenerator::*method)(ZoneList<Expression*>*); |
||||
|
const char* name; |
||||
|
}; |
||||
|
|
||||
|
static InlineRuntimeLUT* FindInlineRuntimeLUT(Handle<String> name); |
||||
|
bool CheckForInlineRuntimeCall(CallRuntime* node); |
||||
|
static bool PatchInlineRuntimeEntry(Handle<String> name, |
||||
|
const InlineRuntimeLUT& new_entry, |
||||
|
InlineRuntimeLUT* old_entry); |
||||
|
|
||||
|
Handle<JSFunction> BuildBoilerplate(FunctionLiteral* node); |
||||
|
void ProcessDeclarations(ZoneList<Declaration*>* declarations); |
||||
|
|
||||
|
Handle<Code> ComputeCallInitialize(int argc); |
||||
|
Handle<Code> ComputeCallInitializeInLoop(int argc); |
||||
|
|
||||
|
// Declare global variables and functions in the given array of
|
||||
|
// name/value pairs.
|
||||
|
void DeclareGlobals(Handle<FixedArray> pairs); |
||||
|
|
||||
|
// Instantiate the function boilerplate.
|
||||
|
void InstantiateBoilerplate(Handle<JSFunction> boilerplate); |
||||
|
|
||||
|
// Support for type checks.
|
||||
|
void GenerateIsSmi(ZoneList<Expression*>* args); |
||||
|
void GenerateIsNonNegativeSmi(ZoneList<Expression*>* args); |
||||
|
void GenerateIsArray(ZoneList<Expression*>* args); |
||||
|
|
||||
|
// Support for arguments.length and arguments[?].
|
||||
|
void GenerateArgumentsLength(ZoneList<Expression*>* args); |
||||
|
void GenerateArgumentsAccess(ZoneList<Expression*>* args); |
||||
|
|
||||
|
// Support for accessing the value field of an object (used by Date).
|
||||
|
void GenerateValueOf(ZoneList<Expression*>* args); |
||||
|
void GenerateSetValueOf(ZoneList<Expression*>* args); |
||||
|
|
||||
|
// Fast support for charCodeAt(n).
|
||||
|
void GenerateFastCharCodeAt(ZoneList<Expression*>* args); |
||||
|
|
||||
|
// Fast support for object equality testing.
|
||||
|
void GenerateObjectEquals(ZoneList<Expression*>* args); |
||||
|
|
||||
|
void GenerateLog(ZoneList<Expression*>* args); |
||||
|
|
||||
|
void GenerateGetFramePointer(ZoneList<Expression*>* args); |
||||
|
|
||||
|
// Methods and constants for fast case switch statement support.
|
||||
|
//
|
||||
|
// Only allow fast-case switch if the range of labels is at most
|
||||
|
// this factor times the number of case labels.
|
||||
|
// Value is derived from comparing the size of code generated by the normal
|
||||
|
// switch code for Smi-labels to the size of a single pointer. If code
|
||||
|
// quality increases this number should be decreased to match.
|
||||
|
static const int kFastSwitchMaxOverheadFactor = 5; |
||||
|
|
||||
|
// Minimal number of switch cases required before we allow jump-table
|
||||
|
// optimization.
|
||||
|
static const int kFastSwitchMinCaseCount = 5; |
||||
|
|
||||
|
// The limit of the range of a fast-case switch, as a factor of the number
|
||||
|
// of cases of the switch. Each platform should return a value that
|
||||
|
// is optimal compared to the default code generated for a switch statement
|
||||
|
// on that platform.
|
||||
|
int FastCaseSwitchMaxOverheadFactor(); |
||||
|
|
||||
|
// The minimal number of cases in a switch before the fast-case switch
|
||||
|
// optimization is enabled. Each platform should return a value that
|
||||
|
// is optimal compared to the default code generated for a switch statement
|
||||
|
// on that platform.
|
||||
|
int FastCaseSwitchMinCaseCount(); |
||||
|
|
||||
|
// Allocate a jump table and create code to jump through it.
|
||||
|
// Should call GenerateFastCaseSwitchCases to generate the code for
|
||||
|
// all the cases at the appropriate point.
|
||||
|
void GenerateFastCaseSwitchJumpTable(SwitchStatement* node, |
||||
|
int min_index, |
||||
|
int range, |
||||
|
Label* fail_label, |
||||
|
Vector<Label*> case_targets, |
||||
|
Vector<Label> case_labels); |
||||
|
|
||||
|
// Generate the code for cases for the fast case switch.
|
||||
|
// Called by GenerateFastCaseSwitchJumpTable.
|
||||
|
void GenerateFastCaseSwitchCases(SwitchStatement* node, |
||||
|
Vector<Label> case_labels, |
||||
|
VirtualFrame* start_frame); |
||||
|
|
||||
|
// Fast support for constant-Smi switches.
|
||||
|
void GenerateFastCaseSwitchStatement(SwitchStatement* node, |
||||
|
int min_index, |
||||
|
int range, |
||||
|
int default_index); |
||||
|
|
||||
|
// Fast support for constant-Smi switches. Tests whether switch statement
|
||||
|
// permits optimization and calls GenerateFastCaseSwitch if it does.
|
||||
|
// Returns true if the fast-case switch was generated, and false if not.
|
||||
|
bool TryGenerateFastCaseSwitchStatement(SwitchStatement* node); |
||||
|
|
||||
|
// Methods used to indicate which source code is generated for. Source
|
||||
|
// positions are collected by the assembler and emitted with the relocation
|
||||
|
// information.
|
||||
|
void CodeForFunctionPosition(FunctionLiteral* fun); |
||||
|
void CodeForReturnPosition(FunctionLiteral* fun); |
||||
|
void CodeForStatementPosition(Node* node); |
||||
|
void CodeForSourcePosition(int pos); |
||||
|
|
||||
|
#ifdef DEBUG |
||||
|
// True if the registers are valid for entry to a block. There should be
|
||||
|
// no frame-external references to eax, ebx, ecx, edx, or edi.
|
||||
|
bool HasValidEntryRegisters(); |
||||
|
#endif |
||||
|
|
||||
|
bool is_eval_; // Tells whether code is generated for eval.
|
||||
|
Handle<Script> script_; |
||||
|
List<DeferredCode*> deferred_; |
||||
|
|
||||
|
// Assembler
|
||||
|
MacroAssembler* masm_; // to generate code
|
||||
|
|
||||
|
// Code generation state
|
||||
|
Scope* scope_; |
||||
|
VirtualFrame* frame_; |
||||
|
RegisterAllocator* allocator_; |
||||
|
CodeGenState* state_; |
||||
|
int loop_nesting_; |
||||
|
|
||||
|
// Jump targets.
|
||||
|
// The target of the return from the function.
|
||||
|
BreakTarget function_return_; |
||||
|
|
||||
|
// True if the function return is shadowed (ie, jumping to the target
|
||||
|
// function_return_ does not jump to the true function return, but rather
|
||||
|
// to some unlinking code).
|
||||
|
bool function_return_is_shadowed_; |
||||
|
|
||||
|
// True when we are in code that expects the virtual frame to be fully
|
||||
|
// spilled. Some virtual frame function are disabled in DEBUG builds when
|
||||
|
// called from spilled code, because they do not leave the virtual frame
|
||||
|
// in a spilled state.
|
||||
|
bool in_spilled_code_; |
||||
|
|
||||
|
static InlineRuntimeLUT kInlineRuntimeLUT[]; |
||||
|
|
||||
|
friend class VirtualFrame; |
||||
|
friend class JumpTarget; |
||||
|
friend class Reference; |
||||
|
friend class Result; |
||||
|
|
||||
|
friend class CodeGeneratorPatcher; // Used in test-log-ia32.cc
|
||||
|
|
||||
|
DISALLOW_COPY_AND_ASSIGN(CodeGenerator); |
||||
|
}; |
||||
|
|
||||
|
|
||||
|
} } // namespace v8::internal
|
||||
|
|
||||
|
#endif // V8_IA32_CODEGEN_IA32_H_
|
@ -0,0 +1,371 @@ |
|||||
|
// Copyright 2006-2008 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_IA32_MACRO_ASSEMBLER_IA32_H_ |
||||
|
#define V8_IA32_MACRO_ASSEMBLER_IA32_H_ |
||||
|
|
||||
|
#include "assembler.h" |
||||
|
|
||||
|
namespace v8 { namespace internal { |
||||
|
|
||||
|
// Forward declaration.
|
||||
|
class JumpTarget; |
||||
|
|
||||
|
|
||||
|
// Helper types to make flags easier to read at call sites.
|
||||
|
enum InvokeFlag { |
||||
|
CALL_FUNCTION, |
||||
|
JUMP_FUNCTION |
||||
|
}; |
||||
|
|
||||
|
enum CodeLocation { |
||||
|
IN_JAVASCRIPT, |
||||
|
IN_JS_ENTRY, |
||||
|
IN_C_ENTRY |
||||
|
}; |
||||
|
|
||||
|
enum HandlerType { |
||||
|
TRY_CATCH_HANDLER, |
||||
|
TRY_FINALLY_HANDLER, |
||||
|
JS_ENTRY_HANDLER |
||||
|
}; |
||||
|
|
||||
|
|
||||
|
// MacroAssembler implements a collection of frequently used macros.
|
||||
|
class MacroAssembler: public Assembler { |
||||
|
public: |
||||
|
MacroAssembler(void* buffer, int size); |
||||
|
|
||||
|
// ---------------------------------------------------------------------------
|
||||
|
// GC Support
|
||||
|
|
||||
|
// Set the remembered set bit for [object+offset].
|
||||
|
// object is the object being stored into, value is the object being stored.
|
||||
|
// If offset is zero, then the scratch register contains the array index into
|
||||
|
// the elements array represented as a Smi.
|
||||
|
// All registers are clobbered by the operation.
|
||||
|
void RecordWrite(Register object, |
||||
|
int offset, |
||||
|
Register value, |
||||
|
Register scratch); |
||||
|
|
||||
|
#ifdef ENABLE_DEBUGGER_SUPPORT |
||||
|
// ---------------------------------------------------------------------------
|
||||
|
// Debugger Support
|
||||
|
|
||||
|
void SaveRegistersToMemory(RegList regs); |
||||
|
void RestoreRegistersFromMemory(RegList regs); |
||||
|
void PushRegistersFromMemory(RegList regs); |
||||
|
void PopRegistersToMemory(RegList regs); |
||||
|
void CopyRegistersFromStackToMemory(Register base, |
||||
|
Register scratch, |
||||
|
RegList regs); |
||||
|
#endif |
||||
|
|
||||
|
// ---------------------------------------------------------------------------
|
||||
|
// Activation frames
|
||||
|
|
||||
|
void EnterInternalFrame() { EnterFrame(StackFrame::INTERNAL); } |
||||
|
void LeaveInternalFrame() { LeaveFrame(StackFrame::INTERNAL); } |
||||
|
|
||||
|
void EnterConstructFrame() { EnterFrame(StackFrame::CONSTRUCT); } |
||||
|
void LeaveConstructFrame() { LeaveFrame(StackFrame::CONSTRUCT); } |
||||
|
|
||||
|
// Enter specific kind of exit frame; either EXIT or
|
||||
|
// EXIT_DEBUG. Expects the number of arguments in register eax and
|
||||
|
// sets up the number of arguments in register edi and the pointer
|
||||
|
// to the first argument in register esi.
|
||||
|
void EnterExitFrame(StackFrame::Type type); |
||||
|
|
||||
|
// Leave the current exit frame. Expects the return value in
|
||||
|
// register eax:edx (untouched) and the pointer to the first
|
||||
|
// argument in register esi.
|
||||
|
void LeaveExitFrame(StackFrame::Type type); |
||||
|
|
||||
|
|
||||
|
// ---------------------------------------------------------------------------
|
||||
|
// JavaScript invokes
|
||||
|
|
||||
|
// Invoke the JavaScript function code by either calling or jumping.
|
||||
|
void InvokeCode(const Operand& code, |
||||
|
const ParameterCount& expected, |
||||
|
const ParameterCount& actual, |
||||
|
InvokeFlag flag); |
||||
|
|
||||
|
void InvokeCode(Handle<Code> code, |
||||
|
const ParameterCount& expected, |
||||
|
const ParameterCount& actual, |
||||
|
RelocInfo::Mode rmode, |
||||
|
InvokeFlag flag); |
||||
|
|
||||
|
// Invoke the JavaScript function in the given register. Changes the
|
||||
|
// current context to the context in the function before invoking.
|
||||
|
void InvokeFunction(Register function, |
||||
|
const ParameterCount& actual, |
||||
|
InvokeFlag flag); |
||||
|
|
||||
|
// Invoke specified builtin JavaScript function. Adds an entry to
|
||||
|
// the unresolved list if the name does not resolve.
|
||||
|
void InvokeBuiltin(Builtins::JavaScript id, InvokeFlag flag); |
||||
|
|
||||
|
// Store the code object for the given builtin in the target register.
|
||||
|
void GetBuiltinEntry(Register target, Builtins::JavaScript id); |
||||
|
|
||||
|
// Expression support
|
||||
|
void Set(Register dst, const Immediate& x); |
||||
|
void Set(const Operand& dst, const Immediate& x); |
||||
|
|
||||
|
// Compare object type for heap object.
|
||||
|
// Incoming register is heap_object and outgoing register is map.
|
||||
|
void CmpObjectType(Register heap_object, InstanceType type, Register map); |
||||
|
|
||||
|
// Compare instance type for map.
|
||||
|
void CmpInstanceType(Register map, InstanceType type); |
||||
|
|
||||
|
// FCmp is similar to integer cmp, but requires unsigned
|
||||
|
// jcc instructions (je, ja, jae, jb, jbe, je, and jz).
|
||||
|
void FCmp(); |
||||
|
|
||||
|
// ---------------------------------------------------------------------------
|
||||
|
// Exception handling
|
||||
|
|
||||
|
// Push a new try handler and link into try handler chain.
|
||||
|
// The return address must be pushed before calling this helper.
|
||||
|
// On exit, eax contains TOS (next_sp).
|
||||
|
void PushTryHandler(CodeLocation try_location, HandlerType type); |
||||
|
|
||||
|
|
||||
|
// ---------------------------------------------------------------------------
|
||||
|
// Inline caching support
|
||||
|
|
||||
|
// Generates code that verifies that the maps of objects in the
|
||||
|
// prototype chain of object hasn't changed since the code was
|
||||
|
// generated and branches to the miss label if any map has. If
|
||||
|
// necessary the function also generates code for security check
|
||||
|
// in case of global object holders. The scratch and holder
|
||||
|
// registers are always clobbered, but the object register is only
|
||||
|
// clobbered if it the same as the holder register. The function
|
||||
|
// returns a register containing the holder - either object_reg or
|
||||
|
// holder_reg.
|
||||
|
Register CheckMaps(JSObject* object, Register object_reg, |
||||
|
JSObject* holder, Register holder_reg, |
||||
|
Register scratch, Label* miss); |
||||
|
|
||||
|
// Generate code for checking access rights - used for security checks
|
||||
|
// on access to global objects across environments. The holder register
|
||||
|
// is left untouched, but the scratch register is clobbered.
|
||||
|
void CheckAccessGlobalProxy(Register holder_reg, |
||||
|
Register scratch, |
||||
|
Label* miss); |
||||
|
|
||||
|
|
||||
|
// ---------------------------------------------------------------------------
|
||||
|
// Support functions.
|
||||
|
|
||||
|
// Check if result is zero and op is negative.
|
||||
|
void NegativeZeroTest(Register result, Register op, Label* then_label); |
||||
|
|
||||
|
// Check if result is zero and op is negative in code using jump targets.
|
||||
|
void NegativeZeroTest(CodeGenerator* cgen, |
||||
|
Register result, |
||||
|
Register op, |
||||
|
JumpTarget* then_target); |
||||
|
|
||||
|
// Check if result is zero and any of op1 and op2 are negative.
|
||||
|
// Register scratch is destroyed, and it must be different from op2.
|
||||
|
void NegativeZeroTest(Register result, Register op1, Register op2, |
||||
|
Register scratch, Label* then_label); |
||||
|
|
||||
|
// Try to get function prototype of a function and puts the value in
|
||||
|
// the result register. Checks that the function really is a
|
||||
|
// function and jumps to the miss label if the fast checks fail. The
|
||||
|
// function register will be untouched; the other registers may be
|
||||
|
// clobbered.
|
||||
|
void TryGetFunctionPrototype(Register function, |
||||
|
Register result, |
||||
|
Register scratch, |
||||
|
Label* miss); |
||||
|
|
||||
|
// Generates code for reporting that an illegal operation has
|
||||
|
// occurred.
|
||||
|
void IllegalOperation(int num_arguments); |
||||
|
|
||||
|
// ---------------------------------------------------------------------------
|
||||
|
// Runtime calls
|
||||
|
|
||||
|
// Call a code stub.
|
||||
|
void CallStub(CodeStub* stub); |
||||
|
|
||||
|
// Return from a code stub after popping its arguments.
|
||||
|
void StubReturn(int argc); |
||||
|
|
||||
|
// Call a runtime routine.
|
||||
|
// Eventually this should be used for all C calls.
|
||||
|
void CallRuntime(Runtime::Function* f, int num_arguments); |
||||
|
|
||||
|
// Convenience function: Same as above, but takes the fid instead.
|
||||
|
void CallRuntime(Runtime::FunctionId id, int num_arguments); |
||||
|
|
||||
|
// Tail call of a runtime routine (jump).
|
||||
|
// Like JumpToBuiltin, but also takes care of passing the number
|
||||
|
// of arguments.
|
||||
|
void TailCallRuntime(const ExternalReference& ext, int num_arguments); |
||||
|
|
||||
|
// Jump to the builtin routine.
|
||||
|
void JumpToBuiltin(const ExternalReference& ext); |
||||
|
|
||||
|
|
||||
|
// ---------------------------------------------------------------------------
|
||||
|
// Utilities
|
||||
|
|
||||
|
void Ret(); |
||||
|
|
||||
|
struct Unresolved { |
||||
|
int pc; |
||||
|
uint32_t flags; // see Bootstrapper::FixupFlags decoders/encoders.
|
||||
|
const char* name; |
||||
|
}; |
||||
|
List<Unresolved>* unresolved() { return &unresolved_; } |
||||
|
|
||||
|
Handle<Object> CodeObject() { return code_object_; } |
||||
|
|
||||
|
|
||||
|
// ---------------------------------------------------------------------------
|
||||
|
// StatsCounter support
|
||||
|
|
||||
|
void SetCounter(StatsCounter* counter, int value); |
||||
|
void IncrementCounter(StatsCounter* counter, int value); |
||||
|
void DecrementCounter(StatsCounter* counter, int value); |
||||
|
|
||||
|
|
||||
|
// ---------------------------------------------------------------------------
|
||||
|
// Debugging
|
||||
|
|
||||
|
// Calls Abort(msg) if the condition cc is not satisfied.
|
||||
|
// Use --debug_code to enable.
|
||||
|
void Assert(Condition cc, const char* msg); |
||||
|
|
||||
|
// Like Assert(), but always enabled.
|
||||
|
void Check(Condition cc, const char* msg); |
||||
|
|
||||
|
// Print a message to stdout and abort execution.
|
||||
|
void Abort(const char* msg); |
||||
|
|
||||
|
// Verify restrictions about code generated in stubs.
|
||||
|
void set_generating_stub(bool value) { generating_stub_ = value; } |
||||
|
bool generating_stub() { return generating_stub_; } |
||||
|
void set_allow_stub_calls(bool value) { allow_stub_calls_ = value; } |
||||
|
bool allow_stub_calls() { return allow_stub_calls_; } |
||||
|
|
||||
|
private: |
||||
|
List<Unresolved> unresolved_; |
||||
|
bool generating_stub_; |
||||
|
bool allow_stub_calls_; |
||||
|
Handle<Object> code_object_; // This handle will be patched with the code
|
||||
|
// code object on installation.
|
||||
|
|
||||
|
// Helper functions for generating invokes.
|
||||
|
void InvokePrologue(const ParameterCount& expected, |
||||
|
const ParameterCount& actual, |
||||
|
Handle<Code> code_constant, |
||||
|
const Operand& code_operand, |
||||
|
Label* done, |
||||
|
InvokeFlag flag); |
||||
|
|
||||
|
// Get the code for the given builtin. Returns if able to resolve
|
||||
|
// the function in the 'resolved' flag.
|
||||
|
Handle<Code> ResolveBuiltin(Builtins::JavaScript id, bool* resolved); |
||||
|
|
||||
|
// Activation support.
|
||||
|
void EnterFrame(StackFrame::Type type); |
||||
|
void LeaveFrame(StackFrame::Type type); |
||||
|
}; |
||||
|
|
||||
|
|
||||
|
// The code patcher is used to patch (typically) small parts of code e.g. for
|
||||
|
// debugging and other types of instrumentation. When using the code patcher
|
||||
|
// the exact number of bytes specified must be emitted. Is not legal to emit
|
||||
|
// relocation information. If any of these constraints are violated it causes
|
||||
|
// an assertion.
|
||||
|
class CodePatcher { |
||||
|
public: |
||||
|
CodePatcher(byte* address, int size); |
||||
|
virtual ~CodePatcher(); |
||||
|
|
||||
|
// Macro assembler to emit code.
|
||||
|
MacroAssembler* masm() { return &masm_; } |
||||
|
|
||||
|
private: |
||||
|
byte* address_; // The address of the code being patched.
|
||||
|
int size_; // Number of bytes of the expected patch size.
|
||||
|
MacroAssembler masm_; // Macro assembler used to generate the code.
|
||||
|
}; |
||||
|
|
||||
|
|
||||
|
// -----------------------------------------------------------------------------
|
||||
|
// Static helper functions.
|
||||
|
|
||||
|
// Generate an Operand for loading a field from an object.
|
||||
|
static inline Operand FieldOperand(Register object, int offset) { |
||||
|
return Operand(object, offset - kHeapObjectTag); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
// Generate an Operand for loading an indexed field from an object.
|
||||
|
static inline Operand FieldOperand(Register object, |
||||
|
Register index, |
||||
|
ScaleFactor scale, |
||||
|
int offset) { |
||||
|
return Operand(object, index, scale, offset - kHeapObjectTag); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
#ifdef GENERATED_CODE_COVERAGE |
||||
|
extern void LogGeneratedCodeCoverage(const char* file_line); |
||||
|
#define CODE_COVERAGE_STRINGIFY(x) #x |
||||
|
#define CODE_COVERAGE_TOSTRING(x) CODE_COVERAGE_STRINGIFY(x) |
||||
|
#define __FILE_LINE__ __FILE__ ":" CODE_COVERAGE_TOSTRING(__LINE__) |
||||
|
#define ACCESS_MASM(masm) { \ |
||||
|
byte* ia32_coverage_function = \ |
||||
|
reinterpret_cast<byte*>(FUNCTION_ADDR(LogGeneratedCodeCoverage)); \ |
||||
|
masm->pushfd(); \ |
||||
|
masm->pushad(); \ |
||||
|
masm->push(Immediate(reinterpret_cast<int>(&__FILE_LINE__))); \ |
||||
|
masm->call(ia32_coverage_function, RelocInfo::RUNTIME_ENTRY); \ |
||||
|
masm->pop(eax); \ |
||||
|
masm->popad(); \ |
||||
|
masm->popfd(); \ |
||||
|
} \ |
||||
|
masm-> |
||||
|
#else |
||||
|
#define ACCESS_MASM(masm) masm-> |
||||
|
#endif |
||||
|
|
||||
|
|
||||
|
} } // namespace v8::internal
|
||||
|
|
||||
|
#endif // V8_IA32_MACRO_ASSEMBLER_IA32_H_
|
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue