porffor 0.16.0-97bb4f33b → 0.16.0-a7bc359af

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CONTRIBUTING.md CHANGED
@@ -26,7 +26,7 @@ You can also swap out `node` in the alias to use another runtime like Deno (`den
26
26
 
27
27
  ### Precompile
28
28
 
29
- **If you update any file inside `compiler/builtins` you will need to do this for it to update inside Porffor otherwise your changes will have no effect.** Run `node compiler/precompile.js` to precompile. It may error during this, if so, you might have an error in your code or there could be a compiler error with Porffor (feel free to ask for help as soon as you encounter any errors with it).
29
+ **If you update any file inside `compiler/builtins` you will need to do this for it to update inside Porffor otherwise your changes will have no effect.** Run `./porf precompile` to precompile. It may error during this, if so, you might have an error in your code or there could be a compiler error with Porffor (feel free to ask for help as soon as you encounter any errors with it).
30
30
 
31
31
  <br>
32
32
 
@@ -2115,7 +2115,6 @@ const brTable = (input, bc, returns) => {
2115
2115
  }
2116
2116
 
2117
2117
  for (let i = 0; i < count; i++) {
2118
- // if (i === 0) out.push([ Opcodes.block, returns, 'br table start' ]);
2119
2118
  if (i === 0) out.push([ Opcodes.block, returns ]);
2120
2119
  else out.push([ Opcodes.block, Blocktype.void ]);
2121
2120
  }
@@ -2149,10 +2148,8 @@ const brTable = (input, bc, returns) => {
2149
2148
  [ Opcodes.br_table, ...encodeVector(table), 0 ]
2150
2149
  );
2151
2150
 
2152
- // if you can guess why we sort the wrong way and then reverse
2153
- // (instead of just sorting the correct way)
2154
- // dm me and if you are correct and the first person
2155
- // I will somehow shout you out or something
2151
+ // sort the wrong way and then reverse
2152
+ // so strings ('default') are at the start before any numbers
2156
2153
  const orderedBc = keys.sort((a, b) => b - a).reverse();
2157
2154
 
2158
2155
  br = count - 1;
@@ -2178,7 +2175,7 @@ const typeSwitch = (scope, type, bc, returns = valtypeBinary) => {
2178
2175
  return bc[known] ?? bc.default;
2179
2176
  }
2180
2177
 
2181
- if (Prefs.typeswitchUseBrtable)
2178
+ if (Prefs.typeswitchBrtable)
2182
2179
  return brTable(type, bc, returns);
2183
2180
 
2184
2181
  const tmp = localTmp(scope, '#typeswitch_tmp' + (Prefs.typeswitchUniqueTmp ? randId() : ''), Valtype.i32);
@@ -3270,7 +3267,7 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
3270
3267
  firstAssign = true;
3271
3268
 
3272
3269
  // todo: can we just have 1 undeclared array? probably not? but this is not really memory efficient
3273
- const uniqueName = name === '$undeclared' ? name + Math.random().toString().slice(2) : name;
3270
+ const uniqueName = name === '$undeclared' ? name + randId() : name;
3274
3271
 
3275
3272
  let page;
3276
3273
  if (Prefs.scopedPageNames) page = allocPage(scope, `${getAllocType(itemType)}: ${scope.name}/${uniqueName}`, itemType);
@@ -3658,7 +3655,7 @@ const generateMember = (scope, decl, _global, _name) => {
3658
3655
  });
3659
3656
  };
3660
3657
 
3661
- const randId = () => Math.random().toString(16).slice(0, -4);
3658
+ const randId = () => Math.random().toString(16).slice(1, -2).padEnd(12, '0');
3662
3659
 
3663
3660
  const objectHack = node => {
3664
3661
  if (!node) return node;
@@ -3710,7 +3707,7 @@ const generateFunc = (scope, decl) => {
3710
3707
  if (decl.async) return todo(scope, 'async functions are not supported');
3711
3708
  if (decl.generator) return todo(scope, 'generator functions are not supported');
3712
3709
 
3713
- const name = decl.id ? decl.id.name : `anonymous_${randId()}`;
3710
+ const name = decl.id ? decl.id.name : `anonymous${randId()}`;
3714
3711
  const params = decl.params ?? [];
3715
3712
 
3716
3713
  // TODO: share scope/locals between !!!
@@ -1,4 +1,4 @@
1
- // cyclone: wasm partial constant evaluator
1
+ // cyclone: wasm partial constant evaluator (it is fast and dangerous hence "cyclone")
2
2
  import { signedLEB128, ieee754_binary64, read_ieee754_binary64, read_signedLEB128 } from './encoding.js';
3
3
  import { Opcodes, Valtype } from './wasmSpec.js';
4
4
 
@@ -246,7 +246,7 @@ export default wasm => {
246
246
  const [ b, a ] = pop2();
247
247
  const v = bool(a === b);
248
248
 
249
- replaceVal(v, Valtype.f64);
249
+ replaceVal(v, Valtype.i32);
250
250
  push(v);
251
251
  break;
252
252
  }
@@ -255,7 +255,7 @@ export default wasm => {
255
255
  const [ b, a ] = pop2();
256
256
  const v = bool(a !== b);
257
257
 
258
- replaceVal(v, Valtype.f64);
258
+ replaceVal(v, Valtype.i32);
259
259
  push(v);
260
260
  break;
261
261
  }
@@ -264,7 +264,7 @@ export default wasm => {
264
264
  const [ b, a ] = pop2();
265
265
  const v = bool(a < b);
266
266
 
267
- replaceVal(v, Valtype.f64);
267
+ replaceVal(v, Valtype.i32);
268
268
  push(v);
269
269
  break;
270
270
  }
@@ -273,7 +273,7 @@ export default wasm => {
273
273
  const [ b, a ] = pop2();
274
274
  const v = bool(a <= b);
275
275
 
276
- replaceVal(v, Valtype.f64);
276
+ replaceVal(v, Valtype.i32);
277
277
  push(v);
278
278
  break;
279
279
  }
@@ -282,7 +282,7 @@ export default wasm => {
282
282
  const [ b, a ] = pop2();
283
283
  const v = bool(a > b);
284
284
 
285
- replaceVal(v, Valtype.f64);
285
+ replaceVal(v, Valtype.i32);
286
286
  push(v);
287
287
  break;
288
288
  }
@@ -291,7 +291,7 @@ export default wasm => {
291
291
  const [ b, a ] = pop2();
292
292
  const v = bool(a >= b);
293
293
 
294
- replaceVal(v, Valtype.f64);
294
+ replaceVal(v, Valtype.i32);
295
295
  push(v);
296
296
  break;
297
297
  }
@@ -404,7 +404,7 @@ export default wasm => {
404
404
  case Opcodes.f64_max: {
405
405
  if (stack.length < 2) { empty(); break; };
406
406
  const [ b, a ] = pop2();
407
- const v = a + b;
407
+ const v = Math.max(a, b);
408
408
 
409
409
  replaceVal(v, Valtype.f64);
410
410
  push(v);
@@ -467,9 +467,9 @@ export default wasm => {
467
467
  // i32.const 1
468
468
  // i32.add
469
469
  // local.set 7 ;; $i (i32)
470
- if (
471
- (opcode >= 0xa0 && opcode <= 0xa3) || // main f64 math op
472
- (opcode >= 0x61 && opcode <= 0x66) // main f64 eq op
470
+ if (i >= 2 &&
471
+ ((opcode >= 0xa0 && opcode <= 0xa3) || // main f64 math op
472
+ (opcode >= 0x61 && opcode <= 0x66)) // main f64 eq op
473
473
  ) {
474
474
  const o2 = wasm[i - 1][0];
475
475
  if (o2 === Opcodes.f64_const) { // f64.const
@@ -500,7 +500,7 @@ export default wasm => {
500
500
  }
501
501
  }
502
502
 
503
- if ((opcode === 0xfc02 || opcode === 0xfc03) && i > 3) { // i32.trunc_sat_f64_s/u
503
+ if ((opcode === 0xfc02 || opcode === 0xfc03) && i >= 3) { // i32.trunc_sat_f64_s/u
504
504
  const o2 = wasm[i - 1][0];
505
505
  if (
506
506
  (o2 >= 0xa0 && o2 <= 0xa3) || // main f64 math op
package/compiler/havoc.js CHANGED
@@ -1,4 +1,4 @@
1
- // havoc: wasm rewrite library
1
+ // havoc: wasm rewrite library (it wreaks havoc upon wasm bytecode hence "havoc")
2
2
  import { Opcodes, Valtype } from './wasmSpec.js';
3
3
 
4
4
  export const localsToConsts = (func, targets, consts, { localKeys }) => {
package/compiler/index.js CHANGED
@@ -27,7 +27,10 @@ const fs = (typeof process?.version !== 'undefined' ? (await import('node:fs'))
27
27
  const execSync = (typeof process?.version !== 'undefined' ? (await import('node:child_process')).execSync : undefined);
28
28
 
29
29
  export default (code, flags) => {
30
- const target = Prefs.target ?? 'wasm';
30
+ let target = Prefs.target ?? 'wasm';
31
+ if (Prefs.native) target = 'native';
32
+
33
+ let outFile = Prefs.o;
31
34
 
32
35
  globalThis.valtype = 'f64';
33
36
  const valtypeOpt = process.argv.find(x => x.startsWith('--valtype='));
@@ -113,8 +116,6 @@ export default (code, flags) => {
113
116
 
114
117
  const out = { wasm, funcs, globals, tags, exceptions, pages, data };
115
118
 
116
- const outFile = Prefs.o;
117
-
118
119
  if (target === 'wasm' && outFile) {
119
120
  fs.writeFileSync(outFile, Buffer.from(wasm));
120
121
 
@@ -135,6 +136,8 @@ export default (code, flags) => {
135
136
  }
136
137
 
137
138
  if (target === 'native') {
139
+ outFile ??= Prefs.native ? './porffor_tmp' : file.split('/').at(-1).split('.').at(0, -1).join('.');
140
+
138
141
  let compiler = Prefs.compiler ?? 'clang';
139
142
  const cO = Prefs._cO ?? 'Ofast';
140
143
 
@@ -153,7 +156,29 @@ export default (code, flags) => {
153
156
 
154
157
  fs.unlinkSync(tmpfile);
155
158
 
156
- if (process.version) process.exit();
159
+ if (process.version) {
160
+ if (Prefs.native) {
161
+ const cleanup = () => {
162
+ try {
163
+ fs.unlinkSync(outFile);
164
+ } catch {}
165
+ };
166
+
167
+ process.on('exit', cleanup);
168
+ process.on('beforeExit', cleanup);
169
+ process.on('SIGINT', () => {
170
+ cleanup();
171
+ process.exit();
172
+ });
173
+
174
+ const runArgs = process.argv.slice(2).filter(x => !x.startsWith('-'));
175
+ try {
176
+ execSync([ outFile, ...runArgs.slice(1) ].join(' '), { stdio: 'inherit' });
177
+ } catch {}
178
+ }
179
+
180
+ process.exit();
181
+ }
157
182
  }
158
183
 
159
184
  return out;
package/compiler/parse.js CHANGED
@@ -43,7 +43,7 @@ export default (input, flags) => {
43
43
  webcompat: true,
44
44
 
45
45
  // babel
46
- plugins: types ? ['estree', 'typescript'] : ['estree'],
46
+ plugins: types || flags.includes('typed') ? ['estree', 'typescript'] : ['estree'],
47
47
 
48
48
  // multiple
49
49
  sourceType: flags.includes('module') ? 'module' : 'script',
package/compiler/pgo.js CHANGED
@@ -11,7 +11,7 @@ export const setup = () => {
11
11
 
12
12
  // enable these prefs by default for pgo
13
13
  Prefs.typeswitchUniqueTmp = Prefs.typeswitchUniqueTmp === false ? false : true;
14
- Prefs.cyclone = Prefs.cyclone === false ? false : true;;
14
+ Prefs.cyclone = Prefs.cyclone === false ? false : true;
15
15
  };
16
16
 
17
17
  export const run = obj => {
@@ -87,9 +87,10 @@ export const run = obj => {
87
87
  localData[activeFunc][i].push(n);
88
88
  },
89
89
  w: (ind, outPtr) => { // readArgv
90
- const args = process.argv.slice(process.argv.indexOf('--pgo')).filter(x => !x.startsWith('-'));
90
+ const pgoInd = process.argv.indexOf('--pgo');
91
+ const args = process.argv.slice(pgoInd).filter(x => !x.startsWith('-'));
91
92
  const str = args[ind - 1];
92
- if (!str) {
93
+ if (pgoInd === -1 || !str) {
93
94
  if (Prefs.pgoLog) console.log('\nPGO warning: script was expecting arguments, please specify args to use for PGO after --pgo arg');
94
95
  return -1;
95
96
  }
@@ -130,16 +131,18 @@ export const run = obj => {
130
131
  func.localValues = localValues;
131
132
 
132
133
  let counts = new Array(10).fill(0);
133
- const consistents = localData[i].map(x => {
134
- if (x.length === 0 || !x.every((y, i) => i < 1 ? true : y === x[i - 1])) return false;
134
+ const consistents = localData[i].map((x, j) => {
135
+ if (j < func.params.length) return false; // param
136
+ if (x.length === 0 || !x.every((y, i) => i < 1 ? true : y === x[i - 1])) return false; // not consistent
135
137
 
136
138
  counts[0]++;
137
139
  return x[0];
138
140
  });
139
141
 
140
142
  const integerOnlyF64s = localData[i].map((x, j) => {
143
+ if (j < func.params.length) return false; // param
141
144
  if (localValues[j].type === Valtype.i32) return false; // already i32
142
- if (x.length === 0 || !x.every(y => Number.isInteger(y))) return false;
145
+ if (x.length === 0 || !x.every(y => Number.isInteger(y))) return false; // not all integer values
143
146
 
144
147
  counts[1]++;
145
148
  return true;
@@ -150,14 +153,14 @@ export const run = obj => {
150
153
 
151
154
  log += ` ${func.name}: identified ${counts[0]}/${total} locals as consistent${Prefs.verbosePgo ? ':' : ''}\n`;
152
155
  if (Prefs.verbosePgo) {
153
- for (let j = 0; j < localData[i].length; j++) {
156
+ for (let j = func.params.length; j < localData[i].length; j++) {
154
157
  log += ` ${consistents[j] !== false ? '\u001b[92m' : '\u001b[91m'}${localKeys[j]}\u001b[0m: ${new Set(localData[i][j]).size} unique values set\n`;
155
158
  }
156
159
  }
157
160
 
158
161
  log += ` ${func.name}: identified ${counts[1]}/${localValues.reduce((acc, x) => acc + (x.type === Valtype.f64 ? 1 : 0), 0)} f64 locals as integer usage only${Prefs.verbosePgo ? ':' : ''}\n`;
159
162
  if (Prefs.verbosePgo) {
160
- for (let j = 0; j < localData[i].length; j++) {
163
+ for (let j = func.params.length; j < localData[i].length; j++) {
161
164
  if (localValues[j].type !== Valtype.f64) continue;
162
165
  log += ` ${integerOnlyF64s[j] ? '\u001b[92m' : '\u001b[91m'}${localKeys[j]}\u001b[0m\n`;
163
166
  }
@@ -177,7 +180,6 @@ export const run = obj => {
177
180
  for (let i = 0; i < x.integerOnlyF64s.length; i++) {
178
181
  const c = x.integerOnlyF64s[i];
179
182
  if (c === false) continue;
180
- if (i < x.params.length) continue;
181
183
 
182
184
  targets.push(i);
183
185
  }
@@ -190,7 +192,6 @@ export const run = obj => {
190
192
  for (let i = 0; i < x.consistents.length; i++) {
191
193
  const c = x.consistents[i];
192
194
  if (c === false) continue;
193
- if (i < x.params.length) continue;
194
195
 
195
196
  targets.push(i);
196
197
 
@@ -26,7 +26,7 @@ const compile = async (file, [ _funcs, _globals ]) => {
26
26
 
27
27
  const porfCompile = (await import(`./index.js?_=${Date.now()}`)).default;
28
28
 
29
- let { funcs, globals, data, exceptions } = porfCompile(source, ['module']);
29
+ let { funcs, globals, data, exceptions } = porfCompile(source, ['module', 'typed']);
30
30
 
31
31
  const allocated = new Set();
32
32
 
@@ -87,6 +87,8 @@ const compile = async (file, [ _funcs, _globals ]) => {
87
87
  };
88
88
 
89
89
  const precompile = async () => {
90
+ if (globalThis._porf_loadParser) await globalThis._porf_loadParser('@babel/parser');
91
+
90
92
  const dir = join(__dirname, 'builtins');
91
93
 
92
94
  let funcs = [], globals = [];
package/compiler/wrap.js CHANGED
@@ -128,7 +128,7 @@ export default (source, flags = [ 'module' ], customImports = {}, print = str =>
128
128
 
129
129
  if (source.includes?.('export ')) flags.push('module');
130
130
 
131
- fs.writeFileSync('out.wasm', Buffer.from(wasm));
131
+ // fs.writeFileSync('out.wasm', Buffer.from(wasm));
132
132
 
133
133
  times.push(performance.now() - t1);
134
134
  if (Prefs.profileCompiler) console.log(bold(`compiled in ${times[0].toFixed(2)}ms`));
package/package.json CHANGED
@@ -1,12 +1,10 @@
1
1
  {
2
2
  "name": "porffor",
3
3
  "description": "a basic experimental wip aot optimizing js -> wasm engine/compiler/runtime in js",
4
- "version": "0.16.0-97bb4f33b",
4
+ "version": "0.16.0-a7bc359af",
5
5
  "author": "CanadaHonk",
6
6
  "license": "MIT",
7
- "scripts": {
8
- "precompile": "node ./compiler/precompile.js"
9
- },
7
+ "scripts": {},
10
8
  "dependencies": {
11
9
  "acorn": "^8.11.3",
12
10
  "node-repl-polyfill": "^0.1.1"
@@ -28,5 +26,5 @@
28
26
  "bugs": {
29
27
  "url": "https://github.com/CanadaHonk/porffor/issues"
30
28
  },
31
- "homepage": "https://porffor.goose.icu"
29
+ "homepage": "https://porffor.dev"
32
30
  }
package/runner/index.js CHANGED
@@ -1,6 +1,4 @@
1
1
  #!/usr/bin/env node
2
-
3
- import compile from '../compiler/wrap.js';
4
2
  import fs from 'node:fs';
5
3
 
6
4
  const start = performance.now();
@@ -59,10 +57,15 @@ if (process.argv.includes('--help')) {
59
57
  }
60
58
 
61
59
  let file = process.argv.slice(2).find(x => x[0] !== '-');
62
- if (['run', 'wasm', 'native', 'c', 'profile', 'debug', 'debug-wasm'].includes(file)) {
60
+ if (['precompile', 'run', 'wasm', 'native', 'c', 'profile', 'debug', 'debug-wasm'].includes(file)) {
63
61
  // remove this arg
64
62
  process.argv.splice(process.argv.indexOf(file), 1);
65
63
 
64
+ if (file === 'precompile') {
65
+ await import('../compiler/precompile.js');
66
+ await new Promise(() => {}); // do nothing for the rest of this file
67
+ }
68
+
66
69
  if (file === 'profile') {
67
70
  await import('./profile.js');
68
71
  await new Promise(() => {}); // do nothing for the rest of this file
@@ -89,6 +92,8 @@ if (['run', 'wasm', 'native', 'c', 'profile', 'debug', 'debug-wasm'].includes(fi
89
92
  }
90
93
  }
91
94
 
95
+ globalThis.file = file;
96
+
92
97
  if (!file) {
93
98
  if (process.argv.includes('-v') || process.argv.includes('--version')) {
94
99
  // just print version
@@ -103,6 +108,8 @@ if (!file) {
103
108
 
104
109
  const source = fs.readFileSync(file, 'utf8');
105
110
 
111
+ const compile = (await import('../compiler/wrap.js')).default;
112
+
106
113
  let cache = '';
107
114
  const print = str => {
108
115
  /* cache += str;