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 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@10220 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
ec66c36fbf
commit
f1649cf39c
@ -1798,7 +1798,8 @@ LInstruction* LChunkBuilder::DoStoreGlobalGeneric(HStoreGlobalGeneric* instr) {
|
||||
|
||||
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 @@ LInstruction* LChunkBuilder::DoStoreContextSlot(HStoreContextSlot* instr) {
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -2303,6 +2303,11 @@ void LCodeGen::DoLoadContextSlot(LLoadContextSlot* instr) {
|
||||
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 @@ void LCodeGen::DoStoreContextSlot(LStoreContextSlot* instr) {
|
||||
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();
|
||||
|
@ -3447,8 +3447,21 @@ class HStoreGlobalGeneric: public HTemplateInstruction<3> {
|
||||
|
||||
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);
|
||||
@ -3456,6 +3469,10 @@ class HLoadContextSlot: public HUnaryOperation {
|
||||
|
||||
int slot_index() const { return slot_index_; }
|
||||
|
||||
bool RequiresHoleCheck() {
|
||||
return mode_ == kLoadCheck;
|
||||
}
|
||||
|
||||
virtual Representation RequiredInputRepresentation(int index) {
|
||||
return Representation::Tagged();
|
||||
}
|
||||
@ -3472,13 +3489,25 @@ class HLoadContextSlot: public HUnaryOperation {
|
||||
|
||||
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,11 +3516,16 @@ class HStoreContextSlot: public HTemplateInstruction<2> {
|
||||
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 @@ class HStoreContextSlot: public HTemplateInstruction<2> {
|
||||
|
||||
private:
|
||||
int slot_index_;
|
||||
Mode mode_;
|
||||
};
|
||||
|
||||
|
||||
|
@ -3285,15 +3285,11 @@ void HGraphBuilder::VisitVariableProxy(VariableProxy* expr) {
|
||||
}
|
||||
|
||||
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 @@ void HGraphBuilder::HandleCompoundAssignment(Assignment* expr) {
|
||||
}
|
||||
|
||||
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 @@ void HGraphBuilder::VisitAssignment(Assignment* expr) {
|
||||
// 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 @@ void HGraphBuilder::VisitAssignment(Assignment* expr) {
|
||||
|
||||
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 @@ void HGraphBuilder::VisitCountOperation(CountOperation* expr) {
|
||||
}
|
||||
|
||||
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 @@ void HGraphBuilder::HandleDeclaration(VariableProxy* proxy,
|
||||
}
|
||||
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 {
|
||||
|
@ -2165,13 +2165,22 @@ 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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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 =
|
||||
|
@ -1875,7 +1875,9 @@ LInstruction* LChunkBuilder::DoStoreGlobalGeneric(HStoreGlobalGeneric* instr) {
|
||||
|
||||
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 @@ LInstruction* LChunkBuilder::DoStoreContextSlot(HStoreContextSlot* instr) {
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -2067,13 +2067,22 @@ void LCodeGen::DoLoadContextSlot(LLoadContextSlot* instr) {
|
||||
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 =
|
||||
|
@ -1786,7 +1786,8 @@ LInstruction* LChunkBuilder::DoStoreGlobalGeneric(HStoreGlobalGeneric* instr) {
|
||||
|
||||
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 @@ LInstruction* LChunkBuilder::DoStoreContextSlot(HStoreContextSlot* instr) {
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -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 @@ function f12() {
|
||||
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.
|
||||
@ -121,8 +182,35 @@ function TestFunctionLocal(s) {
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
|
Loading…
Reference in New Issue
Block a user