"use strict" // The function below is called when constructing a cwise function object, and does the following: // A function object is constructed which accepts as argument a compilation function and returns another function. // It is this other function that is eventually returned by createThunk, and this function is the one that actually // checks whether a certain pattern of arguments has already been used before and compiles new loops as needed. // The compilation passed to the first function object is used for compiling new functions. // Once this function object is created, it is called with compile as argument, where the first argument of compile // is bound to "proc" (essentially containing a preprocessed version of the user arguments to cwise). // So createThunk roughly works like this: // function createThunk(proc) { // var thunk = function(compileBound) { // var CACHED = {} // return function(arrays and scalars) { // if (dtype and order of arrays in CACHED) { // var func = CACHED[dtype and order of arrays] // } else { // var func = CACHED[dtype and order of arrays] = compileBound(dtype and order of arrays) // } // return func(arrays and scalars) // } // } // return thunk(compile.bind1(proc)) // } var compile = require("./compile.js") function createThunk(proc) { var code = ["'use strict'", "var CACHED={}"] var vars = [] var thunkName = proc.funcName + "_cwise_thunk" //Build thunk code.push(["return function ", thunkName, "(", proc.shimArgs.join(","), "){"].join("")) var typesig = [] var string_typesig = [] var proc_args = [["array",proc.arrayArgs[0],".shape.slice(", // Slice shape so that we only retain the shape over which we iterate (which gets passed to the cwise operator as SS). Math.max(0,proc.arrayBlockIndices[0]),proc.arrayBlockIndices[0]<0?(","+proc.arrayBlockIndices[0]+")"):")"].join("")] var shapeLengthConditions = [], shapeConditions = [] // Process array arguments for(var i=0; i<proc.arrayArgs.length; ++i) { var j = proc.arrayArgs[i] vars.push(["t", j, "=array", j, ".dtype,", "r", j, "=array", j, ".order"].join("")) typesig.push("t" + j) typesig.push("r" + j) string_typesig.push("t"+j) string_typesig.push("r"+j+".join()") proc_args.push("array" + j + ".data") proc_args.push("array" + j + ".stride") proc_args.push("array" + j + ".offset|0") if (i>0) { // Gather conditions to check for shape equality (ignoring block indices) shapeLengthConditions.push("array" + proc.arrayArgs[0] + ".shape.length===array" + j + ".shape.length+" + (Math.abs(proc.arrayBlockIndices[0])-Math.abs(proc.arrayBlockIndices[i]))) shapeConditions.push("array" + proc.arrayArgs[0] + ".shape[shapeIndex+" + Math.max(0,proc.arrayBlockIndices[0]) + "]===array" + j + ".shape[shapeIndex+" + Math.max(0,proc.arrayBlockIndices[i]) + "]") } } // Check for shape equality if (proc.arrayArgs.length > 1) { code.push("if (!(" + shapeLengthConditions.join(" && ") + ")) throw new Error('cwise: Arrays do not all have the same dimensionality!')") code.push("for(var shapeIndex=array" + proc.arrayArgs[0] + ".shape.length-" + Math.abs(proc.arrayBlockIndices[0]) + "; shapeIndex-->0;) {") code.push("if (!(" + shapeConditions.join(" && ") + ")) throw new Error('cwise: Arrays do not all have the same shape!')") code.push("}") } // Process scalar arguments for(var i=0; i<proc.scalarArgs.length; ++i) { proc_args.push("scalar" + proc.scalarArgs[i]) } // Check for cached function (and if not present, generate it) vars.push(["type=[", string_typesig.join(","), "].join()"].join("")) vars.push("proc=CACHED[type]") code.push("var " + vars.join(",")) code.push(["if(!proc){", "CACHED[type]=proc=compile([", typesig.join(","), "])}", "return proc(", proc_args.join(","), ")}"].join("")) if(proc.debug) { console.log("-----Generated thunk:\n" + code.join("\n") + "\n----------") } //Compile thunk var thunk = new Function("compile", code.join("\n")) return thunk(compile.bind(undefined, proc)) } module.exports = createThunk