[hydrogen] don't bailout assignments to consts

If constant variable is allocated in CONTEXT

Patch by Fedor Indutny <fedor.indutny@gmail.com>.

BUG=
TEST=
R=vegorov@chromium.org
Review URL: http://codereview.chromium.org/8857001

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@10244 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
fschneider@chromium.org 2011-12-13 17:10:34 +00:00
parent 76b48da7ef
commit 626454a61a
6 changed files with 165 additions and 44 deletions

View File

@ -2306,7 +2306,11 @@ void LCodeGen::DoLoadContextSlot(LLoadContextSlot* instr) {
if (instr->hydrogen()->RequiresHoleCheck()) {
__ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
__ cmp(result, ip);
DeoptimizeIf(eq, instr->environment());
if (instr->hydrogen()->DeoptimizesOnHole()) {
DeoptimizeIf(eq, instr->environment());
} else {
__ mov(result, Operand(factory()->undefined_value()), LeaveCC, eq);
}
}
}
@ -2314,14 +2318,22 @@ void LCodeGen::DoLoadContextSlot(LLoadContextSlot* instr) {
void LCodeGen::DoStoreContextSlot(LStoreContextSlot* instr) {
Register context = ToRegister(instr->context());
Register value = ToRegister(instr->value());
Register scratch = scratch0();
MemOperand target = ContextOperand(context, instr->slot_index());
Label skip_assignment;
if (instr->hydrogen()->RequiresHoleCheck()) {
Register scratch = scratch0();
__ ldr(scratch, target);
__ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
__ cmp(scratch, ip);
DeoptimizeIf(eq, instr->environment());
if (instr->hydrogen()->DeoptimizesOnHole()) {
DeoptimizeIf(eq, instr->environment());
} else {
__ b(ne, &skip_assignment);
}
}
__ str(value, target);
if (instr->hydrogen()->NeedsWriteBarrier()) {
HType type = instr->hydrogen()->value()->type();
@ -2330,12 +2342,14 @@ void LCodeGen::DoStoreContextSlot(LStoreContextSlot* instr) {
__ RecordWriteContextSlot(context,
target.offset(),
value,
scratch0(),
scratch,
kLRHasBeenSaved,
kSaveFPRegs,
EMIT_REMEMBERED_SET,
check_needed);
}
__ bind(&skip_assignment);
}

View File

@ -3449,28 +3449,45 @@ class HLoadContextSlot: public HUnaryOperation {
public:
enum Mode {
// Perform a normal load of the context slot without checking its value.
kLoad,
kNoCheck,
// Load and check the value of the context slot. Deoptimize if it's the
// hole value. This is used for checking for loading of uninitialized
// harmony bindings where we deoptimize into full-codegen generated code
// which will subsequently throw a reference error.
kLoadCheck
kCheckDeoptimize,
// Load and check the value of the context slot. Return undefined if it's
// the hole value. This is used for non-harmony const assignments
kCheckReturnUndefined
};
HLoadContextSlot(HValue* context, Variable* var)
: HUnaryOperation(context), slot_index_(var->index()) {
ASSERT(var->IsContextSlot());
mode_ = (var->mode() == LET || var->mode() == CONST_HARMONY)
? kLoadCheck : kLoad;
switch (var->mode()) {
case LET:
case CONST_HARMONY:
mode_ = kCheckDeoptimize;
break;
case CONST:
mode_ = kCheckReturnUndefined;
break;
default:
mode_ = kNoCheck;
}
set_representation(Representation::Tagged());
SetFlag(kUseGVN);
SetFlag(kDependsOnContextSlots);
}
int slot_index() const { return slot_index_; }
Mode mode() const { return mode_; }
bool DeoptimizesOnHole() {
return mode_ == kCheckDeoptimize;
}
bool RequiresHoleCheck() {
return mode_ == kLoadCheck;
return mode_ != kNoCheck;
}
virtual Representation RequiredInputRepresentation(int index) {
@ -3498,12 +3515,14 @@ class HStoreContextSlot: public HTemplateInstruction<2> {
enum Mode {
// Perform a normal store to the context slot without checking its previous
// value.
kAssign,
kNoCheck,
// Check the previous value of the context slot and deoptimize if it's the
// hole value. This is used for checking for assignments to uninitialized
// harmony bindings where we deoptimize into full-codegen generated code
// which will subsequently throw a reference error.
kAssignCheck
kCheckDeoptimize,
// Check the previous value and ignore assignment if it isn't a hole value
kCheckIgnoreAssignment
};
HStoreContextSlot(HValue* context, int slot_index, Mode mode, HValue* value)
@ -3522,8 +3541,12 @@ class HStoreContextSlot: public HTemplateInstruction<2> {
return StoringValueNeedsWriteBarrier(value());
}
bool DeoptimizesOnHole() {
return mode_ == kCheckDeoptimize;
}
bool RequiresHoleCheck() {
return mode_ == kAssignCheck;
return mode_ != kNoCheck;
}
virtual Representation RequiredInputRepresentation(int index) {

View File

@ -3285,9 +3285,6 @@ void HGraphBuilder::VisitVariableProxy(VariableProxy* expr) {
}
case Variable::CONTEXT: {
if (variable->mode() == CONST) {
return Bailout("reference to const context slot");
}
HValue* context = BuildContextChainWalk(variable);
HLoadContextSlot* instr = new(zone()) HLoadContextSlot(context, variable);
return ast_context()->ReturnInstruction(instr, expr->id());
@ -3805,8 +3802,8 @@ void HGraphBuilder::HandleCompoundAssignment(Assignment* expr) {
if (proxy != NULL) {
Variable* var = proxy->var();
if (var->mode() == CONST || var->mode() == LET) {
return Bailout("unsupported let or const compound assignment");
if (var->mode() == LET) {
return Bailout("unsupported let compound assignment");
}
CHECK_ALIVE(VisitForValue(operation));
@ -3821,6 +3818,9 @@ void HGraphBuilder::HandleCompoundAssignment(Assignment* expr) {
case Variable::PARAMETER:
case Variable::LOCAL:
if (var->mode() == CONST) {
return Bailout("unsupported const compound assignment");
}
Bind(var, Top());
break;
@ -3841,10 +3841,23 @@ void HGraphBuilder::HandleCompoundAssignment(Assignment* expr) {
}
}
HStoreContextSlot::Mode mode;
switch (var->mode()) {
case LET:
mode = HStoreContextSlot::kCheckDeoptimize;
break;
case CONST:
return ast_context()->ReturnValue(Pop());
case CONST_HARMONY:
// This case is checked statically so no need to
// perform checks here
UNREACHABLE();
default:
mode = HStoreContextSlot::kNoCheck;
}
HValue* context = BuildContextChainWalk(var);
HStoreContextSlot::Mode mode =
(var->mode() == LET || var->mode() == CONST_HARMONY)
? HStoreContextSlot::kAssignCheck : HStoreContextSlot::kAssign;
HStoreContextSlot* instr =
new(zone()) HStoreContextSlot(context, var->index(), mode, Top());
AddInstruction(instr);
@ -3955,17 +3968,19 @@ void HGraphBuilder::VisitAssignment(Assignment* expr) {
HandlePropertyAssignment(expr);
} else if (proxy != NULL) {
Variable* var = proxy->var();
if (var->mode() == CONST) {
if (expr->op() != Token::INIT_CONST) {
return Bailout("non-initializer assignment to const");
CHECK_ALIVE(VisitForValue(expr->value()));
return ast_context()->ReturnValue(Pop());
}
if (!var->IsStackAllocated()) {
return Bailout("assignment to const context slot");
if (var->IsStackAllocated()) {
// We insert a use of the old value to detect unsupported uses of const
// variables (e.g. initialization inside a loop).
HValue* old_value = environment()->Lookup(var);
AddInstruction(new HUseConst(old_value));
}
// We insert a use of the old value to detect unsupported uses of const
// variables (e.g. initialization inside a loop).
HValue* old_value = environment()->Lookup(var);
AddInstruction(new HUseConst(old_value));
} else if (var->mode() == CONST_HARMONY) {
if (expr->op() != Token::INIT_CONST_HARMONY) {
return Bailout("non-initializer assignment to const");
@ -4004,7 +4019,6 @@ void HGraphBuilder::VisitAssignment(Assignment* expr) {
}
case Variable::CONTEXT: {
ASSERT(var->mode() != CONST);
// Bail out if we try to mutate a parameter value in a function using
// the arguments object. We do not (yet) correctly handle the
// arguments property of the function.
@ -4020,17 +4034,32 @@ void HGraphBuilder::VisitAssignment(Assignment* expr) {
}
CHECK_ALIVE(VisitForValue(expr->value()));
HValue* context = BuildContextChainWalk(var);
HStoreContextSlot::Mode mode;
if (expr->op() == Token::ASSIGN) {
mode = (var->mode() == LET || var->mode() == CONST_HARMONY)
? HStoreContextSlot::kAssignCheck : HStoreContextSlot::kAssign;
switch (var->mode()) {
case LET:
mode = HStoreContextSlot::kCheckDeoptimize;
break;
case CONST:
return ast_context()->ReturnValue(Pop());
case CONST_HARMONY:
// This case is checked statically so no need to
// perform checks here
UNREACHABLE();
default:
mode = HStoreContextSlot::kNoCheck;
}
} else if (expr->op() == Token::INIT_VAR ||
expr->op() == Token::INIT_LET ||
expr->op() == Token::INIT_CONST_HARMONY) {
mode = HStoreContextSlot::kNoCheck;
} else {
ASSERT(expr->op() == Token::INIT_VAR ||
expr->op() == Token::INIT_LET ||
expr->op() == Token::INIT_CONST_HARMONY);
mode = HStoreContextSlot::kAssign;
ASSERT(expr->op() == Token::INIT_CONST);
mode = HStoreContextSlot::kCheckIgnoreAssignment;
}
HValue* context = BuildContextChainWalk(var);
HStoreContextSlot* instr = new(zone()) HStoreContextSlot(
context, var->index(), mode, Top());
AddInstruction(instr);
@ -5643,7 +5672,7 @@ void HGraphBuilder::VisitCountOperation(CountOperation* expr) {
HValue* context = BuildContextChainWalk(var);
HStoreContextSlot::Mode mode =
(var->mode() == LET || var->mode() == CONST_HARMONY)
? HStoreContextSlot::kAssignCheck : HStoreContextSlot::kAssign;
? HStoreContextSlot::kCheckDeoptimize : HStoreContextSlot::kNoCheck;
HStoreContextSlot* instr =
new(zone()) HStoreContextSlot(context, var->index(), mode, after);
AddInstruction(instr);
@ -6251,7 +6280,7 @@ void HGraphBuilder::HandleDeclaration(VariableProxy* proxy,
if (var->IsContextSlot()) {
HValue* context = environment()->LookupContext();
HStoreContextSlot* store = new HStoreContextSlot(
context, var->index(), HStoreContextSlot::kAssign, value);
context, var->index(), HStoreContextSlot::kNoCheck, value);
AddInstruction(store);
if (store->HasObservableSideEffects()) AddSimulate(proxy->id());
} else {

View File

@ -2165,9 +2165,17 @@ void LCodeGen::DoLoadContextSlot(LLoadContextSlot* instr) {
Register context = ToRegister(instr->context());
Register result = ToRegister(instr->result());
__ mov(result, ContextOperand(context, instr->slot_index()));
if (instr->hydrogen()->RequiresHoleCheck()) {
__ cmp(result, factory()->the_hole_value());
DeoptimizeIf(equal, instr->environment());
if (instr->hydrogen()->DeoptimizesOnHole()) {
DeoptimizeIf(equal, instr->environment());
} else {
Label is_not_hole;
__ j(not_equal, &is_not_hole, Label::kNear);
__ mov(result, factory()->undefined_value());
__ bind(&is_not_hole);
}
}
}
@ -2175,11 +2183,19 @@ void LCodeGen::DoLoadContextSlot(LLoadContextSlot* instr) {
void LCodeGen::DoStoreContextSlot(LStoreContextSlot* instr) {
Register context = ToRegister(instr->context());
Register value = ToRegister(instr->value());
Label skip_assignment;
Operand target = ContextOperand(context, instr->slot_index());
if (instr->hydrogen()->RequiresHoleCheck()) {
__ cmp(target, factory()->the_hole_value());
DeoptimizeIf(equal, instr->environment());
if (instr->hydrogen()->DeoptimizesOnHole()) {
DeoptimizeIf(equal, instr->environment());
} else {
__ j(not_equal, &skip_assignment, Label::kNear);
}
}
__ mov(target, value);
if (instr->hydrogen()->NeedsWriteBarrier()) {
HType type = instr->hydrogen()->value()->type();
@ -2195,6 +2211,8 @@ void LCodeGen::DoStoreContextSlot(LStoreContextSlot* instr) {
EMIT_REMEMBERED_SET,
check_needed);
}
__ bind(&skip_assignment);
}

View File

@ -2180,10 +2180,19 @@ void LCodeGen::DoStoreGlobalGeneric(LStoreGlobalGeneric* instr) {
void LCodeGen::DoLoadContextSlot(LLoadContextSlot* instr) {
Register context = ToRegister(instr->context());
Register result = ToRegister(instr->result());
__ lw(result, ContextOperand(context, instr->slot_index()));
if (instr->hydrogen()->RequiresHoleCheck()) {
__ LoadRoot(at, Heap::kTheHoleValueRootIndex);
DeoptimizeIf(eq, instr->environment(), result, Operand(at));
if (instr->hydrogen()->DeoptimizesOnHole()) {
DeoptimizeIf(eq, instr->environment(), result, Operand(at));
} else {
Label is_not_hole;
__ Branch(&is_not_hole, ne, result, Operand(at));
__ LoadRoot(result, Heap::kUndefinedValueRootIndex);
__ bind(&is_not_hole);
}
}
}
@ -2191,13 +2200,22 @@ void LCodeGen::DoLoadContextSlot(LLoadContextSlot* instr) {
void LCodeGen::DoStoreContextSlot(LStoreContextSlot* instr) {
Register context = ToRegister(instr->context());
Register value = ToRegister(instr->value());
Register scratch = scratch0();
MemOperand target = ContextOperand(context, instr->slot_index());
Label skip_assignment;
if (instr->hydrogen()->RequiresHoleCheck()) {
Register scratch = scratch0();
__ lw(scratch, target);
__ LoadRoot(at, Heap::kTheHoleValueRootIndex);
DeoptimizeIf(eq, instr->environment(), scratch, Operand(at));
if (instr->hydrogen()->DeoptimizesOnHole()) {
DeoptimizeIf(eq, instr->environment(), scratch, Operand(at));
} else {
__ Branch(&skip_assignment, ne, scratch, Operand(at));
}
}
__ sw(value, target);
if (instr->hydrogen()->NeedsWriteBarrier()) {
HType type = instr->hydrogen()->value()->type();
@ -2212,6 +2230,8 @@ void LCodeGen::DoStoreContextSlot(LStoreContextSlot* instr) {
EMIT_REMEMBERED_SET,
check_needed);
}
__ bind(&skip_assignment);
}

View File

@ -2069,7 +2069,14 @@ void LCodeGen::DoLoadContextSlot(LLoadContextSlot* instr) {
__ movq(result, ContextOperand(context, instr->slot_index()));
if (instr->hydrogen()->RequiresHoleCheck()) {
__ CompareRoot(result, Heap::kTheHoleValueRootIndex);
DeoptimizeIf(equal, instr->environment());
if (instr->hydrogen()->DeoptimizesOnHole()) {
DeoptimizeIf(equal, instr->environment());
} else {
Label is_not_hole;
__ j(not_equal, &is_not_hole, Label::kNear);
__ movq(result, factory()->undefined_value(), RelocInfo::NONE);
__ bind(&is_not_hole);
}
}
}
@ -2077,12 +2084,20 @@ void LCodeGen::DoLoadContextSlot(LLoadContextSlot* instr) {
void LCodeGen::DoStoreContextSlot(LStoreContextSlot* instr) {
Register context = ToRegister(instr->context());
Register value = ToRegister(instr->value());
Operand target = ContextOperand(context, instr->slot_index());
Label skip_assignment;
if (instr->hydrogen()->RequiresHoleCheck()) {
__ CompareRoot(target, Heap::kTheHoleValueRootIndex);
DeoptimizeIf(equal, instr->environment());
if (instr->hydrogen()->DeoptimizesOnHole()) {
DeoptimizeIf(equal, instr->environment());
} else {
__ j(not_equal, &skip_assignment, Label::kNear);
}
}
__ movq(target, value);
if (instr->hydrogen()->NeedsWriteBarrier()) {
HType type = instr->hydrogen()->value()->type();
SmiCheck check_needed =
@ -2097,6 +2112,8 @@ void LCodeGen::DoStoreContextSlot(LStoreContextSlot* instr) {
EMIT_REMEMBERED_SET,
check_needed);
}
__ bind(&skip_assignment);
}