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:
sgjesse@chromium.org 2011-03-17 08:16:12 +00:00
parent ec82215b44
commit 1a6c821b05
19 changed files with 343 additions and 113 deletions

View File

@ -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;

View File

@ -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();

View File

@ -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(); }
};

View File

@ -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();

View File

@ -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;

View File

@ -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_; }

View File

@ -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);

View File

@ -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,

View File

@ -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()));

View File

@ -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);

View File

@ -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;

View File

@ -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());

View File

@ -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;

View File

@ -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(); }
};

View File

@ -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;

View File

@ -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));

View File

@ -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());

View File

@ -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(); }
};

View 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;
}