Revision: 10220
Author: [email protected]
Date: Fri Dec 9 01:50:30 2011
Log: Hydrogen support for context allocated harmony bindings.
This CL adds support for loading from and storing to context slots
belonging to harmony let or const bound variables. Checks for the
hole value are performed and the function is deoptimized if they fail.
The full-codegen generated code will take care of properly throwing
a reference error in these cases.
TEST=mjsunit/harmony/block-let-crankshaft.js
Review URL: http://codereview.chromium.org/8820015
http://code.google.com/p/v8/source/detail?r=10220
Modified:
/branches/bleeding_edge/src/arm/lithium-arm.cc
/branches/bleeding_edge/src/arm/lithium-codegen-arm.cc
/branches/bleeding_edge/src/hydrogen-instructions.h
/branches/bleeding_edge/src/hydrogen.cc
/branches/bleeding_edge/src/ia32/lithium-codegen-ia32.cc
/branches/bleeding_edge/src/ia32/lithium-ia32.cc
/branches/bleeding_edge/src/x64/lithium-codegen-x64.cc
/branches/bleeding_edge/src/x64/lithium-x64.cc
/branches/bleeding_edge/test/mjsunit/harmony/block-let-crankshaft.js
=======================================
--- /branches/bleeding_edge/src/arm/lithium-arm.cc Wed Dec 7 08:55:00 2011
+++ /branches/bleeding_edge/src/arm/lithium-arm.cc Fri Dec 9 01:50:30 2011
@@ -1798,7 +1798,8 @@
LInstruction* LChunkBuilder::DoLoadContextSlot(HLoadContextSlot* instr) {
LOperand* context = UseRegisterAtStart(instr->value());
- return DefineAsRegister(new LLoadContextSlot(context));
+ LInstruction* result = DefineAsRegister(new LLoadContextSlot(context));
+ return instr->RequiresHoleCheck() ? AssignEnvironment(result) : result;
}
@@ -1812,7 +1813,8 @@
context = UseRegister(instr->context());
value = UseRegister(instr->value());
}
- return new LStoreContextSlot(context, value);
+ LInstruction* result = new LStoreContextSlot(context, value);
+ return instr->RequiresHoleCheck() ? AssignEnvironment(result) : result;
}
=======================================
--- /branches/bleeding_edge/src/arm/lithium-codegen-arm.cc Wed Dec 7
08:55:00 2011
+++ /branches/bleeding_edge/src/arm/lithium-codegen-arm.cc Fri Dec 9
01:50:30 2011
@@ -2303,6 +2303,11 @@
Register context = ToRegister(instr->context());
Register result = ToRegister(instr->result());
__ ldr(result, ContextOperand(context, instr->slot_index()));
+ if (instr->hydrogen()->RequiresHoleCheck()) {
+ __ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
+ __ cmp(result, ip);
+ DeoptimizeIf(eq, instr->environment());
+ }
}
@@ -2310,6 +2315,13 @@
Register context = ToRegister(instr->context());
Register value = ToRegister(instr->value());
MemOperand target = ContextOperand(context, instr->slot_index());
+ if (instr->hydrogen()->RequiresHoleCheck()) {
+ Register scratch = scratch0();
+ __ ldr(scratch, target);
+ __ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
+ __ cmp(scratch, ip);
+ DeoptimizeIf(eq, instr->environment());
+ }
__ str(value, target);
if (instr->hydrogen()->NeedsWriteBarrier()) {
HType type = instr->hydrogen()->value()->type();
=======================================
--- /branches/bleeding_edge/src/hydrogen-instructions.h Fri Dec 2 04:42:35
2011
+++ /branches/bleeding_edge/src/hydrogen-instructions.h Fri Dec 9 01:50:30
2011
@@ -3447,14 +3447,31 @@
class HLoadContextSlot: public HUnaryOperation {
public:
- HLoadContextSlot(HValue* context , int slot_index)
- : HUnaryOperation(context), slot_index_(slot_index) {
+ enum Mode {
+ // Perform a normal load of the context slot without checking its
value.
+ kLoad,
+ // 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
+ };
+
+ HLoadContextSlot(HValue* context, Variable* var)
+ : HUnaryOperation(context), slot_index_(var->index()) {
+ ASSERT(var->IsContextSlot());
+ mode_ = (var->mode() == LET || var->mode() == CONST_HARMONY)
+ ? kLoadCheck : kLoad;
set_representation(Representation::Tagged());
SetFlag(kUseGVN);
SetFlag(kDependsOnContextSlots);
}
int slot_index() const { return slot_index_; }
+
+ bool RequiresHoleCheck() {
+ return mode_ == kLoadCheck;
+ }
virtual Representation RequiredInputRepresentation(int index) {
return Representation::Tagged();
@@ -3472,13 +3489,25 @@
private:
int slot_index_;
+ Mode mode_;
};
class HStoreContextSlot: public HTemplateInstruction<2> {
public:
- HStoreContextSlot(HValue* context, int slot_index, HValue* value)
- : slot_index_(slot_index) {
+ enum Mode {
+ // Perform a normal store to the context slot without checking its
previous
+ // value.
+ kAssign,
+ // 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
+ };
+
+ HStoreContextSlot(HValue* context, int slot_index, Mode mode, HValue*
value)
+ : slot_index_(slot_index), mode_(mode) {
SetOperandAt(0, context);
SetOperandAt(1, value);
SetFlag(kChangesContextSlots);
@@ -3487,10 +3516,15 @@
HValue* context() { return OperandAt(0); }
HValue* value() { return OperandAt(1); }
int slot_index() const { return slot_index_; }
+ Mode mode() const { return mode_; }
bool NeedsWriteBarrier() {
return StoringValueNeedsWriteBarrier(value());
}
+
+ bool RequiresHoleCheck() {
+ return mode_ == kAssignCheck;
+ }
virtual Representation RequiredInputRepresentation(int index) {
return Representation::Tagged();
@@ -3502,6 +3536,7 @@
private:
int slot_index_;
+ Mode mode_;
};
=======================================
--- /branches/bleeding_edge/src/hydrogen.cc Fri Dec 9 01:26:14 2011
+++ /branches/bleeding_edge/src/hydrogen.cc Fri Dec 9 01:50:30 2011
@@ -3285,15 +3285,11 @@
}
case Variable::CONTEXT: {
- if (variable->mode() == LET || variable->mode() == CONST_HARMONY) {
- return Bailout("reference to harmony declared context slot");
- }
if (variable->mode() == CONST) {
return Bailout("reference to const context slot");
}
HValue* context = BuildContextChainWalk(variable);
- HLoadContextSlot* instr =
- new(zone()) HLoadContextSlot(context, variable->index());
+ HLoadContextSlot* instr = new(zone()) HLoadContextSlot(context,
variable);
return ast_context()->ReturnInstruction(instr, expr->id());
}
@@ -3846,8 +3842,11 @@
}
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(), Top());
+ new(zone()) HStoreContextSlot(context, var->index(), mode,
Top());
AddInstruction(instr);
if (instr->HasObservableSideEffects()) {
AddSimulate(expr->AssignmentId());
@@ -3967,17 +3966,10 @@
// variables (e.g. initialization inside a loop).
HValue* old_value = environment()->Lookup(var);
AddInstruction(new HUseConst(old_value));
- } else if (var->mode() == LET) {
- if (!var->IsStackAllocated()) {
- return Bailout("assignment to let context slot");
- }
} else if (var->mode() == CONST_HARMONY) {
if (expr->op() != Token::INIT_CONST_HARMONY) {
return Bailout("non-initializer assignment to const");
}
- if (!var->IsStackAllocated()) {
- return Bailout("assignment to const context slot");
- }
}
if (proxy->IsArguments()) return Bailout("assignment to arguments");
@@ -4029,8 +4021,18 @@
CHECK_ALIVE(VisitForValue(expr->value()));
HValue* context = BuildContextChainWalk(var);
- HStoreContextSlot* instr =
- new(zone()) HStoreContextSlot(context, var->index(), Top());
+ HStoreContextSlot::Mode mode;
+ if (expr->op() == Token::ASSIGN) {
+ mode = (var->mode() == LET || var->mode() == CONST_HARMONY)
+ ? HStoreContextSlot::kAssignCheck :
HStoreContextSlot::kAssign;
+ } else {
+ ASSERT(expr->op() == Token::INIT_VAR ||
+ expr->op() == Token::INIT_LET ||
+ expr->op() == Token::INIT_CONST_HARMONY);
+ mode = HStoreContextSlot::kAssign;
+ }
+ HStoreContextSlot* instr = new(zone()) HStoreContextSlot(
+ context, var->index(), mode, Top());
AddInstruction(instr);
if (instr->HasObservableSideEffects()) {
AddSimulate(expr->AssignmentId());
@@ -5639,8 +5641,11 @@
}
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(), after);
+ new(zone()) HStoreContextSlot(context, var->index(), mode,
after);
AddInstruction(instr);
if (instr->HasObservableSideEffects()) {
AddSimulate(expr->AssignmentId());
@@ -6245,8 +6250,8 @@
}
if (var->IsContextSlot()) {
HValue* context = environment()->LookupContext();
- HStoreContextSlot* store =
- new HStoreContextSlot(context, var->index(), value);
+ HStoreContextSlot* store = new HStoreContextSlot(
+ context, var->index(), HStoreContextSlot::kAssign, value);
AddInstruction(store);
if (store->HasObservableSideEffects()) AddSimulate(proxy->id());
} else {
=======================================
--- /branches/bleeding_edge/src/ia32/lithium-codegen-ia32.cc Wed Dec 7
02:13:46 2011
+++ /branches/bleeding_edge/src/ia32/lithium-codegen-ia32.cc Fri Dec 9
01:50:30 2011
@@ -2165,13 +2165,22 @@
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());
+ }
}
void LCodeGen::DoStoreContextSlot(LStoreContextSlot* instr) {
Register context = ToRegister(instr->context());
Register value = ToRegister(instr->value());
- __ mov(ContextOperand(context, instr->slot_index()), value);
+ Operand target = ContextOperand(context, instr->slot_index());
+ if (instr->hydrogen()->RequiresHoleCheck()) {
+ __ cmp(target, factory()->the_hole_value());
+ DeoptimizeIf(equal, instr->environment());
+ }
+ __ mov(target, value);
if (instr->hydrogen()->NeedsWriteBarrier()) {
HType type = instr->hydrogen()->value()->type();
SmiCheck check_needed =
=======================================
--- /branches/bleeding_edge/src/ia32/lithium-ia32.cc Wed Dec 7 00:34:27
2011
+++ /branches/bleeding_edge/src/ia32/lithium-ia32.cc Fri Dec 9 01:50:30
2011
@@ -1875,7 +1875,9 @@
LInstruction* LChunkBuilder::DoLoadContextSlot(HLoadContextSlot* instr) {
LOperand* context = UseRegisterAtStart(instr->value());
- return DefineAsRegister(new(zone()) LLoadContextSlot(context));
+ LInstruction* result =
+ DefineAsRegister(new(zone()) LLoadContextSlot(context));
+ return instr->RequiresHoleCheck() ? AssignEnvironment(result) : result;
}
@@ -1890,7 +1892,8 @@
value = UseRegister(instr->value());
temp = NULL;
}
- return new(zone()) LStoreContextSlot(context, value, temp);
+ LInstruction* result = new(zone()) LStoreContextSlot(context, value,
temp);
+ return instr->RequiresHoleCheck() ? AssignEnvironment(result) : result;
}
=======================================
--- /branches/bleeding_edge/src/x64/lithium-codegen-x64.cc Wed Dec 7
02:13:46 2011
+++ /branches/bleeding_edge/src/x64/lithium-codegen-x64.cc Fri Dec 9
01:50:30 2011
@@ -2067,13 +2067,22 @@
Register context = ToRegister(instr->context());
Register result = ToRegister(instr->result());
__ movq(result, ContextOperand(context, instr->slot_index()));
+ if (instr->hydrogen()->RequiresHoleCheck()) {
+ __ CompareRoot(result, Heap::kTheHoleValueRootIndex);
+ DeoptimizeIf(equal, instr->environment());
+ }
}
void LCodeGen::DoStoreContextSlot(LStoreContextSlot* instr) {
Register context = ToRegister(instr->context());
Register value = ToRegister(instr->value());
- __ movq(ContextOperand(context, instr->slot_index()), value);
+ Operand target = ContextOperand(context, instr->slot_index());
+ if (instr->hydrogen()->RequiresHoleCheck()) {
+ __ CompareRoot(target, Heap::kTheHoleValueRootIndex);
+ DeoptimizeIf(equal, instr->environment());
+ }
+ __ movq(target, value);
if (instr->hydrogen()->NeedsWriteBarrier()) {
HType type = instr->hydrogen()->value()->type();
SmiCheck check_needed =
=======================================
--- /branches/bleeding_edge/src/x64/lithium-x64.cc Wed Dec 7 00:34:27 2011
+++ /branches/bleeding_edge/src/x64/lithium-x64.cc Fri Dec 9 01:50:30 2011
@@ -1786,7 +1786,8 @@
LInstruction* LChunkBuilder::DoLoadContextSlot(HLoadContextSlot* instr) {
LOperand* context = UseRegisterAtStart(instr->value());
- return DefineAsRegister(new LLoadContextSlot(context));
+ LInstruction* result = DefineAsRegister(new LLoadContextSlot(context));
+ return instr->RequiresHoleCheck() ? AssignEnvironment(result) : result;
}
@@ -1803,7 +1804,8 @@
value = UseRegister(instr->value());
temp = NULL;
}
- return new LStoreContextSlot(context, value, temp);
+ LInstruction* result = new LStoreContextSlot(context, value, temp);
+ return instr->RequiresHoleCheck() ? AssignEnvironment(result) : result;
}
=======================================
--- /branches/bleeding_edge/test/mjsunit/harmony/block-let-crankshaft.js
Tue Dec 6 01:41:06 2011
+++ /branches/bleeding_edge/test/mjsunit/harmony/block-let-crankshaft.js
Fri Dec 9 01:50:30 2011
@@ -31,7 +31,8 @@
"use strict";
// Check that the following functions are optimizable.
-var functions = [ f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12 ];
+var functions = [ f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13,
f14,
+ f15, f16, f17, f18, f19, f20, f21, f22, f23 ];
for (var i = 0; i < functions.length; ++i) {
var func = functions[i];
@@ -95,6 +96,66 @@
x = 1;
}
+function f13(x) {
+ (function() { x; });
+}
+
+function f14() {
+ let x;
+ (function() { x; });
+}
+
+function f15() {
+ function x() {
+ }
+ (function() { x; });
+}
+
+function f16() {
+ let x = 1;
+ (function() { x; });
+}
+
+function f17() {
+ const x = 1;
+ (function() { x; });
+}
+
+function f18(x) {
+ return x;
+ (function() { x; });
+}
+
+function f19() {
+ let x;
+ return x;
+ (function() { x; });
+}
+
+function f20() {
+ function x() {
+ }
+ return x;
+ (function() { x; });
+}
+
+function f21(x) {
+ x = 1;
+ (function() { x; });
+}
+
+function f22() {
+ let x;
+ x = 1;
+ (function() { x; });
+}
+
+function f23() {
+ function x() { }
+ x = 1;
+ (function() { x; });
+}
+
// Test that temporal dead zone semantics for function and block scoped
// let bindings are handled by the optimizing compiler.
@@ -120,9 +181,36 @@
assertInstanceof(e, ReferenceError);
}
}
+
+function TestFunctionContext(s) {
+ 'use strict';
+ var func = eval("(function baz(){ " + s + "; (function() { x; }); })");
+ print("Testing:");
+ print(func);
+ for (var i = 0; i < 5; ++i) {
+ print(i);
+ try {
+ func();
+ assertUnreachable();
+ } catch (e) {
+ assertInstanceof(e, ReferenceError);
+ }
+ }
+ print("optimize");
+ %OptimizeFunctionOnNextCall(func);
+ try {
+ print("call");
+ func();
+ assertUnreachable();
+ } catch (e) {
+ print("catch");
+ assertInstanceof(e, ReferenceError);
+ }
+}
function TestAll(s) {
TestFunctionLocal(s);
+ TestFunctionContext(s);
}
// Use before initialization in declaration statement.
--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev