зеркало из 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())
|
||||
loops_.popBack();
|
||||
if (cfgStack_.back().state == CFGState::LABEL)
|
||||
labels_.popBack();
|
||||
cfgStack_.popBack();
|
||||
}
|
||||
|
||||
|
@ -774,7 +776,7 @@ IonBuilder::inspectOpcode(JSOp op)
|
|||
return true;
|
||||
|
||||
case JSOP_LABEL:
|
||||
return true;
|
||||
return jsop_label();
|
||||
|
||||
case JSOP_UNDEFINED:
|
||||
return pushConstant(UndefinedValue());
|
||||
|
@ -1199,6 +1201,9 @@ IonBuilder::processCfgEntry(CFGState &state)
|
|||
case CFGState::AND_OR:
|
||||
return processAndOrEnd(state);
|
||||
|
||||
case CFGState::LABEL:
|
||||
return processLabelEnd(state);
|
||||
|
||||
default:
|
||||
JS_NOT_REACHED("unknown cfgstate");
|
||||
}
|
||||
|
@ -1627,46 +1632,68 @@ IonBuilder::processAndOrEnd(CFGState &state)
|
|||
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::processBreak(JSOp op, jssrcnote *sn)
|
||||
{
|
||||
JS_ASSERT(op == JSOP_GOTO);
|
||||
|
||||
// Find the target loop.
|
||||
CFGState *found = NULL;
|
||||
JS_ASSERT(SN_TYPE(sn) == SRC_BREAK ||
|
||||
SN_TYPE(sn) == SRC_BREAK2LABEL);
|
||||
|
||||
// Find the break target.
|
||||
jsbytecode *target = pc + GetJumpOffset(pc);
|
||||
for (size_t i = loops_.length() - 1; i < loops_.length(); i--) {
|
||||
CFGState &cfg = cfgStack_[loops_[i].cfgEntry];
|
||||
if (cfg.loop.exitpc == target) {
|
||||
found = &cfg;
|
||||
break;
|
||||
DebugOnly<bool> found = false;
|
||||
|
||||
if (SN_TYPE(sn) == SRC_BREAK2LABEL) {
|
||||
for (size_t i = labels_.length() - 1; i < labels_.length(); i--) {
|
||||
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) {
|
||||
// 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);
|
||||
JS_ASSERT(found);
|
||||
|
||||
current = NULL;
|
||||
pc += js_CodeSpec[op].length;
|
||||
|
@ -2181,6 +2208,21 @@ IonBuilder::tableSwitch(JSOp op, jssrcnote *sn)
|
|||
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
|
||||
IonBuilder::jsop_condswitch()
|
||||
{
|
||||
|
@ -2278,6 +2320,16 @@ IonBuilder::CFGState::CondSwitch(jsbytecode *exitpc, jsbytecode *defaultTarget)
|
|||
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::processCondSwitchCase(CFGState &state)
|
||||
{
|
||||
|
|
|
@ -73,7 +73,8 @@ class IonBuilder : public MIRGenerator
|
|||
TABLE_SWITCH, // switch() { x }
|
||||
COND_SWITCH_CASE, // 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.
|
||||
|
@ -140,6 +141,9 @@ class IonBuilder : public MIRGenerator
|
|||
jsbytecode *exitpc;
|
||||
DeferredEdge *breaks;
|
||||
} condswitch;
|
||||
struct {
|
||||
DeferredEdge *breaks;
|
||||
} label;
|
||||
};
|
||||
|
||||
inline bool isLoop() const {
|
||||
|
@ -162,6 +166,7 @@ class IonBuilder : public MIRGenerator
|
|||
static CFGState AndOr(jsbytecode *join, MBasicBlock *joinStart);
|
||||
static CFGState TableSwitch(jsbytecode *exitpc, MTableSwitch *ins);
|
||||
static CFGState CondSwitch(jsbytecode *exitpc, jsbytecode *defaultTarget);
|
||||
static CFGState Label(jsbytecode *exitpc);
|
||||
};
|
||||
|
||||
static int CmpSuccessors(const void *a, const void *b);
|
||||
|
@ -214,6 +219,7 @@ class IonBuilder : public MIRGenerator
|
|||
ControlStatus processSwitchBreak(JSOp op, jssrcnote *sn);
|
||||
ControlStatus processSwitchEnd(DeferredEdge *breaks, jsbytecode *exitpc);
|
||||
ControlStatus processAndOrEnd(CFGState &state);
|
||||
ControlStatus processLabelEnd(CFGState &state);
|
||||
ControlStatus processReturn(JSOp op);
|
||||
ControlStatus processThrow();
|
||||
ControlStatus processContinue(JSOp op, jssrcnote *sn);
|
||||
|
@ -329,6 +335,7 @@ class IonBuilder : public MIRGenerator
|
|||
bool jsop_funapplyarguments(uint32_t argc);
|
||||
bool jsop_call(uint32_t argc, bool constructing);
|
||||
bool jsop_ifeq(JSOp op);
|
||||
bool jsop_label();
|
||||
bool jsop_condswitch();
|
||||
bool jsop_andor(JSOp op);
|
||||
bool jsop_dup2();
|
||||
|
@ -509,6 +516,7 @@ class IonBuilder : public MIRGenerator
|
|||
Vector<CFGState, 8, IonAllocPolicy> cfgStack_;
|
||||
Vector<ControlFlowInfo, 4, IonAllocPolicy> loops_;
|
||||
Vector<ControlFlowInfo, 0, IonAllocPolicy> switches_;
|
||||
Vector<ControlFlowInfo, 2, IonAllocPolicy> labels_;
|
||||
Vector<MInstruction *, 2, IonAllocPolicy> iterators_;
|
||||
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);
|
Загрузка…
Ссылка в новой задаче