porffor 0.1.1 → 0.2.0-c6c8c81

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.
@@ -17,8 +17,10 @@ export default (wasm, name = '', ind = 0, locals = {}, params = [], returns = []
17
17
  const justLocals = Object.values(locals).sort((a, b) => a.idx - b.idx).slice(params.length);
18
18
  if (justLocals.length > 0) out += ` local ${justLocals.map(x => invValtype[x.type]).join(' ')}\n`;
19
19
 
20
- let i = 0, lastInst;
20
+ let i = -1, lastInst;
21
+ let byte = 0;
21
22
  for (let inst of wasm.concat(name ? [ [ Opcodes.end ] ] : [])) {
23
+ i++;
22
24
  if (inst[0] === null) continue;
23
25
 
24
26
  if (inst[0] === 0xfd) { // simd inst prefix
@@ -32,9 +34,30 @@ export default (wasm, name = '', ind = 0, locals = {}, params = [], returns = []
32
34
 
33
35
  if (inst[0] === Opcodes.end || inst[0] === Opcodes.else || inst[0] === Opcodes.catch_all) depth--;
34
36
 
35
- const opStr = invOpcodes[inst[0]];
36
- if (!opStr) console.log(`decomp: unknown op ${inst[0].toString(16)}`)
37
- out += /* ' '.repeat(3 - i.toString().length) + i + ' ' + */ ' '.repeat(Math.max(0, depth * 2)) + opStr.replace('_', '.').replace('return.', 'return_').replace('call.', 'call_').replace('br.', 'br_');
37
+ out += ' '.repeat(Math.max(0, depth * 2));
38
+
39
+ let opStr = invOpcodes[inst[0]];
40
+ if (!opStr) {
41
+ console.log(`decomp: unknown op ${inst[0]?.toString?.(16)} @${i}`);
42
+ // console.log(`prior: ${invOpcodes[wasm[i - 1][0]]}`);
43
+ out += `;; unknown op ${inst[0]?.toString?.(16)}\n`;
44
+ continue;
45
+ }
46
+
47
+ // out += '0x' + byte.toString(10).padStart(2, '0');
48
+ byte += inst.length;
49
+
50
+ out += opStr.replace('_', '.').replace('return.', 'return_').replace('call.', 'call_').replace('br.', 'br_').replace('catch.', 'catch_');
51
+
52
+ const comments = [];
53
+ inst = inst.filter(x => {
54
+ if (typeof x === 'string') {
55
+ comments.push(x);
56
+ return false;
57
+ }
58
+
59
+ return true;
60
+ })
38
61
 
39
62
  if (inst[0] === Opcodes.if || inst[0] === Opcodes.loop || inst[0] === Opcodes.block || inst[0] === Opcodes.else || inst[0] === Opcodes.try || inst[0] === Opcodes.catch_all) depth++;
40
63
 
@@ -42,8 +65,8 @@ export default (wasm, name = '', ind = 0, locals = {}, params = [], returns = []
42
65
  out += ` ${read_ieee754_binary64(inst.slice(1))}`;
43
66
  } else if (inst[0] === Opcodes.i32_const || inst[0] === Opcodes.i64_const) {
44
67
  out += ` ${read_signedLEB128(inst.slice(1))}`;
45
- } else if (inst[0] === Opcodes.i32_load || inst[0] === Opcodes.i64_load || inst[0] === Opcodes.f64_load || inst[0] === Opcodes.i32_store || inst[0] === Opcodes.i64_store || inst[0] === Opcodes.f64_store) {
46
- out += ` ${inst[1]} ${read_unsignedLEB128(inst.slice(2))}`
68
+ } else if (inst[0] === Opcodes.i32_load || inst[0] === Opcodes.i64_load || inst[0] === Opcodes.f64_load || inst[0] === Opcodes.i32_store || inst[0] === Opcodes.i64_store || inst[0] === Opcodes.f64_store || inst[0] === Opcodes.i32_store16 || inst[0] === Opcodes.i32_load16_u) {
69
+ out += ` ${inst[1]} ${read_unsignedLEB128(inst.slice(2))}`;
47
70
  } else for (const operand of inst.slice(1)) {
48
71
  if (inst[0] === Opcodes.if || inst[0] === Opcodes.loop || inst[0] === Opcodes.block) {
49
72
  if (operand === Blocktype.void) continue;
@@ -53,11 +76,13 @@ export default (wasm, name = '', ind = 0, locals = {}, params = [], returns = []
53
76
  }
54
77
  }
55
78
 
79
+ if (comments.length > 0) out += ` ;; ${comments.join(' ')}`;
80
+
56
81
  if (inst[0] === Opcodes.if || inst[0] === Opcodes.loop || inst[0] === Opcodes.block || inst[0] === Opcodes.else) {
57
82
  out += ` ;; label @${depth}`;
58
83
  }
59
84
 
60
- if (inst[0] === Opcodes.br) {
85
+ if (inst[0] === Opcodes.br || inst[0] === Opcodes.br_if) {
61
86
  out += ` ;; goto @${depth - inst[1]}`;
62
87
  }
63
88
 
@@ -95,8 +120,9 @@ export const highlightAsm = asm =>
95
120
  asm
96
121
  .replace(/(local|global|memory)\.[^\s]*/g, _ => `\x1B[31m${_}\x1B[0m`)
97
122
  .replace(/(i(8|16|32|64)x[0-9]+|v128)(\.[^\s]*)?/g, _ => `\x1B[34m${_}\x1B[0m`)
98
- .replace(/[^m](i32|i64|f32|f64)(\.[^\s]*)?/g, _ => `${_[0]}\x1B[36m${_.slice(1)}\x1B[0m`)
123
+ .replace(/[^m](i32|i64|f32|f64|drop)(\.[^\s]*)?/g, _ => `${_[0]}\x1B[36m${_.slice(1)}\x1B[0m`)
99
124
  .replace(/(return_call|call|br_if|br|return|throw|rethrow)/g, _ => `\x1B[35m${_}\x1B[0m`)
100
- .replace(/(block|loop|if|end|else|try|catch|catch_all|delegate)/g, _ => `\x1B[95m${_}\x1B[0m`)
125
+ .replace(/(block|loop|if|end|else|try|catch_all|catch|delegate)/g, _ => `\x1B[95m${_}\x1B[0m`)
126
+ .replace(/unreachable/g, _ => `\x1B[91m${_}\x1B[0m`)
101
127
  .replace(/ \-?[0-9\.]+/g, _ => ` \x1B[33m${_.slice(1)}\x1B[0m`)
102
128
  .replace(/ ;;.*$/gm, _ => `\x1B[90m${_.replaceAll(/\x1B\[[0-9]+m/g, '')}\x1B[0m`);
@@ -9,11 +9,15 @@ export const number = (n, valtype = valtypeBinary) => {
9
9
  }
10
10
  };
11
11
 
12
- const enforceTwoBytes = arr => [ arr[0] ?? 0, arr[1] ?? 0, arr[2] ?? 0, arr[3] ?? 0 ];
12
+ export const enforceOneByte = arr => [ arr[0] ?? 0 ];
13
+ export const enforceTwoBytes = arr => [ arr[0] ?? 0, arr[1] ?? 0 ];
14
+ export const enforceFourBytes = arr => [ arr[0] ?? 0, arr[1] ?? 0, arr[2] ?? 0, arr[3] ?? 0 ];
15
+ export const enforceEightBytes = arr => [ arr[0] ?? 0, arr[1] ?? 0, arr[2] ?? 0, arr[3] ?? 0, arr[4] ?? 0, arr[5] ?? 0, arr[6] ?? 0, arr[7] ?? 0 ];
16
+
13
17
  export const i32x4 = (a, b, c, d) => [ [
14
18
  ...Opcodes.v128_const,
15
- ...enforceTwoBytes(signedLEB128(a)),
16
- ...enforceTwoBytes(signedLEB128(b)),
17
- ...enforceTwoBytes(signedLEB128(c)),
18
- ...enforceTwoBytes(signedLEB128(d))
19
+ ...enforceFourBytes(signedLEB128(a)),
20
+ ...enforceFourBytes(signedLEB128(b)),
21
+ ...enforceFourBytes(signedLEB128(c)),
22
+ ...enforceFourBytes(signedLEB128(d))
19
23
  ] ];
@@ -22,15 +22,15 @@ export const encodeLocal = (count, type) => [
22
22
  type
23
23
  ];
24
24
 
25
+ // todo: this only works with integers within 32 bit range
25
26
  export const signedLEB128 = n => {
26
- // todo: this only works with integers within 32 bit range
27
+ n |= 0;
27
28
 
28
29
  // just input for small numbers (for perf as common)
29
30
  if (n >= 0 && n <= 63) return [ n ];
30
31
  if (n >= -64 && n <= 0) return [ 128 + n ];
31
32
 
32
33
  const buffer = [];
33
- n |= 0;
34
34
 
35
35
  while (true) {
36
36
  let byte = n & 0x7f;
@@ -50,6 +50,8 @@ export const signedLEB128 = n => {
50
50
  };
51
51
 
52
52
  export const unsignedLEB128 = n => {
53
+ n |= 0;
54
+
53
55
  // just input for small numbers (for perf as common)
54
56
  if (n >= 0 && n <= 127) return [ n ];
55
57
 
@@ -107,6 +109,8 @@ export const read_unsignedLEB128 = _input => {
107
109
  // from https://github.com/feross/ieee754
108
110
  // BSD 3-Clause. Copyright 2008 Fair Oaks Labs, Inc. (https://github.com/feross/ieee754/blob/master/LICENSE)
109
111
  export const ieee754_binary64 = value => {
112
+ return [...new Uint8Array(new Float64Array([ value ]).buffer)];
113
+
110
114
  let isLE = true, mLen = 52, nBytes = 8, offset = 0;
111
115
  let buffer = new Array(nBytes).fill(0);
112
116
 
@@ -174,6 +178,8 @@ export const ieee754_binary64 = value => {
174
178
  };
175
179
 
176
180
  export const read_ieee754_binary64 = buffer => {
181
+ return new Float64Array(new Uint8Array(buffer).buffer)[0];
182
+
177
183
  let isLE = true, mLen = 52, nBytes = 8, offset = 0;
178
184
 
179
185
  let e, m
package/compiler/index.js CHANGED
@@ -1,29 +1,20 @@
1
+ import { underline, bold, log } from './log.js';
1
2
  import parse from './parse.js';
2
3
  import codeGen from './codeGen.js';
3
4
  import opt from './opt.js';
4
5
  import produceSections from './sections.js';
5
6
  import decompile from './decompile.js';
6
7
  import { BuiltinPreludes } from './builtins.js';
8
+ import toc from './2c.js';
7
9
 
8
10
  globalThis.decompile = decompile;
9
11
 
10
- const rgb = (r, g, b, x) => `\x1b[38;2;${r};${g};${b}m${x}\u001b[0m`;
11
- const underline = x => `\u001b[4m${x}\u001b[0m`;
12
- const bold = x => `\u001b[1m${x}\u001b[0m`;
13
-
14
- const areaColors = {
15
- codegen: [ 20, 80, 250 ],
16
- opt: [ 250, 20, 80 ],
17
- sections: [ 20, 250, 80 ]
18
- };
19
-
20
- globalThis.log = (area, ...args) => console.log(`\u001b[90m[\u001b[0m${rgb(...areaColors[area], area)}\u001b[90m]\u001b[0m`, ...args);
21
-
22
12
  const logFuncs = (funcs, globals, exceptions) => {
23
13
  console.log('\n' + underline(bold('funcs')));
24
14
 
15
+ const startIndex = funcs.sort((a, b) => a.index - b.index)[0].index;
25
16
  for (const f of funcs) {
26
- console.log(`${underline(f.name)} (${f.index})`);
17
+ console.log(`${underline(f.name)} (${f.index - startIndex})`);
27
18
 
28
19
  console.log(`params: ${f.params.map((_, i) => Object.keys(f.locals)[Object.values(f.locals).indexOf(Object.values(f.locals).find(x => x.idx === i))]).join(', ')}`);
29
20
  console.log(`returns: ${f.returns.length > 0 ? true : false}`);
@@ -35,9 +26,16 @@ const logFuncs = (funcs, globals, exceptions) => {
35
26
  console.log();
36
27
  };
37
28
 
29
+ const getArg = name => process.argv.find(x => x.startsWith(`-${name}=`))?.slice(name.length + 2);
30
+
31
+ const writeFileSync = (typeof process !== 'undefined' ? (await import('node:fs')).writeFileSync : undefined);
32
+ const execSync = (typeof process !== 'undefined' ? (await import('node:child_process')).execSync : undefined);
33
+
38
34
  export default (code, flags) => {
39
35
  globalThis.optLog = process.argv.includes('-opt-log');
40
36
  globalThis.codeLog = process.argv.includes('-code-log');
37
+ globalThis.allocLog = process.argv.includes('-alloc-log');
38
+ globalThis.regexLog = process.argv.includes('-regex-log');
41
39
 
42
40
  for (const x in BuiltinPreludes) {
43
41
  if (code.indexOf(x + '(') !== -1) code = BuiltinPreludes[x] + code;
@@ -48,20 +46,60 @@ export default (code, flags) => {
48
46
  if (flags.includes('info')) console.log(`1. parsed in ${(performance.now() - t0).toFixed(2)}ms`);
49
47
 
50
48
  const t1 = performance.now();
51
- const { funcs, globals, tags, exceptions, pages } = codeGen(program);
49
+ const { funcs, globals, tags, exceptions, pages, data } = codeGen(program);
52
50
  if (flags.includes('info')) console.log(`2. generated code in ${(performance.now() - t1).toFixed(2)}ms`);
53
51
 
54
52
  if (process.argv.includes('-funcs')) logFuncs(funcs, globals, exceptions);
55
53
 
56
54
  const t2 = performance.now();
57
- opt(funcs, globals);
55
+ opt(funcs, globals, pages);
58
56
  if (flags.includes('info')) console.log(`3. optimized code in ${(performance.now() - t2).toFixed(2)}ms`);
59
57
 
60
58
  if (process.argv.includes('-opt-funcs')) logFuncs(funcs, globals, exceptions);
61
59
 
62
60
  const t3 = performance.now();
63
- const sections = produceSections(funcs, globals, tags, pages, flags);
61
+ const sections = produceSections(funcs, globals, tags, pages, data, flags);
64
62
  if (flags.includes('info')) console.log(`4. produced sections in ${(performance.now() - t3).toFixed(2)}ms`);
65
63
 
66
- return { wasm: sections, funcs, globals, tags, exceptions, pages };
64
+ if (allocLog) {
65
+ const wasmPages = Math.ceil((pages.size * pageSize) / 65536);
66
+ const bytes = wasmPages * 65536;
67
+ log('alloc', `\x1B[1mallocated ${bytes / 1024}KiB\x1B[0m for ${pages.size} things using ${wasmPages} Wasm page${wasmPages === 1 ? '' : 's'}`);
68
+ console.log([...pages.keys()].map(x => `\x1B[36m - ${x}\x1B[0m`).join('\n') + '\n');
69
+ }
70
+
71
+ const out = { wasm: sections, funcs, globals, tags, exceptions, pages };
72
+
73
+ const target = getArg('target') ?? getArg('t') ?? 'wasm';
74
+ const outFile = getArg('o');
75
+
76
+ if (target === 'c') {
77
+ const c = toc(out);
78
+
79
+ if (outFile) {
80
+ writeFileSync(outFile, c);
81
+ } else {
82
+ console.log(c);
83
+ }
84
+
85
+ process.exit();
86
+ }
87
+
88
+ if (target === 'native') {
89
+ const compiler = getArg('compiler') ?? 'clang';
90
+ const cO = getArg('cO') ?? 'Ofast';
91
+
92
+ const tmpfile = 'tmp.c';
93
+ const args = [ compiler, tmpfile, '-o', outFile ?? (process.platform === 'win32' ? 'out.exe' : 'out'), '-' + cO, '-march=native' ];
94
+
95
+ const c = toc(out);
96
+ writeFileSync(tmpfile, c);
97
+
98
+ // obvious command escape is obvious
99
+ execSync(args.join(' '), { stdio: 'inherit' });
100
+
101
+ process.exit();
102
+ }
103
+
104
+ return out;
67
105
  };
@@ -0,0 +1,15 @@
1
+ export const rgb = (r, g, b, x) => `\x1b[38;2;${r};${g};${b}m${x}\u001b[0m`;
2
+ export const underline = x => `\u001b[4m${x}\u001b[0m`;
3
+ export const bold = x => `\u001b[1m${x}\u001b[0m`;
4
+
5
+ const areaColors = {
6
+ codegen: [ 20, 80, 250 ],
7
+ opt: [ 250, 20, 80 ],
8
+ sections: [ 20, 250, 80 ],
9
+ alloc: [ 250, 250, 20 ],
10
+ parser: [ 240, 240, 240 ],
11
+ '2c': [ 20, 250, 250 ]
12
+ };
13
+
14
+ export const log = (area, ...args) => console.log(`\u001b[90m[\u001b[0m${rgb(...areaColors[area], area)}\u001b[90m]\u001b[0m`, ...args);
15
+ log.warning = (area, ...args) => log(area, '\u001b[93m' + args[0], ...args.slice(1), '\u001b[0m');