Skip to content

Commit 0cce0a0

Browse files
fix: Clone GPU for loop into CPU
Add unit tests to browser all.html fix a typo in file and in desc Add a few missing items to index.d.ts Move debug console.log to match GPU counterpart Build files All unit tests pass or skip (when appropriate) for Chrome, Firefox, Safari, and Node This marks v1 rc1.
1 parent 2204626 commit 0cce0a0

File tree

11 files changed

+414
-447
lines changed

11 files changed

+414
-447
lines changed

bin/gpu-browser-core.js

Lines changed: 76 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
* GPU Accelerated JavaScript
66
*
77
* @version 2.0.0
8-
* @date Sun Feb 10 2019 12:13:28 GMT-0500 (Eastern Standard Time)
8+
* @date Sun Feb 10 2019 22:55:24 GMT-0500 (Eastern Standard Time)
99
*
1010
* @license MIT
1111
* The MIT License
@@ -220,87 +220,68 @@ class CPUFunctionNode extends FunctionNode {
220220

221221
astForStatement(forNode, retArr) {
222222
if (forNode.type !== 'ForStatement') {
223-
throw this.astErrorOutput(
224-
'Invalid for statement',
225-
forNode
226-
);
223+
throw this.astErrorOutput('Invalid for statement', forNode);
227224
}
228225

229-
if (forNode.test && forNode.test.type === 'BinaryExpression') {
230-
if ((forNode.test.right.type === 'Identifier') &&
231-
forNode.test.operator === '<' &&
232-
this.isIdentifierConstant(forNode.test.right.name) === false) {
226+
const initArr = [];
227+
const testArr = [];
228+
const updateArr = [];
229+
const bodyArr = [];
230+
let isSafe = null;
233231

234-
if (!this.loopMaxIterations) {
235-
console.warn('Warning: loopMaxIterations is not set! Using default of 1000 which may result in unintended behavior.');
236-
console.warn('Set loopMaxIterations or use a for loop of fixed length to silence this message.');
232+
if (forNode.init) {
233+
this.pushState('in-for-loop-init');
234+
this.astGeneric(forNode.init, initArr);
235+
for (let i = 0; i < initArr.length; i++) {
236+
if (initArr[i].includes && initArr[i].includes(',')) {
237+
isSafe = false;
237238
}
239+
}
240+
this.popState('in-for-loop-init');
241+
} else {
242+
isSafe = false;
243+
}
238244

239-
retArr.push('for (');
240-
this.astGeneric(forNode.init, retArr);
241-
if (retArr[retArr.length - 1] !== ';') {
242-
retArr.push(';');
243-
}
244-
this.astGeneric(forNode.test.left, retArr);
245-
retArr.push(forNode.test.operator);
246-
retArr.push('LOOP_MAX');
247-
retArr.push(';');
248-
this.astGeneric(forNode.update, retArr);
249-
retArr.push(')');
245+
if (forNode.test) {
246+
this.astGeneric(forNode.test, testArr);
247+
} else {
248+
isSafe = false;
249+
}
250250

251-
retArr.push('{\n');
252-
retArr.push('if (');
253-
this.astGeneric(forNode.test.left, retArr);
254-
retArr.push(forNode.test.operator);
255-
this.astGeneric(forNode.test.right, retArr);
256-
retArr.push(') {\n');
257-
if (forNode.body.type === 'BlockStatement') {
258-
for (let i = 0; i < forNode.body.body.length; i++) {
259-
this.astGeneric(forNode.body.body[i], retArr);
260-
}
261-
} else {
262-
this.astGeneric(forNode.body, retArr);
263-
}
264-
retArr.push('} else {\n');
265-
retArr.push('break;\n');
266-
retArr.push('}\n');
267-
retArr.push('}\n');
251+
if (forNode.update) {
252+
this.astGeneric(forNode.update, updateArr);
253+
} else {
254+
isSafe = false;
255+
}
268256

269-
return retArr;
270-
} else if (forNode.init && forNode.init.declarations) {
271-
const declarations = forNode.init.declarations;
272-
if (!Array.isArray(declarations) || declarations.length < 1) {
273-
throw new Error('Error: Incompatible for loop declaration');
274-
}
257+
if (forNode.body) {
258+
this.pushState('loop-body');
259+
this.astGeneric(forNode.body, bodyArr);
260+
this.popState('loop-body');
261+
}
275262

276-
if (declarations.length > 1) {
277-
retArr.push('for (');
278-
retArr.push(`${forNode.init.kind} `);
279-
for (let i = 0; i < declarations.length; i++) {
280-
if (i > 0) {
281-
retArr.push(',');
282-
}
283-
this.astGeneric(declarations[i], retArr);
284-
}
285-
retArr.push(';');
286-
} else {
287-
retArr.push('for (');
288-
this.astGeneric(forNode.init, retArr);
289-
}
263+
if (isSafe === null) {
264+
isSafe = this.isSafe(forNode.init) && this.isSafe(forNode.test);
265+
}
290266

291-
this.astGeneric(forNode.test, retArr);
292-
retArr.push(';');
293-
this.astGeneric(forNode.update, retArr);
294-
retArr.push(')');
295-
this.astGeneric(forNode.body, retArr);
296-
return retArr;
267+
if (isSafe) {
268+
retArr.push(`for (${initArr.join('')};${testArr.join('')};${updateArr.join('')}){\n`);
269+
retArr.push(bodyArr.join(''));
270+
retArr.push('}\n');
271+
} else {
272+
const iVariableName = this.getInternalVariableName('safeI');
273+
if (initArr.length > 0) {
274+
retArr.push(initArr.join(''), ';\n');
275+
}
276+
retArr.push(`for (int ${iVariableName}=0;${iVariableName}<LOOP_MAX;${iVariableName}++){\n`);
277+
if (testArr.length > 0) {
278+
retArr.push(`if (!${testArr.join('')}) break;\n`);
297279
}
280+
retArr.push(bodyArr.join(''));
281+
retArr.push(`\n${updateArr.join('')};`);
282+
retArr.push('}\n');
298283
}
299-
300-
throw this.astErrorOutput(
301-
'Invalid for statement',
302-
forNode
303-
);
284+
return retArr;
304285
}
305286

306287
astWhileStatement(whileNode, retArr) {
@@ -353,11 +334,15 @@ class CPUFunctionNode extends FunctionNode {
353334
}
354335

355336
astBlockStatement(bNode, retArr) {
356-
retArr.push('{\n');
337+
if (!this.isState('loop-body')) {
338+
retArr.push('{\n');
339+
}
357340
for (let i = 0; i < bNode.body.length; i++) {
358341
this.astGeneric(bNode.body[i], retArr);
359342
}
360-
retArr.push('}\n');
343+
if (!this.isState('loop-body')) {
344+
retArr.push('}\n');
345+
}
361346
return retArr;
362347
}
363348

@@ -382,7 +367,9 @@ class CPUFunctionNode extends FunctionNode {
382367
}
383368
this.astGeneric(varDecNode.declarations[i], retArr);
384369
}
385-
retArr.push(';');
370+
if (!this.isState('in-for-loop-init')) {
371+
retArr.push(';');
372+
}
386373
return retArr;
387374
}
388375

@@ -819,16 +806,16 @@ class CPUKernel extends Kernel {
819806
const kernelString = this.getKernelString();
820807
this.kernelString = kernelString;
821808

809+
if (this.debug) {
810+
console.log('Function output:');
811+
console.log(kernelString);
812+
}
813+
822814
try {
823815
this.run = new Function([], kernelString).bind(this)();
824816
} catch (e) {
825817
console.error('An error occurred compiling the javascript: ', e);
826818
}
827-
828-
if (this.debug) {
829-
console.log('Function output:');
830-
console.log(kernelString);
831-
}
832819
}
833820

834821
color(r, g, b, a) {
@@ -1601,8 +1588,8 @@ class FunctionNode {
16011588
if (ast.operator === '%') {
16021589
return 'Number';
16031590
} else if (ast.operator === '>' || ast.operator === '<') {
1604-
return 'Boolean';
1605-
}
1591+
return 'Boolean';
1592+
}
16061593
const type = this.getType(ast.left);
16071594
return typeLookupMap[type] || type;
16081595
case 'UpdateExpression':
@@ -2331,10 +2318,10 @@ class FunctionNode {
23312318
if (!this._internalVariableNames.hasOwnProperty(name)) {
23322319
this._internalVariableNames[name] = 0;
23332320
}
2334-
this._internalVariableNames[name]++;
2321+
this._internalVariableNames[name]++;
23352322
if (this._internalVariableNames[name] === 1) {
2336-
return name;
2337-
}
2323+
return name;
2324+
}
23382325
return name + this._internalVariableNames[name];
23392326
}
23402327
}
@@ -2355,7 +2342,6 @@ const typeLookupMap = {
23552342
module.exports = {
23562343
FunctionNode
23572344
};
2358-
23592345
},{"../utils":28,"acorn":1}],9:[function(require,module,exports){
23602346
const {
23612347
Kernel
@@ -3335,7 +3321,7 @@ class WebGLFunctionNode extends FunctionNode {
33353321
this.pushState('in-for-loop-init');
33363322
this.astGeneric(forNode.init, initArr);
33373323
for (let i = 0; i < initArr.length; i++) {
3338-
if (initArr[i].includes(',')) {
3324+
if (initArr[i].includes && initArr[i].includes(',')) {
33393325
isSafe = false;
33403326
}
33413327
}
@@ -3393,7 +3379,8 @@ class WebGLFunctionNode extends FunctionNode {
33933379
throw this.astErrorOutput('Invalid while statement', whileNode);
33943380
}
33953381

3396-
retArr.push('for (int safeI=0;safeI<LOOP_MAX;safeI++){\n');
3382+
const iVariableName = this.getInternalVariableName('safeI');
3383+
retArr.push(`for (int ${iVariableName}=0;${iVariableName}<LOOP_MAX;${iVariableName}++){\n`);
33973384
retArr.push('if (!');
33983385
this.astGeneric(whileNode.test, retArr);
33993386
retArr.push(') break;\n');
@@ -3408,7 +3395,8 @@ class WebGLFunctionNode extends FunctionNode {
34083395
throw this.astErrorOutput('Invalid while statement', doWhileNode);
34093396
}
34103397

3411-
retArr.push('for (int safeI=0;safeI<LOOP_MAX;safeI++){\n');
3398+
const iVariableName = this.getInternalVariableName('safeI');
3399+
retArr.push(`for (int ${iVariableName}=0;${iVariableName}<LOOP_MAX;${iVariableName}++){\n`);
34123400
this.astGeneric(doWhileNode.body, retArr);
34133401
retArr.push('if (!');
34143402
this.astGeneric(doWhileNode.test, retArr);
@@ -3797,7 +3785,6 @@ const operatorMap = {
37973785
module.exports = {
37983786
WebGLFunctionNode
37993787
};
3800-
38013788
},{"../function-node":8}],14:[function(require,module,exports){
38023789
const {
38033790
utils

0 commit comments

Comments
 (0)