Evaluator optimizations

reducing the stack size for reporters
pull/3/merge
jmoenig 2014-11-25 12:24:20 +01:00
rodzic 4be96bb240
commit 75849a59a2
2 zmienionych plików z 67 dodań i 66 usunięć

Wyświetl plik

@ -2342,7 +2342,7 @@ ______
------- -------
* Threads: Fix “stop this block” primitive for tail-call-elimination * Threads: Fix “stop this block” primitive for tail-call-elimination
1411214 1411224
------- -------
* Threads: Fixed #318 * Threads: Fixed #318
* Objects: Fixed #416 * Objects: Fixed #416
@ -2353,3 +2353,7 @@ ______
* snap.html, favicon.ico: new Favicon, thanks, Michael! * snap.html, favicon.ico: new Favicon, thanks, Michael!
* Threads: improved whitespace detection for “split” primitive, thanks, Michael! * Threads: improved whitespace detection for “split” primitive, thanks, Michael!
* Threads: tail-call-elimination for reporters experiment (commented out, under construction) * Threads: tail-call-elimination for reporters experiment (commented out, under construction)
1411225
-------
* Threads: Evaluator optimizations (reducing the stack size for reporters)

Wyświetl plik

@ -83,7 +83,7 @@ ArgLabelMorph, localize, XML_Element, hex_sha512*/
// Global stuff //////////////////////////////////////////////////////// // Global stuff ////////////////////////////////////////////////////////
modules.threads = '2014-November-24'; modules.threads = '2014-November-25';
var ThreadManager; var ThreadManager;
var Process; var Process;
@ -218,11 +218,10 @@ ThreadManager.prototype.resumeAll = function (stage) {
}; };
ThreadManager.prototype.step = function () { ThreadManager.prototype.step = function () {
/* // run each process until it gives up control, skipping processes
run each process until it gives up control, skipping processes // for sprites that are currently picked up, then filter out any
for sprites that are currently picked up, then filter out any // processes that have been terminated
processes that have been terminated
*/
this.processes.forEach(function (proc) { this.processes.forEach(function (proc) {
if (!proc.homeContext.receiver.isPickedUp() && !proc.isDead) { if (!proc.homeContext.receiver.isPickedUp() && !proc.isDead) {
proc.runStep(); proc.runStep();
@ -384,13 +383,13 @@ Process.prototype.isRunning = function () {
// Process entry points // Process entry points
Process.prototype.runStep = function () { Process.prototype.runStep = function () {
/* // a step is an an uninterruptable 'atom', it can consist
a step is an an uninterruptable 'atom', it can consist // of several contexts, even of several blocks
of several contexts, even of several blocks
*/
if (this.isPaused) { // allow pausing in between atomic steps: if (this.isPaused) { // allow pausing in between atomic steps:
return this.pauseStep(); return this.pauseStep();
} }
this.readyToYield = false; this.readyToYield = false;
while (!this.readyToYield while (!this.readyToYield
&& this.context && this.context
@ -459,6 +458,9 @@ Process.prototype.pauseStep = function () {
Process.prototype.evaluateContext = function () { Process.prototype.evaluateContext = function () {
var exp = this.context.expression; var exp = this.context.expression;
this.frameCount += 1; this.frameCount += 1;
if (this.context.tag === 'exit') {
this.expectReport();
}
if (exp instanceof Array) { if (exp instanceof Array) {
return this.evaluateSequence(exp); return this.evaluateSequence(exp);
} }
@ -482,7 +484,7 @@ Process.prototype.evaluateContext = function () {
Process.prototype.evaluateBlock = function (block, argCount) { Process.prototype.evaluateBlock = function (block, argCount) {
// check for special forms // check for special forms
if (contains(['reportOr', 'reportAnd'], block.selector)) { if (contains(['reportOr', 'reportAnd', 'doReport'], block.selector)) {
return this[block.selector](block); return this[block.selector](block);
} }
@ -548,33 +550,29 @@ Process.prototype.reportAnd = function (block) {
} }
}; };
/*
tail-call-elimination for reporters
-----------------------------------
currently under construction, commented out for now
to activate add 'doReport' to the list of special forms
selectors in evaluateBlock() and comment out / remove
the current 'doReport' primitive in the "return"
section of the code
Process.prototype.doReport = function (block) { Process.prototype.doReport = function (block) {
if (this.context.expression.partOfCustomCommand) {
// if (this.context.expression.partOfCustomCommand) { this.doStopCustomBlock();
// return this.doStopCustomBlock(); this.popContext();
// } return;
}
var outer = this.context.outerContext; var outer = this.context.outerContext;
while (this.context && this.context.expression !== 'exitReporter') { while (this.context && this.context.tag !== 'exit') {
if (this.context.expression === 'doStopWarping') { if (this.context.expression === 'doStopWarping') {
this.doStopWarping(); this.doStopWarping();
} else { } else {
this.popContext(); this.popContext();
} }
} }
this.popContext(); if (this.context.expression === 'expectReport') {
// pop off inserted top-level exit context
this.popContext();
} else {
// un-tag and preserve original caller
this.context.tag = null;
}
this.pushContext(block.inputs()[0], outer); this.pushContext(block.inputs()[0], outer);
}; };
*/
// Process: Non-Block evaluation // Process: Non-Block evaluation
@ -724,9 +722,8 @@ Process.prototype.doYield = function () {
} }
}; };
Process.prototype.exitReporter = function () { Process.prototype.expectReport = function () {
// catch-tag for REPORT and STOP BLOCK primitives this.handleError(new Error("reporter didn't report"));
this.handleError(new Error("missing 'report' statement in reporter"));
}; };
// Process Exception Handling // Process Exception Handling
@ -831,9 +828,10 @@ Process.prototype.evaluate = function (
} }
var outer = new Context(null, null, context.outerContext), var outer = new Context(null, null, context.outerContext),
caller = this.context.parentContext,
exit,
runnable, runnable,
extra, extra,
exit,
parms = args.asArray(), parms = args.asArray(),
i, i,
value; value;
@ -904,17 +902,22 @@ Process.prototype.evaluate = function (
if (runnable.expression instanceof CommandBlockMorph) { if (runnable.expression instanceof CommandBlockMorph) {
runnable.expression = runnable.expression.blockSequence(); runnable.expression = runnable.expression.blockSequence();
// insert a reporter exit tag for the
// CALL SCRIPT primitive variant
if (!isCommand) { if (!isCommand) {
exit = new Context( if (caller) {
runnable.parentContext, // tag caller, so "report" can catch it later
'exitReporter', caller.tag = 'exit';
outer, } else {
outer.receiver // top-level context, insert a tagged exit context
); // which "report" can catch later
runnable.parentContext = exit; exit = new Context(
runnable.parentContext,
'expectReport',
outer,
outer.receiver
);
exit.tag = 'exit';
runnable.parentContext = exit;
}
} }
} }
}; };
@ -993,21 +996,7 @@ Process.prototype.fork = function (context, args) {
stage.threads.processes.push(proc); stage.threads.processes.push(proc);
}; };
// Process "return" primitives // Process stopping blocks primitives
Process.prototype.doReport = function (value) {
if (this.context.expression.partOfCustomCommand) {
return this.doStopCustomBlock();
}
while (this.context && this.context.expression !== 'exitReporter') {
if (this.context.expression === 'doStopWarping') {
this.doStopWarping();
} else {
this.popContext();
}
}
return value;
};
Process.prototype.doStopBlock = function () { Process.prototype.doStopBlock = function () {
var target = this.context.expression.exitTag; var target = this.context.expression.exitTag;
@ -1119,13 +1108,21 @@ Process.prototype.evaluateCustomBlock = function () {
// insert a reporter exit tag for the // insert a reporter exit tag for the
// CALL SCRIPT primitive variant // CALL SCRIPT primitive variant
if (this.context.expression.definition.type !== 'command') { if (this.context.expression.definition.type !== 'command') {
exit = new Context( if (caller) {
runnable.parentContext, // tag caller, so "report" can catch it later
'exitReporter', caller.tag = 'exit';
outer, } else {
outer.receiver // top-level context, insert a tagged exit context
); // which "report" can catch later
runnable.parentContext = exit; exit = new Context(
runnable.parentContext,
'expectReport',
outer,
outer.receiver
);
exit.tag = 'exit';
runnable.parentContext = exit;
}
} else { } else {
// tag all "stop this block" blocks with the current // tag all "stop this block" blocks with the current
// procedureCount as exitTag, and mark all "report" blocks // procedureCount as exitTag, and mark all "report" blocks
@ -2925,7 +2922,7 @@ Context.prototype.continuation = function () {
} else { } else {
return new Context(null, 'doStop'); return new Context(null, 'doStop');
} }
if (cont.expression === 'exitReporter') { if (cont.expression === 'expectReport') {
return cont.continuation(); return cont.continuation();
} }
cont = cont.copyForContinuation(); cont = cont.copyForContinuation();