porffor 0.0.0-61de729 → 0.0.0-679c4ea

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.
@@ -42,8 +42,8 @@ export default (wasm, name = '', ind = 0, locals = {}, params = [], returns = []
42
42
  out += ` ${read_ieee754_binary64(inst.slice(1))}`;
43
43
  } else if (inst[0] === Opcodes.i32_const || inst[0] === Opcodes.i64_const) {
44
44
  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))}`
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 || inst[0] === Opcodes.i32_store16 || inst[0] === Opcodes.i32_load16_u) {
46
+ out += ` ${inst[1]} ${read_unsignedLEB128(inst.slice(2))}`;
47
47
  } else for (const operand of inst.slice(1)) {
48
48
  if (inst[0] === Opcodes.if || inst[0] === Opcodes.loop || inst[0] === Opcodes.block) {
49
49
  if (operand === Blocktype.void) continue;
@@ -57,7 +57,7 @@ export default (wasm, name = '', ind = 0, locals = {}, params = [], returns = []
57
57
  out += ` ;; label @${depth}`;
58
58
  }
59
59
 
60
- if (inst[0] === Opcodes.br) {
60
+ if (inst[0] === Opcodes.br || inst[0] === Opcodes.br_if) {
61
61
  out += ` ;; goto @${depth - inst[1]}`;
62
62
  }
63
63
 
@@ -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
 
package/compiler/index.js CHANGED
@@ -4,6 +4,8 @@ import opt from './opt.js';
4
4
  import produceSections from './sections.js';
5
5
  import decompile from './decompile.js';
6
6
  import { BuiltinPreludes } from './builtins.js';
7
+ import toc from './2c.js';
8
+
7
9
 
8
10
  globalThis.decompile = decompile;
9
11
 
@@ -15,7 +17,8 @@ const areaColors = {
15
17
  codegen: [ 20, 80, 250 ],
16
18
  opt: [ 250, 20, 80 ],
17
19
  sections: [ 20, 250, 80 ],
18
- alloc: [ 250, 250, 20 ]
20
+ alloc: [ 250, 250, 20 ],
21
+ '2c': [ 20, 250, 250 ]
19
22
  };
20
23
 
21
24
  globalThis.log = (area, ...args) => console.log(`\u001b[90m[\u001b[0m${rgb(...areaColors[area], area)}\u001b[90m]\u001b[0m`, ...args);
@@ -36,10 +39,16 @@ const logFuncs = (funcs, globals, exceptions) => {
36
39
  console.log();
37
40
  };
38
41
 
42
+ const getArg = name => process.argv.find(x => x.startsWith(`-${name}=`))?.slice(name.length + 2);
43
+
44
+ const writeFileSync = (typeof process !== 'undefined' ? (await import('node:fs')).writeFileSync : undefined);
45
+ const execSync = (typeof process !== 'undefined' ? (await import('node:child_process')).execSync : undefined);
46
+
39
47
  export default (code, flags) => {
40
48
  globalThis.optLog = process.argv.includes('-opt-log');
41
49
  globalThis.codeLog = process.argv.includes('-code-log');
42
50
  globalThis.allocLog = process.argv.includes('-alloc-log');
51
+ globalThis.regexLog = process.argv.includes('-regex-log');
43
52
 
44
53
  for (const x in BuiltinPreludes) {
45
54
  if (code.indexOf(x + '(') !== -1) code = BuiltinPreludes[x] + code;
@@ -50,7 +59,7 @@ export default (code, flags) => {
50
59
  if (flags.includes('info')) console.log(`1. parsed in ${(performance.now() - t0).toFixed(2)}ms`);
51
60
 
52
61
  const t1 = performance.now();
53
- const { funcs, globals, tags, exceptions, pages } = codeGen(program);
62
+ const { funcs, globals, tags, exceptions, pages, data } = codeGen(program);
54
63
  if (flags.includes('info')) console.log(`2. generated code in ${(performance.now() - t1).toFixed(2)}ms`);
55
64
 
56
65
  if (process.argv.includes('-funcs')) logFuncs(funcs, globals, exceptions);
@@ -62,15 +71,48 @@ export default (code, flags) => {
62
71
  if (process.argv.includes('-opt-funcs')) logFuncs(funcs, globals, exceptions);
63
72
 
64
73
  const t3 = performance.now();
65
- const sections = produceSections(funcs, globals, tags, pages, flags);
74
+ const sections = produceSections(funcs, globals, tags, pages, data, flags);
66
75
  if (flags.includes('info')) console.log(`4. produced sections in ${(performance.now() - t3).toFixed(2)}ms`);
67
76
 
68
77
  if (allocLog) {
69
78
  const wasmPages = Math.ceil((pages.size * pageSize) / 65536);
70
79
  const bytes = wasmPages * 65536;
71
80
  log('alloc', `\x1B[1mallocated ${bytes / 1024}KiB\x1B[0m for ${pages.size} things using ${wasmPages} Wasm page${wasmPages === 1 ? '' : 's'}`);
72
- // console.log([...pages.keys()].map(x => `\x1B[36m - ${x}\x1B[0m`).join('\n'));
81
+ console.log([...pages.keys()].map(x => `\x1B[36m - ${x}\x1B[0m`).join('\n') + '\n');
82
+ }
83
+
84
+ const out = { wasm: sections, funcs, globals, tags, exceptions, pages };
85
+
86
+ const target = getArg('target') ?? getArg('t') ?? 'wasm';
87
+ const outFile = getArg('o');
88
+
89
+ if (target === 'c') {
90
+ const c = toc(out);
91
+
92
+ if (outFile) {
93
+ writeFileSync(outFile, c);
94
+ } else {
95
+ console.log(c);
96
+ }
97
+
98
+ process.exit();
99
+ }
100
+
101
+ if (target === 'native') {
102
+ const compiler = getArg('compiler') ?? 'clang';
103
+ const cO = getArg('cO') ?? 'Ofast';
104
+
105
+ const tmpfile = 'tmp.c';
106
+ const args = [ compiler, tmpfile, '-o', outFile ?? (process.platform === 'win32' ? 'out.exe' : 'out'), '-' + cO, '-march=native' ];
107
+
108
+ const c = toc(out);
109
+ writeFileSync(tmpfile, c);
110
+
111
+ // obvious command escape is obvious
112
+ execSync(args.join(' '), { stdio: 'inherit' });
113
+
114
+ process.exit();
73
115
  }
74
116
 
75
- return { wasm: sections, funcs, globals, tags, exceptions, pages };
117
+ return out;
76
118
  };
package/compiler/opt.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { Opcodes, Valtype } from "./wasmSpec.js";
2
2
  import { number } from "./embedding.js";
3
+ import { read_signedLEB128, read_ieee754_binary64 } from "./encoding.js";
3
4
 
4
5
  // deno compat
5
6
  if (typeof process === 'undefined' && typeof Deno !== 'undefined') {
@@ -95,7 +96,6 @@ export default (funcs, globals) => {
95
96
  }
96
97
 
97
98
  if (t.index > c.index) t.index--; // adjust index if after removed func
98
- if (c.memory) t.memory = true;
99
99
  }
100
100
 
101
101
  funcs.splice(funcs.indexOf(c), 1); // remove func from funcs
@@ -142,7 +142,7 @@ export default (funcs, globals) => {
142
142
  depth--;
143
143
  if (depth <= 0) break;
144
144
  }
145
- if (op === Opcodes.br) {
145
+ if (op === Opcodes.br || op === Opcodes.br_if) {
146
146
  hasBranch = true;
147
147
  break;
148
148
  }
@@ -213,9 +213,9 @@ export default (funcs, globals) => {
213
213
  // i32.const 0
214
214
  // drop
215
215
  // -->
216
- // <nothing>>
216
+ // <nothing>
217
217
 
218
- wasm.splice(i - 1, 2); // remove this inst
218
+ wasm.splice(i - 1, 2); // remove these inst
219
219
  i -= 2;
220
220
  continue;
221
221
  }
@@ -259,6 +259,36 @@ export default (funcs, globals) => {
259
259
  continue;
260
260
  }
261
261
 
262
+ if (lastInst[0] === Opcodes.const && (inst === Opcodes.i32_to || inst === Opcodes.i32_to_u)) {
263
+ // change const and immediate i32 convert to i32 const
264
+ // f64.const 0
265
+ // i32.trunc_sat_f64_s || i32.trunc_sat_f64_u
266
+ // -->
267
+ // i32.const 0
268
+
269
+ wasm[i - 1] = number((valtype === 'f64' ? read_ieee754_binary64 : read_signedLEB128)(lastInst.slice(1)), Valtype.i32)[0]; // f64.const -> i32.const
270
+
271
+ wasm.splice(i, 1); // remove this inst
272
+ i--;
273
+ if (optLog) log('opt', `converted const -> i32 convert into i32 const`);
274
+ continue;
275
+ }
276
+
277
+ if (lastInst[0] === Opcodes.i32_const && (inst === Opcodes.i32_from || inst === Opcodes.i32_from_u)) {
278
+ // change i32 const and immediate convert to const (opposite way of previous)
279
+ // i32.const 0
280
+ // f64.convert_i32_s || f64.convert_i32_u
281
+ // -->
282
+ // f64.const 0
283
+
284
+ wasm[i - 1] = number(read_signedLEB128(lastInst.slice(1)))[0]; // i32.const -> f64.const
285
+
286
+ wasm.splice(i, 1); // remove this inst
287
+ i--;
288
+ if (optLog) log('opt', `converted i32 const -> convert into const`);
289
+ continue;
290
+ }
291
+
262
292
  if (tailCall && lastInst[0] === Opcodes.call && inst[0] === Opcodes.return) {
263
293
  // replace call, return with tail calls (return_call)
264
294
  // call X
@@ -287,28 +317,24 @@ export default (funcs, globals) => {
287
317
  continue;
288
318
  }
289
319
 
290
- if (i < 2) continue;
291
- const lastLastInst = wasm[i - 2];
320
+ // remove unneeded before get with update exprs (n++, etc) when value is unused
321
+ if (i < wasm.length - 4 && lastInst[1] === inst[1] && lastInst[0] === Opcodes.local_get && inst[0] === Opcodes.local_get && wasm[i + 1][0] === Opcodes.const && [Opcodes.add, Opcodes.sub].includes(wasm[i + 2][0]) && wasm[i + 3][0] === Opcodes.local_set && wasm[i + 3][1] === inst[1] && (wasm[i + 4][0] === Opcodes.drop || wasm[i + 4][0] === Opcodes.br)) {
322
+ // local.get 1
323
+ // local.get 1
324
+ // -->
325
+ // local.get 1
292
326
 
293
- if (depth.length === 2) {
294
- // hack to remove unneeded before get in for loops with (...; i++)
295
- if (lastLastInst[0] === Opcodes.end && lastInst[1] === inst[1] && lastInst[0] === Opcodes.local_get && inst[0] === Opcodes.local_get) {
296
- // local.get 1
297
- // local.get 1
298
- // -->
299
- // local.get 1
300
-
301
- // remove drop at the end as well
302
- if (wasm[i + 4][0] === Opcodes.drop) {
303
- wasm.splice(i + 4, 1);
304
- }
327
+ // remove drop at the end as well
328
+ if (wasm[i + 4][0] === Opcodes.drop) wasm.splice(i + 4, 1);
305
329
 
306
- wasm.splice(i, 1); // remove this inst (second get)
307
- i--;
308
- continue;
309
- }
330
+ wasm.splice(i, 1); // remove this inst (second get)
331
+ i--;
332
+ continue;
310
333
  }
311
334
 
335
+ if (i < 2) continue;
336
+ const lastLastInst = wasm[i - 2];
337
+
312
338
  if (lastLastInst[1] === inst[1] && inst[0] === Opcodes.local_get && lastInst[0] === Opcodes.local_tee && lastLastInst[0] === Opcodes.local_set) {
313
339
  // local.set x
314
340
  // local.tee y
package/compiler/parse.js CHANGED
@@ -1,3 +1,4 @@
1
+ // import { parse } from 'acorn';
1
2
  const { parse } = (await import(globalThis.document ? 'https://esm.sh/acorn' : 'acorn'));
2
3
 
3
4
  export default (input, flags) => {
@@ -15,13 +15,17 @@ const TYPES = {
15
15
  bigint: 0xffffffffffff7,
16
16
 
17
17
  // these are not "typeof" types but tracked internally
18
- _array: 0xffffffffffff8
18
+ _array: 0xfffffffffff0f,
19
+ _regexp: 0xfffffffffff1f
19
20
  };
20
21
 
21
22
  // todo: turn these into built-ins once arrays and these become less hacky
22
23
 
23
24
  export const PrototypeFuncs = function() {
24
25
  const noUnlikelyChecks = process.argv.includes('-funsafe-no-unlikely-proto-checks');
26
+ let zeroChecks = process.argv.find(x => x.startsWith('-funsafe-zero-proto-checks='));
27
+ if (zeroChecks) zeroChecks = zeroChecks.split('=')[1].split(',').reduce((acc, x) => { acc[x.toLowerCase()] = true; return acc; }, {});
28
+ else zeroChecks = {};
25
29
 
26
30
  this[TYPES._array] = {
27
31
  // lX = local accessor of X ({ get, set }), iX = local index of X, wX = wasm ops of X
@@ -35,7 +39,7 @@ export const PrototypeFuncs = function() {
35
39
  [ Opcodes.i32_lt_s ],
36
40
  [ Opcodes.if, Blocktype.void ],
37
41
  [ Opcodes.local_get, iTmp ],
38
- ...length.cachedI32,
42
+ ...length.getCachedI32(),
39
43
  [ Opcodes.i32_add ],
40
44
  [ Opcodes.local_set, iTmp ],
41
45
  [ Opcodes.end ],
@@ -46,7 +50,7 @@ export const PrototypeFuncs = function() {
46
50
  [ Opcodes.i32_lt_s ],
47
51
 
48
52
  [ Opcodes.local_get, iTmp ],
49
- ...length.cachedI32,
53
+ ...length.getCachedI32(),
50
54
  [ Opcodes.i32_ge_s ],
51
55
  [ Opcodes.i32_or ],
52
56
 
@@ -66,7 +70,7 @@ export const PrototypeFuncs = function() {
66
70
  // todo: only for 1 argument
67
71
  push: (pointer, length, wNewMember) => [
68
72
  // get memory offset of array at last index (length)
69
- ...length.cachedI32,
73
+ ...length.getCachedI32(),
70
74
  ...number(ValtypeSize[valtype], Valtype.i32),
71
75
  [ Opcodes.i32_mul ],
72
76
 
@@ -78,17 +82,17 @@ export const PrototypeFuncs = function() {
78
82
 
79
83
  // bump array length by 1 and return it
80
84
  ...length.setI32([
81
- ...length.cachedI32,
85
+ ...length.getCachedI32(),
82
86
  ...number(1, Valtype.i32),
83
87
  [ Opcodes.i32_add ]
84
88
  ]),
85
89
 
86
- ...length.get
90
+ ...length.get()
87
91
  ],
88
92
 
89
93
  pop: (pointer, length) => [
90
94
  // if length == 0, noop
91
- ...length.cachedI32,
95
+ ...length.getCachedI32(),
92
96
  [ Opcodes.i32_eqz ],
93
97
  [ Opcodes.if, Blocktype.void ],
94
98
  ...number(UNDEFINED),
@@ -99,13 +103,13 @@ export const PrototypeFuncs = function() {
99
103
 
100
104
  // decrement length by 1
101
105
  ...length.setI32([
102
- ...length.cachedI32,
106
+ ...length.getCachedI32(),
103
107
  ...number(1, Valtype.i32),
104
108
  [ Opcodes.i32_sub ]
105
109
  ]),
106
110
 
107
111
  // load last element
108
- ...length.cachedI32,
112
+ ...length.getCachedI32(),
109
113
  ...number(ValtypeSize[valtype], Valtype.i32),
110
114
  [ Opcodes.i32_mul ],
111
115
 
@@ -114,7 +118,7 @@ export const PrototypeFuncs = function() {
114
118
 
115
119
  shift: (pointer, length) => [
116
120
  // if length == 0, noop
117
- ...length.cachedI32,
121
+ ...length.getCachedI32(),
118
122
  Opcodes.i32_eqz,
119
123
  [ Opcodes.if, Blocktype.void ],
120
124
  ...number(UNDEFINED),
@@ -125,7 +129,7 @@ export const PrototypeFuncs = function() {
125
129
 
126
130
  // decrement length by 1
127
131
  ...length.setI32([
128
- ...length.cachedI32,
132
+ ...length.getCachedI32(),
129
133
  ...number(1, Valtype.i32),
130
134
  [ Opcodes.i32_sub ]
131
135
  ]),
@@ -139,11 +143,66 @@ export const PrototypeFuncs = function() {
139
143
  ...number(pointer + ValtypeSize.i32 + ValtypeSize[valtype], Valtype.i32), // src = base array index + length size + an index
140
144
  ...number(pageSize - ValtypeSize.i32 - ValtypeSize[valtype], Valtype.i32), // size = PageSize - length size - an index
141
145
  [ ...Opcodes.memory_copy, 0x00, 0x00 ]
146
+ ],
147
+
148
+ fill: (pointer, length, wElement, iTmp) => [
149
+ ...wElement,
150
+ [ Opcodes.local_set, iTmp ],
151
+
152
+ // use cached length i32 as pointer
153
+ ...length.getCachedI32(),
154
+
155
+ // length - 1 for indexes
156
+ ...number(1, Valtype.i32),
157
+ [ Opcodes.i32_sub ],
158
+
159
+ // * sizeof value
160
+ ...number(ValtypeSize[valtype], Valtype.i32),
161
+ [ Opcodes.i32_mul ],
162
+
163
+ ...length.setCachedI32(),
164
+
165
+ ...(noUnlikelyChecks ? [] : [
166
+ ...length.getCachedI32(),
167
+ ...number(0, Valtype.i32),
168
+ [ Opcodes.i32_lt_s ],
169
+ [ Opcodes.if, Blocktype.void ],
170
+ ...number(pointer),
171
+ [ Opcodes.br, 1 ],
172
+ [ Opcodes.end ]
173
+ ]),
174
+
175
+ [ Opcodes.loop, Blocktype.void ],
176
+
177
+ // set element using pointer
178
+ ...length.getCachedI32(),
179
+ [ Opcodes.local_get, iTmp ],
180
+ [ Opcodes.store, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128(pointer + ValtypeSize.i32) ],
181
+
182
+ // pointer - sizeof value
183
+ ...length.getCachedI32(),
184
+ ...number(ValtypeSize[valtype], Valtype.i32),
185
+ [ Opcodes.i32_sub ],
186
+
187
+ ...length.setCachedI32(),
188
+
189
+ // if pointer >= 0, loop
190
+ ...length.getCachedI32(),
191
+ ...number(0, Valtype.i32),
192
+ [ Opcodes.i32_ge_s ],
193
+ [ Opcodes.br_if, 0 ],
194
+
195
+ [ Opcodes.end ],
196
+
197
+ // return this array
198
+ ...number(pointer)
142
199
  ]
143
200
  };
144
201
 
145
202
  this[TYPES._array].at.local = Valtype.i32;
146
203
  this[TYPES._array].push.noArgRetLength = true;
204
+ this[TYPES._array].fill.local = valtypeBinary;
205
+ this[TYPES._array].fill.returnType = TYPES._array;
147
206
 
148
207
  this[TYPES.string] = {
149
208
  at: (pointer, length, wIndex, iTmp, arrayShell) => {
@@ -165,7 +224,7 @@ export const PrototypeFuncs = function() {
165
224
  [ Opcodes.i32_lt_s ],
166
225
  [ Opcodes.if, Blocktype.void ],
167
226
  [ Opcodes.local_get, iTmp ],
168
- ...length.cachedI32,
227
+ ...length.getCachedI32(),
169
228
  [ Opcodes.i32_add ],
170
229
  [ Opcodes.local_set, iTmp ],
171
230
  [ Opcodes.end ],
@@ -176,7 +235,7 @@ export const PrototypeFuncs = function() {
176
235
  [ Opcodes.i32_lt_s ],
177
236
 
178
237
  [ Opcodes.local_get, iTmp ],
179
- ...length.cachedI32,
238
+ ...length.getCachedI32(),
180
239
  [ Opcodes.i32_ge_s ],
181
240
  [ Opcodes.i32_or ],
182
241
 
@@ -232,27 +291,31 @@ export const PrototypeFuncs = function() {
232
291
  return [
233
292
  ...wIndex,
234
293
  Opcodes.i32_to,
235
- [ Opcodes.local_set, iTmp ],
236
294
 
237
- // index < 0
238
- ...(noUnlikelyChecks ? [] : [
295
+ ...(zeroChecks.charcodeat ? [] : [
296
+ [ Opcodes.local_set, iTmp ],
297
+
298
+ // index < 0
299
+ ...(noUnlikelyChecks ? [] : [
300
+ [ Opcodes.local_get, iTmp ],
301
+ ...number(0, Valtype.i32),
302
+ [ Opcodes.i32_lt_s ],
303
+ ]),
304
+
305
+ // index >= length
239
306
  [ Opcodes.local_get, iTmp ],
240
- ...number(0, Valtype.i32),
241
- [ Opcodes.i32_lt_s ],
242
- ]),
307
+ ...length.getCachedI32(),
308
+ [ Opcodes.i32_ge_s ],
243
309
 
244
- // index >= length
245
- [ Opcodes.local_get, iTmp ],
246
- ...length.cachedI32,
247
- [ Opcodes.i32_ge_s ],
310
+ ...(noUnlikelyChecks ? [] : [ [ Opcodes.i32_or ] ]),
311
+ [ Opcodes.if, Blocktype.void ],
312
+ ...number(NaN),
313
+ [ Opcodes.br, 1 ],
314
+ [ Opcodes.end ],
248
315
 
249
- ...(noUnlikelyChecks ? [] : [ [ Opcodes.i32_or ] ]),
250
- [ Opcodes.if, Blocktype.void ],
251
- ...number(NaN),
252
- [ Opcodes.br, 1 ],
253
- [ Opcodes.end ],
316
+ [ Opcodes.local_get, iTmp ],
317
+ ]),
254
318
 
255
- [ Opcodes.local_get, iTmp ],
256
319
  ...number(ValtypeSize.i16, Valtype.i32),
257
320
  [ Opcodes.i32_mul ],
258
321
 
@@ -1,5 +1,5 @@
1
1
  import { Valtype, FuncType, Empty, ExportDesc, Section, Magic, ModuleVersion, Opcodes, PageSize } from './wasmSpec.js';
2
- import { encodeVector, encodeString, encodeLocal } from './encoding.js';
2
+ import { encodeVector, encodeString, encodeLocal, unsignedLEB128, signedLEB128 } from './encoding.js';
3
3
  import { number } from './embedding.js';
4
4
  import { importedFuncs } from './builtins.js';
5
5
 
@@ -8,11 +8,26 @@ const createSection = (type, data) => [
8
8
  ...encodeVector(data)
9
9
  ];
10
10
 
11
- export default (funcs, globals, tags, pages, flags) => {
11
+ const customSection = (name, data) => [
12
+ Section.custom,
13
+ ...encodeVector([...encodeString(name), ...data])
14
+ ];
15
+
16
+ const chHint = (topTier, baselineTier, strategy) => {
17
+ // 1 byte of 4 2 bit components: spare, top tier, baseline tier, compilation strategy
18
+ // tiers: 0x00 = default, 0x01 = baseline (liftoff), 0x02 = optimized (turbofan)
19
+ // strategy: 0x00 = default, 0x01 = lazy, 0x02 = eager, 0x03 = lazy baseline, eager top tier
20
+ return (strategy | (baselineTier << 2) | (topTier << 4));
21
+ };
22
+
23
+ export default (funcs, globals, tags, pages, data, flags) => {
12
24
  const types = [], typeCache = {};
13
25
 
14
26
  const optLevel = parseInt(process.argv.find(x => x.startsWith('-O'))?.[2] ?? 1);
15
27
 
28
+ const compileHints = process.argv.includes('-compile-hints');
29
+ if (compileHints) log('sections', 'warning: compile hints is V8 only w/ experimental arg! (you used -compile-hints)');
30
+
16
31
  const getType = (params, returns) => {
17
32
  const hash = `${params.join(',')}_${returns.join(',')}`;
18
33
  if (optLog) log('sections', `getType(${JSON.stringify(params)}, ${JSON.stringify(returns)}) -> ${hash} | cache: ${typeCache[hash]}`);
@@ -61,6 +76,7 @@ export default (funcs, globals, tags, pages, flags) => {
61
76
  }
62
77
  }
63
78
  }
79
+ globalThis.importFuncs = importFuncs;
64
80
 
65
81
  if (optLog) log('sections', `treeshake: using ${importFuncs.length}/${importedFuncs.length} imports`);
66
82
 
@@ -74,6 +90,14 @@ export default (funcs, globals, tags, pages, flags) => {
74
90
  encodeVector(funcs.map(x => getType(x.params, x.returns))) // type indexes
75
91
  );
76
92
 
93
+ // compilation hints section - unspec v8 only
94
+ // https://github.com/WebAssembly/design/issues/1473#issuecomment-1431274746
95
+ const chSection = !compileHints ? [] : customSection(
96
+ 'compilationHints',
97
+ // for now just do everything as optimise eager
98
+ encodeVector(funcs.map(_ => chHint(0x02, 0x02, 0x02)))
99
+ );
100
+
77
101
  const globalSection = Object.keys(globals).length === 0 ? [] : createSection(
78
102
  Section.global,
79
103
  encodeVector(Object.keys(globals).map(x => [ globals[x].type, 0x01, ...number(globals[x].init ?? 0, globals[x].type).flat(), Opcodes.end ]))
@@ -131,13 +155,24 @@ export default (funcs, globals, tags, pages, flags) => {
131
155
  encodeVector(types)
132
156
  );
133
157
 
158
+ const dataSection = data.length === 0 ? [] : createSection(
159
+ Section.data,
160
+ encodeVector(data.map(x => [ 0x00, Opcodes.i32_const, ...signedLEB128(x.offset), Opcodes.end, ...encodeVector(x.bytes) ]))
161
+ );
162
+
163
+ const dataCountSection = data.length === 0 ? [] : createSection(
164
+ Section.data_count,
165
+ unsignedLEB128(data.length)
166
+ );
167
+
134
168
  if (process.argv.includes('-sections')) console.log({
135
169
  typeSection: typeSection.map(x => x.toString(16)),
136
170
  importSection: importSection.map(x => x.toString(16)),
137
171
  funcSection: funcSection.map(x => x.toString(16)),
138
172
  globalSection: globalSection.map(x => x.toString(16)),
139
173
  exportSection: exportSection.map(x => x.toString(16)),
140
- codeSection: codeSection.map(x => x.toString(16))
174
+ codeSection: codeSection.map(x => x.toString(16)),
175
+ dataSection: dataSection.map(x => x.toString(16)),
141
176
  });
142
177
 
143
178
  return Uint8Array.from([
@@ -146,10 +181,13 @@ export default (funcs, globals, tags, pages, flags) => {
146
181
  ...typeSection,
147
182
  ...importSection,
148
183
  ...funcSection,
184
+ ...chSection,
149
185
  ...memorySection,
150
186
  ...tagSection,
151
187
  ...globalSection,
152
188
  ...exportSection,
153
- ...codeSection
189
+ ...dataCountSection,
190
+ ...codeSection,
191
+ ...dataSection
154
192
  ]);
155
193
  };
package/compiler/wrap.js CHANGED
@@ -1,10 +1,11 @@
1
1
  import compile from './index.js';
2
2
  import decompile from './decompile.js';
3
- import fs from 'node:fs';
3
+ // import fs from 'node:fs';
4
4
 
5
5
  const bold = x => `\u001b[1m${x}\u001b[0m`;
6
6
 
7
7
  const typeBase = 0xffffffffffff0;
8
+ const internalTypeBase = 0xfffffffffff0f;
8
9
  const TYPES = {
9
10
  [typeBase]: 'number',
10
11
  [typeBase + 1]: 'boolean',
@@ -16,7 +17,8 @@ const TYPES = {
16
17
  [typeBase + 7]: 'bigint',
17
18
 
18
19
  // internal
19
- [typeBase + 8]: '_array'
20
+ [internalTypeBase]: '_array',
21
+ [internalTypeBase + 1]: '_regexp'
20
22
  };
21
23
 
22
24
  export default async (source, flags = [ 'module' ], customImports = {}, print = str => process.stdout.write(str)) => {
@@ -27,16 +29,23 @@ export default async (source, flags = [ 'module' ], customImports = {}, print =
27
29
 
28
30
  if (source.includes('export function')) flags.push('module');
29
31
 
30
- fs.writeFileSync('out.wasm', Buffer.from(wasm));
32
+ // fs.writeFileSync('out.wasm', Buffer.from(wasm));
31
33
 
32
34
  times.push(performance.now() - t1);
33
35
  if (flags.includes('info')) console.log(bold(`compiled in ${times[0].toFixed(2)}ms`));
34
36
 
37
+ const getString = pointer => {
38
+ const length = new Int32Array(memory.buffer, pointer, 1);
39
+
40
+ return Array.from(new Uint16Array(memory.buffer, pointer + 4, length)).map(x => String.fromCharCode(x)).join('');
41
+ };
42
+
35
43
  const t2 = performance.now();
36
44
  const { instance } = await WebAssembly.instantiate(wasm, {
37
45
  '': {
38
46
  p: valtype === 'i64' ? i => print(Number(i).toString()) : i => print(i.toString()),
39
47
  c: valtype === 'i64' ? i => print(String.fromCharCode(Number(i))) : i => print(String.fromCharCode(i)),
48
+ s: valtype === 'i64' ? i => print(getString(Number(i))) : i => print(getString(i)),
40
49
  a: c => { if (!Number(c)) throw new Error(`assert failed`); },
41
50
  t: _ => performance.now(),
42
51
  ...customImports
package/cool.exe ADDED
Binary file
package/g ADDED
Binary file
package/g.exe ADDED
Binary file