porffor 0.1.1 → 0.2.0-3fad637

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
 
@@ -91,12 +116,12 @@ export default (wasm, name = '', ind = 0, locals = {}, params = [], returns = []
91
116
  return highlightAsm(out);
92
117
  };
93
118
 
94
- export const highlightAsm = asm =>
95
- asm
96
- .replace(/(local|global|memory)\.[^\s]*/g, _ => `\x1B[31m${_}\x1B[0m`)
97
- .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`)
99
- .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`)
101
- .replace(/ \-?[0-9\.]+/g, _ => ` \x1B[33m${_.slice(1)}\x1B[0m`)
102
- .replace(/ ;;.*$/gm, _ => `\x1B[90m${_.replaceAll(/\x1B\[[0-9]+m/g, '')}\x1B[0m`);
119
+ export const highlightAsm = asm => asm
120
+ .replace(/(local|global|memory)\.[^\s]*/g, _ => `\x1B[31m${_}\x1B[0m`)
121
+ .replace(/(i(8|16|32|64)x[0-9]+|v128)(\.[^\s]*)?/g, _ => `\x1B[34m${_}\x1B[0m`)
122
+ .replace(/[^m](i32|i64|f32|f64|drop)(\.[^\s]*)?/g, _ => `${_[0]}\x1B[36m${_.slice(1)}\x1B[0m`)
123
+ .replace(/(return_call|call|br_if|br|return|rethrow|throw)/g, _ => `\x1B[35m${_}\x1B[0m`)
124
+ .replace(/(block|loop|if|end|else|try|catch_all|catch|delegate)/g, _ => `\x1B[95m${_}\x1B[0m`)
125
+ .replace(/unreachable/g, _ => `\x1B[91m${_}\x1B[0m`)
126
+ .replace(/ \-?[0-9\.]+/g, _ => ` \x1B[33m${_.slice(1)}\x1B[0m`)
127
+ .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
 
@@ -103,115 +105,5 @@ export const read_unsignedLEB128 = _input => {
103
105
  };
104
106
 
105
107
  // ieee 754 binary64
106
-
107
- // from https://github.com/feross/ieee754
108
- // BSD 3-Clause. Copyright 2008 Fair Oaks Labs, Inc. (https://github.com/feross/ieee754/blob/master/LICENSE)
109
- export const ieee754_binary64 = value => {
110
- let isLE = true, mLen = 52, nBytes = 8, offset = 0;
111
- let buffer = new Array(nBytes).fill(0);
112
-
113
- let e, m, c
114
- let eLen = (nBytes * 8) - mLen - 1
115
- const eMax = (1 << eLen) - 1
116
- const eBias = eMax >> 1
117
- const rt = (mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0)
118
- let i = isLE ? 0 : (nBytes - 1)
119
- const d = isLE ? 1 : -1
120
- const s = value < 0 || (value === 0 && 1 / value < 0) ? 1 : 0
121
-
122
- value = Math.abs(value)
123
-
124
- if (isNaN(value) || value === Infinity) {
125
- m = isNaN(value) ? 1 : 0
126
- e = eMax
127
- } else {
128
- e = Math.floor(Math.log(value) / Math.LN2)
129
- if (value * (c = Math.pow(2, -e)) < 1) {
130
- e--
131
- c *= 2
132
- }
133
- if (e + eBias >= 1) {
134
- value += rt / c
135
- } else {
136
- value += rt * Math.pow(2, 1 - eBias)
137
- }
138
- if (value * c >= 2) {
139
- e++
140
- c /= 2
141
- }
142
-
143
- if (e + eBias >= eMax) {
144
- m = 0
145
- e = eMax
146
- } else if (e + eBias >= 1) {
147
- m = ((value * c) - 1) * Math.pow(2, mLen)
148
- e = e + eBias
149
- } else {
150
- m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen)
151
- e = 0
152
- }
153
- }
154
-
155
- while (mLen >= 8) {
156
- buffer[offset + i] = m & 0xff
157
- i += d
158
- m /= 256
159
- mLen -= 8
160
- }
161
-
162
- e = (e << mLen) | m
163
- eLen += mLen
164
- while (eLen > 0) {
165
- buffer[offset + i] = e & 0xff
166
- i += d
167
- e /= 256
168
- eLen -= 8
169
- }
170
-
171
- buffer[offset + i - d] |= s * 128
172
-
173
- return buffer;
174
- };
175
-
176
- export const read_ieee754_binary64 = buffer => {
177
- let isLE = true, mLen = 52, nBytes = 8, offset = 0;
178
-
179
- let e, m
180
- const eLen = (nBytes * 8) - mLen - 1
181
- const eMax = (1 << eLen) - 1
182
- const eBias = eMax >> 1
183
- let nBits = -7
184
- let i = isLE ? (nBytes - 1) : 0
185
- const d = isLE ? -1 : 1
186
- let s = buffer[offset + i]
187
-
188
- i += d
189
-
190
- e = s & ((1 << (-nBits)) - 1)
191
- s >>= (-nBits)
192
- nBits += eLen
193
- while (nBits > 0) {
194
- e = (e * 256) + buffer[offset + i]
195
- i += d
196
- nBits -= 8
197
- }
198
-
199
- m = e & ((1 << (-nBits)) - 1)
200
- e >>= (-nBits)
201
- nBits += mLen
202
- while (nBits > 0) {
203
- m = (m * 256) + buffer[offset + i]
204
- i += d
205
- nBits -= 8
206
- }
207
-
208
- if (e === 0) {
209
- e = 1 - eBias
210
- } else if (e === eMax) {
211
- return m ? NaN : ((s ? -1 : 1) * Infinity)
212
- } else {
213
- m = m + Math.pow(2, mLen)
214
- e = e - eBias
215
- }
216
- return (s ? -1 : 1) * m * Math.pow(2, e - mLen)
217
- };
108
+ export const ieee754_binary64 = value => [...new Uint8Array(new Float64Array([ value ]).buffer)];
109
+ export const read_ieee754_binary64 = buffer => new Float64Array(new Uint8Array(buffer).buffer)[0];
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?.version !== 'undefined' ? (await import('node:fs')).writeFileSync : undefined);
32
+ const execSync = (typeof process?.version !== '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,61 @@ 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
+ out.c = c;
79
+
80
+ if (outFile) {
81
+ writeFileSync(outFile, c);
82
+ } else {
83
+ console.log(c);
84
+ }
85
+
86
+ if (process.version) process.exit();
87
+ }
88
+
89
+ if (target === 'native') {
90
+ const compiler = getArg('compiler') ?? 'clang';
91
+ const cO = getArg('cO') ?? 'Ofast';
92
+
93
+ const tmpfile = 'tmp.c';
94
+ const args = [ compiler, tmpfile, '-o', outFile ?? (process.platform === 'win32' ? 'out.exe' : 'out'), '-' + cO, '-march=native' ];
95
+
96
+ const c = toc(out);
97
+ writeFileSync(tmpfile, c);
98
+
99
+ // obvious command escape is obvious
100
+ execSync(args.join(' '), { stdio: 'inherit' });
101
+
102
+ if (process.version) process.exit();
103
+ }
104
+
105
+ return out;
67
106
  };
@@ -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');