Increase coverage of global loads in optimized code
In the cases where a global property cell cannot be used in the optimized code use standard load ic to get the property instead of bailing out. Review URL: http://codereview.chromium.org/6665026 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@7212 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
ec82215b44
commit
1a6c821b05
@ -1604,27 +1604,26 @@ void FullCodeGenerator::VisitAssignment(Assignment* expr) {
|
||||
break;
|
||||
}
|
||||
|
||||
// For compound assignments we need another deoptimization point after the
|
||||
// variable/property load.
|
||||
if (expr->is_compound()) {
|
||||
{ AccumulatorValueContext context(this);
|
||||
switch (assign_type) {
|
||||
case VARIABLE:
|
||||
EmitVariableLoad(expr->target()->AsVariableProxy()->var());
|
||||
PrepareForBailout(expr->target(), TOS_REG);
|
||||
break;
|
||||
case NAMED_PROPERTY:
|
||||
EmitNamedPropertyLoad(property);
|
||||
PrepareForBailoutForId(expr->CompoundLoadId(), TOS_REG);
|
||||
break;
|
||||
case KEYED_PROPERTY:
|
||||
EmitKeyedPropertyLoad(property);
|
||||
PrepareForBailoutForId(expr->CompoundLoadId(), TOS_REG);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// For property compound assignments we need another deoptimization
|
||||
// point after the property load.
|
||||
if (property != NULL) {
|
||||
PrepareForBailoutForId(expr->CompoundLoadId(), TOS_REG);
|
||||
}
|
||||
|
||||
Token::Value op = expr->binary_op();
|
||||
__ push(r0); // Left operand goes on the stack.
|
||||
VisitForAccumulatorValue(expr->value());
|
||||
@ -3805,7 +3804,11 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) {
|
||||
|
||||
// We need a second deoptimization point after loading the value
|
||||
// in case evaluating the property load my have a side effect.
|
||||
PrepareForBailout(expr->increment(), TOS_REG);
|
||||
if (assign_type == VARIABLE) {
|
||||
PrepareForBailout(expr->expression(), TOS_REG);
|
||||
} else {
|
||||
PrepareForBailout(expr->increment(), TOS_REG);
|
||||
}
|
||||
|
||||
// Call ToNumber only if operand is not a smi.
|
||||
Label no_conversion;
|
||||
|
@ -1725,14 +1725,21 @@ LInstruction* LChunkBuilder::DoConstant(HConstant* instr) {
|
||||
}
|
||||
|
||||
|
||||
LInstruction* LChunkBuilder::DoLoadGlobal(HLoadGlobal* instr) {
|
||||
LLoadGlobal* result = new LLoadGlobal();
|
||||
LInstruction* LChunkBuilder::DoLoadGlobalCell(HLoadGlobalCell* instr) {
|
||||
LLoadGlobalCell* result = new LLoadGlobalCell();
|
||||
return instr->check_hole_value()
|
||||
? AssignEnvironment(DefineAsRegister(result))
|
||||
: DefineAsRegister(result);
|
||||
}
|
||||
|
||||
|
||||
LInstruction* LChunkBuilder::DoLoadGlobalGeneric(HLoadGlobalGeneric* instr) {
|
||||
LOperand* global_object = UseFixed(instr->global_object(), r0);
|
||||
LLoadGlobalGeneric* result = new LLoadGlobalGeneric(global_object);
|
||||
return MarkAsCall(DefineFixed(result, r0), instr);
|
||||
}
|
||||
|
||||
|
||||
LInstruction* LChunkBuilder::DoStoreGlobal(HStoreGlobal* instr) {
|
||||
if (instr->check_hole_value()) {
|
||||
LOperand* temp = TempRegister();
|
||||
|
@ -119,7 +119,8 @@ class LCodeGen;
|
||||
V(LoadElements) \
|
||||
V(LoadExternalArrayPointer) \
|
||||
V(LoadFunctionPrototype) \
|
||||
V(LoadGlobal) \
|
||||
V(LoadGlobalCell) \
|
||||
V(LoadGlobalGeneric) \
|
||||
V(LoadKeyedFastElement) \
|
||||
V(LoadKeyedGeneric) \
|
||||
V(LoadNamedField) \
|
||||
@ -1222,10 +1223,25 @@ class LLoadKeyedGeneric: public LTemplateInstruction<1, 2, 0> {
|
||||
};
|
||||
|
||||
|
||||
class LLoadGlobal: public LTemplateInstruction<1, 0, 0> {
|
||||
class LLoadGlobalCell: public LTemplateInstruction<1, 0, 0> {
|
||||
public:
|
||||
DECLARE_CONCRETE_INSTRUCTION(LoadGlobal, "load-global")
|
||||
DECLARE_HYDROGEN_ACCESSOR(LoadGlobal)
|
||||
DECLARE_CONCRETE_INSTRUCTION(LoadGlobalCell, "load-global-cell")
|
||||
DECLARE_HYDROGEN_ACCESSOR(LoadGlobalCell)
|
||||
};
|
||||
|
||||
|
||||
class LLoadGlobalGeneric: public LTemplateInstruction<1, 1, 0> {
|
||||
public:
|
||||
explicit LLoadGlobalGeneric(LOperand* global_object) {
|
||||
inputs_[0] = global_object;
|
||||
}
|
||||
|
||||
DECLARE_CONCRETE_INSTRUCTION(LoadGlobalGeneric, "load-global-generic")
|
||||
DECLARE_HYDROGEN_ACCESSOR(LoadGlobalGeneric)
|
||||
|
||||
LOperand* global_object() { return inputs_[0]; }
|
||||
Handle<Object> name() const { return hydrogen()->name(); }
|
||||
bool for_typeof() const { return hydrogen()->for_typeof(); }
|
||||
};
|
||||
|
||||
|
||||
|
@ -2106,7 +2106,7 @@ void LCodeGen::DoReturn(LReturn* instr) {
|
||||
}
|
||||
|
||||
|
||||
void LCodeGen::DoLoadGlobal(LLoadGlobal* instr) {
|
||||
void LCodeGen::DoLoadGlobalCell(LLoadGlobalCell* instr) {
|
||||
Register result = ToRegister(instr->result());
|
||||
__ mov(ip, Operand(Handle<Object>(instr->hydrogen()->cell())));
|
||||
__ ldr(result, FieldMemOperand(ip, JSGlobalPropertyCell::kValueOffset));
|
||||
@ -2118,6 +2118,18 @@ void LCodeGen::DoLoadGlobal(LLoadGlobal* instr) {
|
||||
}
|
||||
|
||||
|
||||
void LCodeGen::DoLoadGlobalGeneric(LLoadGlobalGeneric* instr) {
|
||||
ASSERT(ToRegister(instr->global_object()).is(r0));
|
||||
ASSERT(ToRegister(instr->result()).is(r0));
|
||||
|
||||
__ mov(r2, Operand(instr->name()));
|
||||
RelocInfo::Mode mode = instr->for_typeof() ? RelocInfo::CODE_TARGET :
|
||||
RelocInfo::CODE_TARGET_CONTEXT;
|
||||
Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize));
|
||||
CallCode(ic, mode, instr);
|
||||
}
|
||||
|
||||
|
||||
void LCodeGen::DoStoreGlobal(LStoreGlobal* instr) {
|
||||
Register value = ToRegister(instr->InputAt(0));
|
||||
Register scratch = scratch0();
|
||||
|
29
src/ast.cc
29
src/ast.cc
@ -621,24 +621,21 @@ bool Call::ComputeTarget(Handle<Map> type, Handle<String> name) {
|
||||
|
||||
|
||||
bool Call::ComputeGlobalTarget(Handle<GlobalObject> global,
|
||||
Handle<String> name) {
|
||||
LookupResult* lookup) {
|
||||
target_ = Handle<JSFunction>::null();
|
||||
cell_ = Handle<JSGlobalPropertyCell>::null();
|
||||
LookupResult lookup;
|
||||
global->Lookup(*name, &lookup);
|
||||
if (lookup.IsProperty() &&
|
||||
lookup.type() == NORMAL &&
|
||||
lookup.holder() == *global) {
|
||||
cell_ = Handle<JSGlobalPropertyCell>(global->GetPropertyCell(&lookup));
|
||||
if (cell_->value()->IsJSFunction()) {
|
||||
Handle<JSFunction> candidate(JSFunction::cast(cell_->value()));
|
||||
// If the function is in new space we assume it's more likely to
|
||||
// change and thus prefer the general IC code.
|
||||
if (!Heap::InNewSpace(*candidate) &&
|
||||
CanCallWithoutIC(candidate, arguments()->length())) {
|
||||
target_ = candidate;
|
||||
return true;
|
||||
}
|
||||
ASSERT(lookup->IsProperty() &&
|
||||
lookup->type() == NORMAL &&
|
||||
lookup->holder() == *global);
|
||||
cell_ = Handle<JSGlobalPropertyCell>(global->GetPropertyCell(lookup));
|
||||
if (cell_->value()->IsJSFunction()) {
|
||||
Handle<JSFunction> candidate(JSFunction::cast(cell_->value()));
|
||||
// If the function is in new space we assume it's more likely to
|
||||
// change and thus prefer the general IC code.
|
||||
if (!Heap::InNewSpace(*candidate) &&
|
||||
CanCallWithoutIC(candidate, arguments()->length())) {
|
||||
target_ = candidate;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
@ -1307,7 +1307,7 @@ class Call: public Expression {
|
||||
Handle<JSGlobalPropertyCell> cell() { return cell_; }
|
||||
|
||||
bool ComputeTarget(Handle<Map> type, Handle<String> name);
|
||||
bool ComputeGlobalTarget(Handle<GlobalObject> global, Handle<String> name);
|
||||
bool ComputeGlobalTarget(Handle<GlobalObject> global, LookupResult* lookup);
|
||||
|
||||
// Bailout support.
|
||||
int ReturnId() const { return return_id_; }
|
||||
|
@ -1230,12 +1230,17 @@ void HStorePixelArrayElement::PrintDataTo(StringStream* stream) {
|
||||
}
|
||||
|
||||
|
||||
void HLoadGlobal::PrintDataTo(StringStream* stream) {
|
||||
void HLoadGlobalCell::PrintDataTo(StringStream* stream) {
|
||||
stream->Add("[%p]", *cell());
|
||||
if (check_hole_value()) stream->Add(" (deleteable/read-only)");
|
||||
}
|
||||
|
||||
|
||||
void HLoadGlobalGeneric::PrintDataTo(StringStream* stream) {
|
||||
stream->Add("%o ", *name());
|
||||
}
|
||||
|
||||
|
||||
void HStoreGlobal::PrintDataTo(StringStream* stream) {
|
||||
stream->Add("[%p] = ", *cell());
|
||||
value()->PrintNameTo(stream);
|
||||
|
@ -122,7 +122,8 @@ class LChunkBuilder;
|
||||
V(LoadElements) \
|
||||
V(LoadExternalArrayPointer) \
|
||||
V(LoadFunctionPrototype) \
|
||||
V(LoadGlobal) \
|
||||
V(LoadGlobalCell) \
|
||||
V(LoadGlobalGeneric) \
|
||||
V(LoadKeyedFastElement) \
|
||||
V(LoadKeyedGeneric) \
|
||||
V(LoadNamedField) \
|
||||
@ -2785,9 +2786,9 @@ class HUnknownOSRValue: public HTemplateInstruction<0> {
|
||||
};
|
||||
|
||||
|
||||
class HLoadGlobal: public HTemplateInstruction<0> {
|
||||
class HLoadGlobalCell: public HTemplateInstruction<0> {
|
||||
public:
|
||||
HLoadGlobal(Handle<JSGlobalPropertyCell> cell, bool check_hole_value)
|
||||
HLoadGlobalCell(Handle<JSGlobalPropertyCell> cell, bool check_hole_value)
|
||||
: cell_(cell), check_hole_value_(check_hole_value) {
|
||||
set_representation(Representation::Tagged());
|
||||
SetFlag(kUseGVN);
|
||||
@ -2808,11 +2809,11 @@ class HLoadGlobal: public HTemplateInstruction<0> {
|
||||
return Representation::None();
|
||||
}
|
||||
|
||||
DECLARE_CONCRETE_INSTRUCTION(LoadGlobal, "load_global")
|
||||
DECLARE_CONCRETE_INSTRUCTION(LoadGlobalCell, "load_global_cell")
|
||||
|
||||
protected:
|
||||
virtual bool DataEquals(HValue* other) {
|
||||
HLoadGlobal* b = HLoadGlobal::cast(other);
|
||||
HLoadGlobalCell* b = HLoadGlobalCell::cast(other);
|
||||
return cell_.is_identical_to(b->cell());
|
||||
}
|
||||
|
||||
@ -2822,6 +2823,38 @@ class HLoadGlobal: public HTemplateInstruction<0> {
|
||||
};
|
||||
|
||||
|
||||
class HLoadGlobalGeneric: public HBinaryOperation {
|
||||
public:
|
||||
HLoadGlobalGeneric(HValue* context,
|
||||
HValue* global_object,
|
||||
Handle<Object> name,
|
||||
bool for_typeof)
|
||||
: HBinaryOperation(context, global_object),
|
||||
name_(name),
|
||||
for_typeof_(for_typeof) {
|
||||
set_representation(Representation::Tagged());
|
||||
SetAllSideEffects();
|
||||
}
|
||||
|
||||
HValue* context() { return OperandAt(0); }
|
||||
HValue* global_object() { return OperandAt(1); }
|
||||
Handle<Object> name() const { return name_; }
|
||||
bool for_typeof() const { return for_typeof_; }
|
||||
|
||||
virtual void PrintDataTo(StringStream* stream);
|
||||
|
||||
virtual Representation RequiredInputRepresentation(int index) const {
|
||||
return Representation::Tagged();
|
||||
}
|
||||
|
||||
DECLARE_CONCRETE_INSTRUCTION(LoadGlobalGeneric, "load_global_generic")
|
||||
|
||||
private:
|
||||
Handle<Object> name_;
|
||||
bool for_typeof_;
|
||||
};
|
||||
|
||||
|
||||
class HStoreGlobal: public HUnaryOperation {
|
||||
public:
|
||||
HStoreGlobal(HValue* value,
|
||||
|
111
src/hydrogen.cc
111
src/hydrogen.cc
@ -1961,7 +1961,10 @@ FunctionState::~FunctionState() {
|
||||
// Implementation of utility classes to represent an expression's context in
|
||||
// the AST.
|
||||
AstContext::AstContext(HGraphBuilder* owner, Expression::Context kind)
|
||||
: owner_(owner), kind_(kind), outer_(owner->ast_context()) {
|
||||
: owner_(owner),
|
||||
kind_(kind),
|
||||
outer_(owner->ast_context()),
|
||||
for_typeof_(false) {
|
||||
owner->set_ast_context(this); // Push.
|
||||
#ifdef DEBUG
|
||||
original_length_ = owner->environment()->length();
|
||||
@ -2105,6 +2108,14 @@ void HGraphBuilder::VisitForValue(Expression* expr) {
|
||||
}
|
||||
|
||||
|
||||
void HGraphBuilder::VisitForTypeOf(Expression* expr) {
|
||||
ValueContext for_value(this);
|
||||
for_value.set_for_typeof(true);
|
||||
Visit(expr);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void HGraphBuilder::VisitForControl(Expression* expr,
|
||||
HBasicBlock* true_block,
|
||||
HBasicBlock* false_block) {
|
||||
@ -2797,29 +2808,21 @@ void HGraphBuilder::VisitConditional(Conditional* expr) {
|
||||
}
|
||||
|
||||
|
||||
void HGraphBuilder::LookupGlobalPropertyCell(Variable* var,
|
||||
LookupResult* lookup,
|
||||
bool is_store) {
|
||||
if (var->is_this()) {
|
||||
BAILOUT("global this reference");
|
||||
}
|
||||
if (!info()->has_global_object()) {
|
||||
BAILOUT("no global object to optimize VariableProxy");
|
||||
HGraphBuilder::GlobalPropertyAccess HGraphBuilder::LookupGlobalProperty(
|
||||
Variable* var, LookupResult* lookup, bool is_store) {
|
||||
if (var->is_this() || !info()->has_global_object()) {
|
||||
return kUseGeneric;
|
||||
}
|
||||
Handle<GlobalObject> global(info()->global_object());
|
||||
global->Lookup(*var->name(), lookup);
|
||||
if (!lookup->IsProperty()) {
|
||||
BAILOUT("global variable cell not yet introduced");
|
||||
}
|
||||
if (lookup->type() != NORMAL) {
|
||||
BAILOUT("global variable has accessors");
|
||||
}
|
||||
if (is_store && lookup->IsReadOnly()) {
|
||||
BAILOUT("read-only global variable");
|
||||
}
|
||||
if (lookup->holder() != *global) {
|
||||
BAILOUT("global property on prototype of global object");
|
||||
if (!lookup->IsProperty() ||
|
||||
lookup->type() != NORMAL ||
|
||||
(is_store && lookup->IsReadOnly()) ||
|
||||
lookup->holder() != *global) {
|
||||
return kUseGeneric;
|
||||
}
|
||||
|
||||
return kUseCell;
|
||||
}
|
||||
|
||||
|
||||
@ -2855,19 +2858,31 @@ void HGraphBuilder::VisitVariableProxy(VariableProxy* expr) {
|
||||
ast_context()->ReturnInstruction(instr, expr->id());
|
||||
} else if (variable->is_global()) {
|
||||
LookupResult lookup;
|
||||
LookupGlobalPropertyCell(variable, &lookup, false);
|
||||
CHECK_BAILOUT;
|
||||
GlobalPropertyAccess type = LookupGlobalProperty(variable, &lookup, false);
|
||||
|
||||
Handle<GlobalObject> global(info()->global_object());
|
||||
// TODO(3039103): Handle global property load through an IC call when access
|
||||
// checks are enabled.
|
||||
if (global->IsAccessCheckNeeded()) {
|
||||
BAILOUT("global object requires access check");
|
||||
if (type == kUseCell &&
|
||||
info()->global_object()->IsAccessCheckNeeded()) {
|
||||
type = kUseGeneric;
|
||||
}
|
||||
|
||||
if (type == kUseCell) {
|
||||
Handle<GlobalObject> global(info()->global_object());
|
||||
Handle<JSGlobalPropertyCell> cell(global->GetPropertyCell(&lookup));
|
||||
bool check_hole = !lookup.IsDontDelete() || lookup.IsReadOnly();
|
||||
HLoadGlobalCell* instr = new HLoadGlobalCell(cell, check_hole);
|
||||
ast_context()->ReturnInstruction(instr, expr->id());
|
||||
} else {
|
||||
HContext* context = new HContext;
|
||||
AddInstruction(context);
|
||||
HGlobalObject* global_object = new HGlobalObject(context);
|
||||
AddInstruction(global_object);
|
||||
HLoadGlobalGeneric* instr =
|
||||
new HLoadGlobalGeneric(context,
|
||||
global_object,
|
||||
variable->name(),
|
||||
ast_context()->is_for_typeof());
|
||||
ast_context()->ReturnInstruction(instr, expr->id());
|
||||
}
|
||||
Handle<JSGlobalPropertyCell> cell(global->GetPropertyCell(&lookup));
|
||||
bool check_hole = !lookup.IsDontDelete() || lookup.IsReadOnly();
|
||||
HLoadGlobal* instr = new HLoadGlobal(cell, check_hole);
|
||||
ast_context()->ReturnInstruction(instr, expr->id());
|
||||
} else {
|
||||
BAILOUT("reference to a variable which requires dynamic lookup");
|
||||
}
|
||||
@ -3224,16 +3239,18 @@ void HGraphBuilder::HandleGlobalVariableAssignment(Variable* var,
|
||||
int position,
|
||||
int ast_id) {
|
||||
LookupResult lookup;
|
||||
LookupGlobalPropertyCell(var, &lookup, true);
|
||||
CHECK_BAILOUT;
|
||||
|
||||
bool check_hole = !lookup.IsDontDelete() || lookup.IsReadOnly();
|
||||
Handle<GlobalObject> global(info()->global_object());
|
||||
Handle<JSGlobalPropertyCell> cell(global->GetPropertyCell(&lookup));
|
||||
HInstruction* instr = new HStoreGlobal(value, cell, check_hole);
|
||||
instr->set_position(position);
|
||||
AddInstruction(instr);
|
||||
if (instr->HasSideEffects()) AddSimulate(ast_id);
|
||||
GlobalPropertyAccess type = LookupGlobalProperty(var, &lookup, true);
|
||||
if (type == kUseCell) {
|
||||
bool check_hole = !lookup.IsDontDelete() || lookup.IsReadOnly();
|
||||
Handle<GlobalObject> global(info()->global_object());
|
||||
Handle<JSGlobalPropertyCell> cell(global->GetPropertyCell(&lookup));
|
||||
HInstruction* instr = new HStoreGlobal(value, cell, check_hole);
|
||||
instr->set_position(position);
|
||||
AddInstruction(instr);
|
||||
if (instr->HasSideEffects()) AddSimulate(ast_id);
|
||||
} else {
|
||||
BAILOUT("global store only supported for cells");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -4328,10 +4345,12 @@ void HGraphBuilder::VisitCall(Call* expr) {
|
||||
// If there is a global property cell for the name at compile time and
|
||||
// access check is not enabled we assume that the function will not change
|
||||
// and generate optimized code for calling the function.
|
||||
if (info()->has_global_object() &&
|
||||
LookupResult lookup;
|
||||
GlobalPropertyAccess type = LookupGlobalProperty(var, &lookup, false);
|
||||
if (type == kUseCell &&
|
||||
!info()->global_object()->IsAccessCheckNeeded()) {
|
||||
Handle<GlobalObject> global(info()->global_object());
|
||||
known_global_function = expr->ComputeGlobalTarget(global, var->name());
|
||||
known_global_function = expr->ComputeGlobalTarget(global, &lookup);
|
||||
}
|
||||
if (known_global_function) {
|
||||
// Push the global object instead of the global receiver because
|
||||
@ -4550,7 +4569,8 @@ void HGraphBuilder::VisitUnaryOperation(UnaryOperation* expr) {
|
||||
}
|
||||
ast_context()->ReturnInstruction(instr, expr->id());
|
||||
} else if (op == Token::TYPEOF) {
|
||||
VISIT_FOR_VALUE(expr->expression());
|
||||
VisitForTypeOf(expr);
|
||||
if (HasStackOverflow()) return;
|
||||
HValue* value = Pop();
|
||||
ast_context()->ReturnInstruction(new HTypeof(value), expr->id());
|
||||
} else {
|
||||
@ -4935,7 +4955,8 @@ void HGraphBuilder::VisitCompareOperation(CompareOperation* expr) {
|
||||
if ((expr->op() == Token::EQ || expr->op() == Token::EQ_STRICT) &&
|
||||
left_unary != NULL && left_unary->op() == Token::TYPEOF &&
|
||||
right_literal != NULL && right_literal->handle()->IsString()) {
|
||||
VISIT_FOR_VALUE(left_unary->expression());
|
||||
VisitForTypeOf(expr);
|
||||
if (HasStackOverflow()) return;
|
||||
HValue* left = Pop();
|
||||
HInstruction* instr = new HTypeofIs(left,
|
||||
Handle<String>::cast(right_literal->handle()));
|
||||
|
@ -445,6 +445,9 @@ class AstContext {
|
||||
// the instruction as value.
|
||||
virtual void ReturnInstruction(HInstruction* instr, int ast_id) = 0;
|
||||
|
||||
void set_for_typeof(bool for_typeof) { for_typeof_ = for_typeof; }
|
||||
bool is_for_typeof() { return for_typeof_; }
|
||||
|
||||
protected:
|
||||
AstContext(HGraphBuilder* owner, Expression::Context kind);
|
||||
virtual ~AstContext();
|
||||
@ -461,6 +464,7 @@ class AstContext {
|
||||
HGraphBuilder* owner_;
|
||||
Expression::Context kind_;
|
||||
AstContext* outer_;
|
||||
bool for_typeof_;
|
||||
};
|
||||
|
||||
|
||||
@ -727,6 +731,7 @@ class HGraphBuilder: public AstVisitor {
|
||||
void Bind(Variable* var, HValue* value) { environment()->Bind(var, value); }
|
||||
|
||||
void VisitForValue(Expression* expr);
|
||||
void VisitForTypeOf(Expression* expr);
|
||||
void VisitForEffect(Expression* expr);
|
||||
void VisitForControl(Expression* expr,
|
||||
HBasicBlock* true_block,
|
||||
@ -762,9 +767,13 @@ class HGraphBuilder: public AstVisitor {
|
||||
HBasicBlock* CreateLoopHeaderBlock();
|
||||
|
||||
// Helpers for flow graph construction.
|
||||
void LookupGlobalPropertyCell(Variable* var,
|
||||
LookupResult* lookup,
|
||||
bool is_store);
|
||||
enum GlobalPropertyAccess {
|
||||
kUseCell,
|
||||
kUseGeneric
|
||||
};
|
||||
GlobalPropertyAccess LookupGlobalProperty(Variable* var,
|
||||
LookupResult* lookup,
|
||||
bool is_store);
|
||||
|
||||
bool TryArgumentsAccess(Property* expr);
|
||||
bool TryCallApply(Call* expr);
|
||||
|
@ -1543,27 +1543,26 @@ void FullCodeGenerator::VisitAssignment(Assignment* expr) {
|
||||
}
|
||||
}
|
||||
|
||||
// For compound assignments we need another deoptimization point after the
|
||||
// variable/property load.
|
||||
if (expr->is_compound()) {
|
||||
{ AccumulatorValueContext context(this);
|
||||
switch (assign_type) {
|
||||
case VARIABLE:
|
||||
EmitVariableLoad(expr->target()->AsVariableProxy()->var());
|
||||
PrepareForBailout(expr->target(), TOS_REG);
|
||||
break;
|
||||
case NAMED_PROPERTY:
|
||||
EmitNamedPropertyLoad(property);
|
||||
PrepareForBailoutForId(expr->CompoundLoadId(), TOS_REG);
|
||||
break;
|
||||
case KEYED_PROPERTY:
|
||||
EmitKeyedPropertyLoad(property);
|
||||
PrepareForBailoutForId(expr->CompoundLoadId(), TOS_REG);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// For property compound assignments we need another deoptimization
|
||||
// point after the property load.
|
||||
if (property != NULL) {
|
||||
PrepareForBailoutForId(expr->CompoundLoadId(), TOS_REG);
|
||||
}
|
||||
|
||||
Token::Value op = expr->binary_op();
|
||||
__ push(eax); // Left operand goes on the stack.
|
||||
VisitForAccumulatorValue(expr->value());
|
||||
@ -3748,7 +3747,11 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) {
|
||||
|
||||
// We need a second deoptimization point after loading the value
|
||||
// in case evaluating the property load my have a side effect.
|
||||
PrepareForBailout(expr->increment(), TOS_REG);
|
||||
if (assign_type == VARIABLE) {
|
||||
PrepareForBailout(expr->expression(), TOS_REG);
|
||||
} else {
|
||||
PrepareForBailout(expr->increment(), TOS_REG);
|
||||
}
|
||||
|
||||
// Call ToNumber only if operand is not a smi.
|
||||
NearLabel no_conversion;
|
||||
|
@ -2021,7 +2021,7 @@ void LCodeGen::DoReturn(LReturn* instr) {
|
||||
}
|
||||
|
||||
|
||||
void LCodeGen::DoLoadGlobal(LLoadGlobal* instr) {
|
||||
void LCodeGen::DoLoadGlobalCell(LLoadGlobalCell* instr) {
|
||||
Register result = ToRegister(instr->result());
|
||||
__ mov(result, Operand::Cell(instr->hydrogen()->cell()));
|
||||
if (instr->hydrogen()->check_hole_value()) {
|
||||
@ -2031,6 +2031,19 @@ void LCodeGen::DoLoadGlobal(LLoadGlobal* instr) {
|
||||
}
|
||||
|
||||
|
||||
void LCodeGen::DoLoadGlobalGeneric(LLoadGlobalGeneric* instr) {
|
||||
ASSERT(ToRegister(instr->context()).is(esi));
|
||||
ASSERT(ToRegister(instr->global_object()).is(eax));
|
||||
ASSERT(ToRegister(instr->result()).is(eax));
|
||||
|
||||
__ mov(ecx, instr->name());
|
||||
RelocInfo::Mode mode = instr->for_typeof() ? RelocInfo::CODE_TARGET :
|
||||
RelocInfo::CODE_TARGET_CONTEXT;
|
||||
Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize));
|
||||
CallCode(ic, mode, instr);
|
||||
}
|
||||
|
||||
|
||||
void LCodeGen::DoStoreGlobal(LStoreGlobal* instr) {
|
||||
Register value = ToRegister(instr->InputAt(0));
|
||||
Operand cell_operand = Operand::Cell(instr->hydrogen()->cell());
|
||||
|
@ -1745,14 +1745,22 @@ LInstruction* LChunkBuilder::DoConstant(HConstant* instr) {
|
||||
}
|
||||
|
||||
|
||||
LInstruction* LChunkBuilder::DoLoadGlobal(HLoadGlobal* instr) {
|
||||
LLoadGlobal* result = new LLoadGlobal;
|
||||
LInstruction* LChunkBuilder::DoLoadGlobalCell(HLoadGlobalCell* instr) {
|
||||
LLoadGlobalCell* result = new LLoadGlobalCell;
|
||||
return instr->check_hole_value()
|
||||
? AssignEnvironment(DefineAsRegister(result))
|
||||
: DefineAsRegister(result);
|
||||
}
|
||||
|
||||
|
||||
LInstruction* LChunkBuilder::DoLoadGlobalGeneric(HLoadGlobalGeneric* instr) {
|
||||
LOperand* context = UseFixed(instr->context(), esi);
|
||||
LOperand* global_object = UseFixed(instr->global_object(), eax);
|
||||
LLoadGlobalGeneric* result = new LLoadGlobalGeneric(context, global_object);
|
||||
return MarkAsCall(DefineFixed(result, eax), instr);
|
||||
}
|
||||
|
||||
|
||||
LInstruction* LChunkBuilder::DoStoreGlobal(HStoreGlobal* instr) {
|
||||
LStoreGlobal* result = new LStoreGlobal(UseRegisterAtStart(instr->value()));
|
||||
return instr->check_hole_value() ? AssignEnvironment(result) : result;
|
||||
|
@ -121,7 +121,8 @@ class LCodeGen;
|
||||
V(LoadElements) \
|
||||
V(LoadExternalArrayPointer) \
|
||||
V(LoadFunctionPrototype) \
|
||||
V(LoadGlobal) \
|
||||
V(LoadGlobalCell) \
|
||||
V(LoadGlobalGeneric) \
|
||||
V(LoadKeyedFastElement) \
|
||||
V(LoadKeyedGeneric) \
|
||||
V(LoadNamedField) \
|
||||
@ -1271,10 +1272,27 @@ class LLoadKeyedGeneric: public LTemplateInstruction<1, 3, 0> {
|
||||
};
|
||||
|
||||
|
||||
class LLoadGlobal: public LTemplateInstruction<1, 0, 0> {
|
||||
class LLoadGlobalCell: public LTemplateInstruction<1, 0, 0> {
|
||||
public:
|
||||
DECLARE_CONCRETE_INSTRUCTION(LoadGlobal, "load-global")
|
||||
DECLARE_HYDROGEN_ACCESSOR(LoadGlobal)
|
||||
DECLARE_CONCRETE_INSTRUCTION(LoadGlobalCell, "load-global-cell")
|
||||
DECLARE_HYDROGEN_ACCESSOR(LoadGlobalCell)
|
||||
};
|
||||
|
||||
|
||||
class LLoadGlobalGeneric: public LTemplateInstruction<1, 2, 0> {
|
||||
public:
|
||||
LLoadGlobalGeneric(LOperand* context, LOperand* global_object) {
|
||||
inputs_[0] = context;
|
||||
inputs_[1] = global_object;
|
||||
}
|
||||
|
||||
DECLARE_CONCRETE_INSTRUCTION(LoadGlobalGeneric, "load-global-generic")
|
||||
DECLARE_HYDROGEN_ACCESSOR(LoadGlobalGeneric)
|
||||
|
||||
LOperand* context() { return inputs_[0]; }
|
||||
LOperand* global_object() { return inputs_[1]; }
|
||||
Handle<Object> name() const { return hydrogen()->name(); }
|
||||
bool for_typeof() const { return hydrogen()->for_typeof(); }
|
||||
};
|
||||
|
||||
|
||||
|
@ -1558,27 +1558,26 @@ void FullCodeGenerator::VisitAssignment(Assignment* expr) {
|
||||
}
|
||||
}
|
||||
|
||||
// For compound assignments we need another deoptimization point after the
|
||||
// variable/property load.
|
||||
if (expr->is_compound()) {
|
||||
{ AccumulatorValueContext context(this);
|
||||
switch (assign_type) {
|
||||
case VARIABLE:
|
||||
EmitVariableLoad(expr->target()->AsVariableProxy()->var());
|
||||
PrepareForBailout(expr->target(), TOS_REG);
|
||||
break;
|
||||
case NAMED_PROPERTY:
|
||||
EmitNamedPropertyLoad(property);
|
||||
PrepareForBailoutForId(expr->CompoundLoadId(), TOS_REG);
|
||||
break;
|
||||
case KEYED_PROPERTY:
|
||||
EmitKeyedPropertyLoad(property);
|
||||
PrepareForBailoutForId(expr->CompoundLoadId(), TOS_REG);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// For property compound assignments we need another deoptimization
|
||||
// point after the property load.
|
||||
if (property != NULL) {
|
||||
PrepareForBailoutForId(expr->CompoundLoadId(), TOS_REG);
|
||||
}
|
||||
|
||||
Token::Value op = expr->binary_op();
|
||||
__ push(rax); // Left operand goes on the stack.
|
||||
VisitForAccumulatorValue(expr->value());
|
||||
@ -3450,7 +3449,11 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) {
|
||||
|
||||
// We need a second deoptimization point after loading the value
|
||||
// in case evaluating the property load my have a side effect.
|
||||
PrepareForBailout(expr->increment(), TOS_REG);
|
||||
if (assign_type == VARIABLE) {
|
||||
PrepareForBailout(expr->expression(), TOS_REG);
|
||||
} else {
|
||||
PrepareForBailout(expr->increment(), TOS_REG);
|
||||
}
|
||||
|
||||
// Call ToNumber only if operand is not a smi.
|
||||
NearLabel no_conversion;
|
||||
|
@ -1984,7 +1984,7 @@ void LCodeGen::DoReturn(LReturn* instr) {
|
||||
}
|
||||
|
||||
|
||||
void LCodeGen::DoLoadGlobal(LLoadGlobal* instr) {
|
||||
void LCodeGen::DoLoadGlobalCell(LLoadGlobalCell* instr) {
|
||||
Register result = ToRegister(instr->result());
|
||||
if (result.is(rax)) {
|
||||
__ load_rax(instr->hydrogen()->cell().location(),
|
||||
@ -2000,6 +2000,18 @@ void LCodeGen::DoLoadGlobal(LLoadGlobal* instr) {
|
||||
}
|
||||
|
||||
|
||||
void LCodeGen::DoLoadGlobalGeneric(LLoadGlobalGeneric* instr) {
|
||||
ASSERT(ToRegister(instr->global_object()).is(rax));
|
||||
ASSERT(ToRegister(instr->result()).is(rax));
|
||||
|
||||
__ Move(rcx, instr->name());
|
||||
RelocInfo::Mode mode = instr->for_typeof() ? RelocInfo::CODE_TARGET :
|
||||
RelocInfo::CODE_TARGET_CONTEXT;
|
||||
Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize));
|
||||
CallCode(ic, mode, instr);
|
||||
}
|
||||
|
||||
|
||||
void LCodeGen::DoStoreGlobal(LStoreGlobal* instr) {
|
||||
Register value = ToRegister(instr->InputAt(0));
|
||||
Register temp = ToRegister(instr->TempAt(0));
|
||||
|
@ -1716,14 +1716,21 @@ LInstruction* LChunkBuilder::DoConstant(HConstant* instr) {
|
||||
}
|
||||
|
||||
|
||||
LInstruction* LChunkBuilder::DoLoadGlobal(HLoadGlobal* instr) {
|
||||
LLoadGlobal* result = new LLoadGlobal;
|
||||
LInstruction* LChunkBuilder::DoLoadGlobalCell(HLoadGlobalCell* instr) {
|
||||
LLoadGlobalCell* result = new LLoadGlobalCell;
|
||||
return instr->check_hole_value()
|
||||
? AssignEnvironment(DefineAsRegister(result))
|
||||
: DefineAsRegister(result);
|
||||
}
|
||||
|
||||
|
||||
LInstruction* LChunkBuilder::DoLoadGlobalGeneric(HLoadGlobalGeneric* instr) {
|
||||
LOperand* global_object = UseFixed(instr->global_object(), rax);
|
||||
LLoadGlobalGeneric* result = new LLoadGlobalGeneric(global_object);
|
||||
return MarkAsCall(DefineFixed(result, rax), instr);
|
||||
}
|
||||
|
||||
|
||||
LInstruction* LChunkBuilder::DoStoreGlobal(HStoreGlobal* instr) {
|
||||
LStoreGlobal* result = new LStoreGlobal(UseRegister(instr->value()),
|
||||
TempRegister());
|
||||
|
@ -118,7 +118,8 @@ class LCodeGen;
|
||||
V(LoadContextSlot) \
|
||||
V(LoadElements) \
|
||||
V(LoadExternalArrayPointer) \
|
||||
V(LoadGlobal) \
|
||||
V(LoadGlobalCell) \
|
||||
V(LoadGlobalGeneric) \
|
||||
V(LoadKeyedFastElement) \
|
||||
V(LoadKeyedGeneric) \
|
||||
V(LoadNamedField) \
|
||||
@ -1226,10 +1227,25 @@ class LLoadKeyedGeneric: public LTemplateInstruction<1, 2, 0> {
|
||||
};
|
||||
|
||||
|
||||
class LLoadGlobal: public LTemplateInstruction<1, 0, 0> {
|
||||
class LLoadGlobalCell: public LTemplateInstruction<1, 0, 0> {
|
||||
public:
|
||||
DECLARE_CONCRETE_INSTRUCTION(LoadGlobal, "load-global")
|
||||
DECLARE_HYDROGEN_ACCESSOR(LoadGlobal)
|
||||
DECLARE_CONCRETE_INSTRUCTION(LoadGlobalCell, "load-global-cell")
|
||||
DECLARE_HYDROGEN_ACCESSOR(LoadGlobalCell)
|
||||
};
|
||||
|
||||
|
||||
class LLoadGlobalGeneric: public LTemplateInstruction<1, 1, 0> {
|
||||
public:
|
||||
explicit LLoadGlobalGeneric(LOperand* global_object) {
|
||||
inputs_[0] = global_object;
|
||||
}
|
||||
|
||||
DECLARE_CONCRETE_INSTRUCTION(LoadGlobalGeneric, "load-global-generic")
|
||||
DECLARE_HYDROGEN_ACCESSOR(LoadGlobalGeneric)
|
||||
|
||||
LOperand* global_object() { return inputs_[0]; }
|
||||
Handle<Object> name() const { return hydrogen()->name(); }
|
||||
bool for_typeof() const { return hydrogen()->for_typeof(); }
|
||||
};
|
||||
|
||||
|
||||
|
47
test/mjsunit/compiler/global-accessors.js
Normal file
47
test/mjsunit/compiler/global-accessors.js
Normal file
@ -0,0 +1,47 @@
|
||||
// Copyright 2010 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.
|
||||
|
||||
// This test tests that no bailouts are missing by not hitting asserts in debug
|
||||
// mode.
|
||||
|
||||
test_count_operation()
|
||||
test_compound_assignment()
|
||||
|
||||
function f() {}
|
||||
function test_count_operation()
|
||||
{
|
||||
this.__defineSetter__('x', f);
|
||||
this.__defineGetter__('x', f);
|
||||
x = x++;
|
||||
}
|
||||
|
||||
function test_compound_assignment()
|
||||
{
|
||||
this.__defineSetter__('y', f);
|
||||
this.__defineGetter__('y', f);
|
||||
y += y;
|
||||
}
|
Loading…
Reference in New Issue
Block a user