Introduce procedure warp variants
We now know at compile time whether a procedure is in warp mode or not This allows more optimizations to be made in non-warp scripts used by warp scripts
This commit is contained in:
1 parent
5e59cfa4c8
commit
434b1da69e
4 files changed
+73
-38
No files matched your search
@@ -37,10 +37,10 @@ const compile = thread => {
|
||||
|
||||
const entry = compileScript(ir.entry);
|
||||
|
||||
for (const procedureCode of Object.keys(ir.procedures)) {
|
||||
const procedureData = ir.procedures[procedureCode];
|
||||
for (const procedureVariant of Object.keys(ir.procedures)) {
|
||||
const procedureData = ir.procedures[procedureVariant];
|
||||
const procedureTree = compileScript(procedureData);
|
||||
procedures[procedureCode] = procedureTree;
|
||||
procedures[procedureVariant] = procedureTree;
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@@ -43,7 +43,7 @@ class IntermediateScript {
|
||||
this.isProcedure = false;
|
||||
|
||||
/**
|
||||
* The name of this procedure, if any.
|
||||
* This procedure's code, if any.
|
||||
* @type {string}
|
||||
*/
|
||||
this.procedureCode = '';
|
||||
@@ -100,7 +100,7 @@ class IntermediateRepresentation {
|
||||
this.entry = null;
|
||||
|
||||
/**
|
||||
* Maps procedure IDs to their intermediate script.
|
||||
* Maps procedure variants to their intermediate script.
|
||||
* @type {Object.<string, IntermediateScript>}
|
||||
*/
|
||||
this.procedures = {};
|
||||
|
||||
+65
-31
@@ -47,6 +47,30 @@ const createVariableData = (scope, varObj) => ({
|
||||
isCloud: varObj.isCloud
|
||||
});
|
||||
|
||||
/**
|
||||
* @param {string} code
|
||||
* @param {boolean} warp
|
||||
* @returns {string}
|
||||
*/
|
||||
const generateProcedureVariant = (code, warp) => {
|
||||
if (warp) {
|
||||
return `W${code}`;
|
||||
}
|
||||
return `Z${code}`;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} variant Variant generated by generateProcedureVariant()
|
||||
* @returns {string} original procedure code
|
||||
*/
|
||||
const parseProcedureCode = variant => variant.substring(1);
|
||||
|
||||
/**
|
||||
* @param {string} variant Variant generated by generateProcedureVariant()
|
||||
* @returns {boolean} true if warp enabled
|
||||
*/
|
||||
const parseIsWarp = variant => variant.charAt(0) === 'W';
|
||||
|
||||
class ScriptTreeGenerator {
|
||||
constructor (thread) {
|
||||
/** @private */
|
||||
@@ -74,14 +98,16 @@ class ScriptTreeGenerator {
|
||||
this.variableCache = {};
|
||||
}
|
||||
|
||||
setProcedureCode (procedureCode) {
|
||||
setProcedureVariant (procedureVariant) {
|
||||
const procedureCode = parseProcedureCode(procedureVariant);
|
||||
|
||||
this.script.procedureCode = procedureCode;
|
||||
this.script.isProcedure = true;
|
||||
this.script.yields = false;
|
||||
|
||||
const paramNamesIdsAndDefaults = this.blocks.getProcedureParamNamesIdsAndDefaults(procedureCode);
|
||||
if (paramNamesIdsAndDefaults === null) {
|
||||
throw new Error(`IR: cannot find procedure: ${procedureCode}`);
|
||||
throw new Error(`IR: cannot find procedure: ${procedureVariant}`);
|
||||
}
|
||||
|
||||
const [paramNames, _paramIds, _paramDefaults] = paramNamesIdsAndDefaults;
|
||||
@@ -1153,8 +1179,27 @@ class ScriptTreeGenerator {
|
||||
};
|
||||
}
|
||||
|
||||
if (!this.script.dependedProcedures.includes(procedureCode)) {
|
||||
this.script.dependedProcedures.push(procedureCode);
|
||||
// These are guaranteed to exist by previous checks
|
||||
const definitionId = this.blocks.getProcedureDefinition(procedureCode);
|
||||
const definitionBlock = this.blocks.getBlock(definitionId);
|
||||
const innerDefinition = this.blocks.getBlock(definitionBlock.inputs.custom_block.block);
|
||||
|
||||
let isWarp = this.script.isWarp;
|
||||
if (!isWarp) {
|
||||
if (innerDefinition && innerDefinition.mutation) {
|
||||
const warp = innerDefinition.mutation.warp;
|
||||
if (typeof warp === 'boolean') {
|
||||
isWarp = warp;
|
||||
} else if (typeof warp === 'string') {
|
||||
isWarp = JSON.parse(warp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const variant = generateProcedureVariant(procedureCode, isWarp);
|
||||
|
||||
if (!this.script.dependedProcedures.includes(variant)) {
|
||||
this.script.dependedProcedures.push(variant);
|
||||
}
|
||||
|
||||
// Non-warp direct recursion yields.
|
||||
@@ -1181,6 +1226,7 @@ class ScriptTreeGenerator {
|
||||
return {
|
||||
kind: 'procedures.call',
|
||||
code: procedureCode,
|
||||
variant,
|
||||
arguments: args
|
||||
};
|
||||
}
|
||||
@@ -1471,18 +1517,19 @@ class IRGenerator {
|
||||
}
|
||||
|
||||
addProcedureDependencies (dependencies) {
|
||||
for (const procedureCode of dependencies) {
|
||||
if (this.procedures.hasOwnProperty(procedureCode)) {
|
||||
for (const procedureVariant of dependencies) {
|
||||
if (this.procedures.hasOwnProperty(procedureVariant)) {
|
||||
continue;
|
||||
}
|
||||
if (this.compilingProcedures.has(procedureCode)) {
|
||||
if (this.compilingProcedures.has(procedureVariant)) {
|
||||
continue;
|
||||
}
|
||||
if (this.proceduresToCompile.has(procedureCode)) {
|
||||
if (this.proceduresToCompile.has(procedureVariant)) {
|
||||
continue;
|
||||
}
|
||||
const procedureCode = parseProcedureCode(procedureVariant);
|
||||
const definition = this.blocks.getProcedureDefinition(procedureCode);
|
||||
this.proceduresToCompile.set(procedureCode, definition);
|
||||
this.proceduresToCompile.set(procedureVariant, definition);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1532,37 +1579,24 @@ class IRGenerator {
|
||||
|
||||
// Compile any required procedures.
|
||||
// As procedures can depend on other procedures, this process may take several iterations.
|
||||
const procedureTreeCache = this.blocks._cache.compiledProcedures;
|
||||
while (this.proceduresToCompile.size > 0) {
|
||||
this.compilingProcedures = this.proceduresToCompile;
|
||||
this.proceduresToCompile = new Map();
|
||||
|
||||
for (const [procedureCode, definitionId] of this.compilingProcedures.entries()) {
|
||||
const definitionBlock = this.blocks.getBlock(definitionId);
|
||||
const innerDefinition = this.blocks.getBlock(definitionBlock.inputs.custom_block.block);
|
||||
|
||||
// Extract the function's warp mode.
|
||||
// See Sequencer.stepToProcedure
|
||||
let isWarp = false;
|
||||
if (innerDefinition && innerDefinition.mutation) {
|
||||
const warp = innerDefinition.mutation.warp;
|
||||
if (typeof warp === 'boolean') {
|
||||
isWarp = warp;
|
||||
} else if (typeof warp === 'string') {
|
||||
isWarp = JSON.parse(warp);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.blocks._cache.compiledProcedures[procedureCode]) {
|
||||
const result = this.blocks._cache.compiledProcedures[procedureCode];
|
||||
this.procedures[procedureCode] = result;
|
||||
for (const [procedureVariant, definitionId] of this.compilingProcedures.entries()) {
|
||||
if (procedureTreeCache[procedureVariant]) {
|
||||
const result = procedureTreeCache[procedureVariant];
|
||||
this.procedures[procedureVariant] = result;
|
||||
this.addProcedureDependencies(result.dependedProcedures);
|
||||
} else {
|
||||
const isWarp = parseIsWarp(procedureVariant);
|
||||
const generator = new ScriptTreeGenerator(this.thread);
|
||||
generator.setProcedureCode(procedureCode);
|
||||
generator.setProcedureVariant(procedureVariant);
|
||||
if (isWarp) generator.enableWarp();
|
||||
const compiledProcedure = this.generateScriptTree(generator, definitionId);
|
||||
this.procedures[procedureCode] = compiledProcedure;
|
||||
this.blocks._cache.compiledProcedures[procedureCode] = compiledProcedure;
|
||||
this.procedures[procedureVariant] = compiledProcedure;
|
||||
procedureTreeCache[procedureVariant] = compiledProcedure;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -927,8 +927,9 @@ class JSGenerator {
|
||||
|
||||
case 'procedures.call': {
|
||||
const procedureCode = node.code;
|
||||
const procedureVariant = node.variant;
|
||||
// Do not generate any code for empty procedures.
|
||||
const procedureData = this.ir.procedures[procedureCode];
|
||||
const procedureData = this.ir.procedures[procedureVariant];
|
||||
if (procedureData.stack === null) {
|
||||
break;
|
||||
}
|
||||
@@ -945,7 +946,7 @@ class JSGenerator {
|
||||
throw new Error('Script uses yielding procedure but is not marked as yielding.');
|
||||
}
|
||||
}
|
||||
this.source += `thread.procedures["${sanitize(procedureCode)}"](`;
|
||||
this.source += `thread.procedures["${sanitize(procedureVariant)}"](`;
|
||||
// Only include arguments if the procedure accepts any.
|
||||
if (procedureData.arguments.length) {
|
||||
const args = [];
|
||||
|
||||
Reference in new issue
Block a user