зеркало из https://github.com/mozilla/gecko-dev.git
Bug 684384 - Ion-compile break-to-labeled-scope. r=dvander
This commit is contained in:
Родитель
0f92a37666
Коммит
653b0a892c
|
@ -233,6 +233,8 @@ IonBuilder::popCfgStack()
|
||||||
{
|
{
|
||||||
if (cfgStack_.back().isLoop())
|
if (cfgStack_.back().isLoop())
|
||||||
loops_.popBack();
|
loops_.popBack();
|
||||||
|
if (cfgStack_.back().state == CFGState::LABEL)
|
||||||
|
labels_.popBack();
|
||||||
cfgStack_.popBack();
|
cfgStack_.popBack();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -774,7 +776,7 @@ IonBuilder::inspectOpcode(JSOp op)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case JSOP_LABEL:
|
case JSOP_LABEL:
|
||||||
return true;
|
return jsop_label();
|
||||||
|
|
||||||
case JSOP_UNDEFINED:
|
case JSOP_UNDEFINED:
|
||||||
return pushConstant(UndefinedValue());
|
return pushConstant(UndefinedValue());
|
||||||
|
@ -1199,6 +1201,9 @@ IonBuilder::processCfgEntry(CFGState &state)
|
||||||
case CFGState::AND_OR:
|
case CFGState::AND_OR:
|
||||||
return processAndOrEnd(state);
|
return processAndOrEnd(state);
|
||||||
|
|
||||||
|
case CFGState::LABEL:
|
||||||
|
return processLabelEnd(state);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
JS_NOT_REACHED("unknown cfgstate");
|
JS_NOT_REACHED("unknown cfgstate");
|
||||||
}
|
}
|
||||||
|
@ -1627,46 +1632,68 @@ IonBuilder::processAndOrEnd(CFGState &state)
|
||||||
return ControlStatus_Joined;
|
return ControlStatus_Joined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IonBuilder::ControlStatus
|
||||||
|
IonBuilder::processLabelEnd(CFGState &state)
|
||||||
|
{
|
||||||
|
JS_ASSERT(state.state == CFGState::LABEL);
|
||||||
|
|
||||||
|
// If there are no breaks and no current, controlflow is terminated.
|
||||||
|
if (!state.label.breaks && !current)
|
||||||
|
return ControlStatus_Ended;
|
||||||
|
|
||||||
|
// If there are no breaks to this label, there's nothing to do.
|
||||||
|
if (!state.label.breaks)
|
||||||
|
return ControlStatus_Joined;
|
||||||
|
|
||||||
|
MBasicBlock *successor = createBreakCatchBlock(state.label.breaks, state.stopAt);
|
||||||
|
if (!successor)
|
||||||
|
return ControlStatus_Error;
|
||||||
|
|
||||||
|
if (current) {
|
||||||
|
current->end(MGoto::New(successor));
|
||||||
|
successor->addPredecessor(current);
|
||||||
|
}
|
||||||
|
|
||||||
|
pc = state.stopAt;
|
||||||
|
current = successor;
|
||||||
|
return ControlStatus_Joined;
|
||||||
|
}
|
||||||
|
|
||||||
IonBuilder::ControlStatus
|
IonBuilder::ControlStatus
|
||||||
IonBuilder::processBreak(JSOp op, jssrcnote *sn)
|
IonBuilder::processBreak(JSOp op, jssrcnote *sn)
|
||||||
{
|
{
|
||||||
JS_ASSERT(op == JSOP_GOTO);
|
JS_ASSERT(op == JSOP_GOTO);
|
||||||
|
|
||||||
// Find the target loop.
|
JS_ASSERT(SN_TYPE(sn) == SRC_BREAK ||
|
||||||
CFGState *found = NULL;
|
SN_TYPE(sn) == SRC_BREAK2LABEL);
|
||||||
|
|
||||||
|
// Find the break target.
|
||||||
jsbytecode *target = pc + GetJumpOffset(pc);
|
jsbytecode *target = pc + GetJumpOffset(pc);
|
||||||
for (size_t i = loops_.length() - 1; i < loops_.length(); i--) {
|
DebugOnly<bool> found = false;
|
||||||
CFGState &cfg = cfgStack_[loops_[i].cfgEntry];
|
|
||||||
if (cfg.loop.exitpc == target) {
|
if (SN_TYPE(sn) == SRC_BREAK2LABEL) {
|
||||||
found = &cfg;
|
for (size_t i = labels_.length() - 1; i < labels_.length(); i--) {
|
||||||
break;
|
CFGState &cfg = cfgStack_[labels_[i].cfgEntry];
|
||||||
|
JS_ASSERT(cfg.state == CFGState::LABEL);
|
||||||
|
if (cfg.stopAt == target) {
|
||||||
|
cfg.label.breaks = new DeferredEdge(current, cfg.label.breaks);
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (size_t i = loops_.length() - 1; i < loops_.length(); i--) {
|
||||||
|
CFGState &cfg = cfgStack_[loops_[i].cfgEntry];
|
||||||
|
JS_ASSERT(cfg.isLoop());
|
||||||
|
if (cfg.loop.exitpc == target) {
|
||||||
|
cfg.loop.breaks = new DeferredEdge(current, cfg.loop.breaks);
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!found) {
|
JS_ASSERT(found);
|
||||||
// Sometimes, we can't determine the structure of a labeled break. For
|
|
||||||
// example:
|
|
||||||
//
|
|
||||||
// 0: label: {
|
|
||||||
// 1: for (;;) {
|
|
||||||
// 2: break label;
|
|
||||||
// 3: }
|
|
||||||
// 4: stuff;
|
|
||||||
// 5: }
|
|
||||||
//
|
|
||||||
// In this case, the successor of the block is 4, but the target of the
|
|
||||||
// single-level break is actually 5. To recognize this case we'd need
|
|
||||||
// to know about the label structure at 0,5 ahead of time - and lacking
|
|
||||||
// those source notes for now, we just abort instead.
|
|
||||||
abort("could not find the target of a break");
|
|
||||||
return ControlStatus_Error;
|
|
||||||
}
|
|
||||||
|
|
||||||
// There must always be a valid target loop structure. If not, there's
|
|
||||||
// probably an off-by-something error in which pc we track.
|
|
||||||
CFGState &state = *found;
|
|
||||||
|
|
||||||
state.loop.breaks = new DeferredEdge(current, state.loop.breaks);
|
|
||||||
|
|
||||||
current = NULL;
|
current = NULL;
|
||||||
pc += js_CodeSpec[op].length;
|
pc += js_CodeSpec[op].length;
|
||||||
|
@ -2181,6 +2208,21 @@ IonBuilder::tableSwitch(JSOp op, jssrcnote *sn)
|
||||||
return ControlStatus_Jumped;
|
return ControlStatus_Jumped;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
IonBuilder::jsop_label()
|
||||||
|
{
|
||||||
|
JS_ASSERT(JSOp(*pc) == JSOP_LABEL);
|
||||||
|
|
||||||
|
jsbytecode *endpc = pc + GET_JUMP_OFFSET(pc);
|
||||||
|
JS_ASSERT(endpc > pc);
|
||||||
|
|
||||||
|
ControlFlowInfo label(cfgStack_.length(), endpc);
|
||||||
|
if (!labels_.append(label))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return cfgStack_.append(CFGState::Label(endpc));
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
IonBuilder::jsop_condswitch()
|
IonBuilder::jsop_condswitch()
|
||||||
{
|
{
|
||||||
|
@ -2278,6 +2320,16 @@ IonBuilder::CFGState::CondSwitch(jsbytecode *exitpc, jsbytecode *defaultTarget)
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IonBuilder::CFGState
|
||||||
|
IonBuilder::CFGState::Label(jsbytecode *exitpc)
|
||||||
|
{
|
||||||
|
CFGState state;
|
||||||
|
state.state = LABEL;
|
||||||
|
state.stopAt = exitpc;
|
||||||
|
state.label.breaks = NULL;
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
IonBuilder::ControlStatus
|
IonBuilder::ControlStatus
|
||||||
IonBuilder::processCondSwitchCase(CFGState &state)
|
IonBuilder::processCondSwitchCase(CFGState &state)
|
||||||
{
|
{
|
||||||
|
|
|
@ -73,7 +73,8 @@ class IonBuilder : public MIRGenerator
|
||||||
TABLE_SWITCH, // switch() { x }
|
TABLE_SWITCH, // switch() { x }
|
||||||
COND_SWITCH_CASE, // switch() { case X: ... }
|
COND_SWITCH_CASE, // switch() { case X: ... }
|
||||||
COND_SWITCH_BODY, // switch() { case ...: X }
|
COND_SWITCH_BODY, // switch() { case ...: X }
|
||||||
AND_OR // && x, || x
|
AND_OR, // && x, || x
|
||||||
|
LABEL // label: x
|
||||||
};
|
};
|
||||||
|
|
||||||
State state; // Current state of this control structure.
|
State state; // Current state of this control structure.
|
||||||
|
@ -140,6 +141,9 @@ class IonBuilder : public MIRGenerator
|
||||||
jsbytecode *exitpc;
|
jsbytecode *exitpc;
|
||||||
DeferredEdge *breaks;
|
DeferredEdge *breaks;
|
||||||
} condswitch;
|
} condswitch;
|
||||||
|
struct {
|
||||||
|
DeferredEdge *breaks;
|
||||||
|
} label;
|
||||||
};
|
};
|
||||||
|
|
||||||
inline bool isLoop() const {
|
inline bool isLoop() const {
|
||||||
|
@ -162,6 +166,7 @@ class IonBuilder : public MIRGenerator
|
||||||
static CFGState AndOr(jsbytecode *join, MBasicBlock *joinStart);
|
static CFGState AndOr(jsbytecode *join, MBasicBlock *joinStart);
|
||||||
static CFGState TableSwitch(jsbytecode *exitpc, MTableSwitch *ins);
|
static CFGState TableSwitch(jsbytecode *exitpc, MTableSwitch *ins);
|
||||||
static CFGState CondSwitch(jsbytecode *exitpc, jsbytecode *defaultTarget);
|
static CFGState CondSwitch(jsbytecode *exitpc, jsbytecode *defaultTarget);
|
||||||
|
static CFGState Label(jsbytecode *exitpc);
|
||||||
};
|
};
|
||||||
|
|
||||||
static int CmpSuccessors(const void *a, const void *b);
|
static int CmpSuccessors(const void *a, const void *b);
|
||||||
|
@ -214,6 +219,7 @@ class IonBuilder : public MIRGenerator
|
||||||
ControlStatus processSwitchBreak(JSOp op, jssrcnote *sn);
|
ControlStatus processSwitchBreak(JSOp op, jssrcnote *sn);
|
||||||
ControlStatus processSwitchEnd(DeferredEdge *breaks, jsbytecode *exitpc);
|
ControlStatus processSwitchEnd(DeferredEdge *breaks, jsbytecode *exitpc);
|
||||||
ControlStatus processAndOrEnd(CFGState &state);
|
ControlStatus processAndOrEnd(CFGState &state);
|
||||||
|
ControlStatus processLabelEnd(CFGState &state);
|
||||||
ControlStatus processReturn(JSOp op);
|
ControlStatus processReturn(JSOp op);
|
||||||
ControlStatus processThrow();
|
ControlStatus processThrow();
|
||||||
ControlStatus processContinue(JSOp op, jssrcnote *sn);
|
ControlStatus processContinue(JSOp op, jssrcnote *sn);
|
||||||
|
@ -329,6 +335,7 @@ class IonBuilder : public MIRGenerator
|
||||||
bool jsop_funapplyarguments(uint32_t argc);
|
bool jsop_funapplyarguments(uint32_t argc);
|
||||||
bool jsop_call(uint32_t argc, bool constructing);
|
bool jsop_call(uint32_t argc, bool constructing);
|
||||||
bool jsop_ifeq(JSOp op);
|
bool jsop_ifeq(JSOp op);
|
||||||
|
bool jsop_label();
|
||||||
bool jsop_condswitch();
|
bool jsop_condswitch();
|
||||||
bool jsop_andor(JSOp op);
|
bool jsop_andor(JSOp op);
|
||||||
bool jsop_dup2();
|
bool jsop_dup2();
|
||||||
|
@ -509,6 +516,7 @@ class IonBuilder : public MIRGenerator
|
||||||
Vector<CFGState, 8, IonAllocPolicy> cfgStack_;
|
Vector<CFGState, 8, IonAllocPolicy> cfgStack_;
|
||||||
Vector<ControlFlowInfo, 4, IonAllocPolicy> loops_;
|
Vector<ControlFlowInfo, 4, IonAllocPolicy> loops_;
|
||||||
Vector<ControlFlowInfo, 0, IonAllocPolicy> switches_;
|
Vector<ControlFlowInfo, 0, IonAllocPolicy> switches_;
|
||||||
|
Vector<ControlFlowInfo, 2, IonAllocPolicy> labels_;
|
||||||
Vector<MInstruction *, 2, IonAllocPolicy> iterators_;
|
Vector<MInstruction *, 2, IonAllocPolicy> iterators_;
|
||||||
TypeOracle *oracle;
|
TypeOracle *oracle;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
// Labeled break tests.
|
||||||
|
function f1() {
|
||||||
|
foo:
|
||||||
|
if ([1]) {
|
||||||
|
bar:
|
||||||
|
for (var i=0; i<100; i++) {
|
||||||
|
if (i > 60)
|
||||||
|
break foo;
|
||||||
|
}
|
||||||
|
assertEq(0, 1);
|
||||||
|
}
|
||||||
|
assertEq(i, 61);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
assertEq(f1(), true);
|
||||||
|
|
||||||
|
// Label with no breaks.
|
||||||
|
function f2() {
|
||||||
|
foo:
|
||||||
|
if ([1]) {
|
||||||
|
for (var i=0; i<100; i++) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertEq(i, 100);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
assertEq(f2(), true);
|
||||||
|
|
||||||
|
// No breaks and early return.
|
||||||
|
function f3() {
|
||||||
|
foo: {
|
||||||
|
if (true) {
|
||||||
|
for (var i=0; i<100; i++) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
assertEq(i, 100);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
assertEq(f3(), false);
|
||||||
|
|
||||||
|
// Multiple breaks.
|
||||||
|
function f4() {
|
||||||
|
foo: {
|
||||||
|
if (true) {
|
||||||
|
for (var i=0; i<100; i++)
|
||||||
|
if (i > 70)
|
||||||
|
break foo;
|
||||||
|
if (i > 80)
|
||||||
|
break foo;
|
||||||
|
}
|
||||||
|
break foo;
|
||||||
|
}
|
||||||
|
assertEq(i, 71);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
assertEq(f4(), true);
|
Загрузка…
Ссылка в новой задаче