porffor 0.0.0-758fed5 → 0.0.0-7d5ae9c

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.
@@ -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
  ] ];
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
@@ -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
  }
@@ -317,28 +317,24 @@ export default (funcs, globals) => {
317
317
  continue;
318
318
  }
319
319
 
320
- if (i < 2) continue;
321
- 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
322
326
 
323
- if (depth.length === 2) {
324
- // hack to remove unneeded before get in for loops with (...; i++)
325
- if (lastLastInst[0] === Opcodes.end && lastInst[1] === inst[1] && lastInst[0] === Opcodes.local_get && inst[0] === Opcodes.local_get) {
326
- // local.get 1
327
- // local.get 1
328
- // -->
329
- // local.get 1
330
-
331
- // remove drop at the end as well
332
- if (wasm[i + 4][0] === Opcodes.drop) {
333
- wasm.splice(i + 4, 1);
334
- }
327
+ // remove drop at the end as well
328
+ if (wasm[i + 4][0] === Opcodes.drop) wasm.splice(i + 4, 1);
335
329
 
336
- wasm.splice(i, 1); // remove this inst (second get)
337
- i--;
338
- continue;
339
- }
330
+ wasm.splice(i, 1); // remove this inst (second get)
331
+ i--;
332
+ continue;
340
333
  }
341
334
 
335
+ if (i < 2) continue;
336
+ const lastLastInst = wasm[i - 2];
337
+
342
338
  if (lastLastInst[1] === inst[1] && inst[0] === Opcodes.local_get && lastInst[0] === Opcodes.local_tee && lastLastInst[0] === Opcodes.local_set) {
343
339
  // local.set x
344
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,14 +143,69 @@ 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
- at: (pointer, length, wIndex, iTmp, arrayShell) => {
208
+ at: (pointer, length, wIndex, iTmp, _, arrayShell) => {
150
209
  const [ newOut, newPointer ] = arrayShell(1, 'i16');
151
210
 
152
211
  return [
@@ -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
 
@@ -201,7 +260,7 @@ export const PrototypeFuncs = function() {
201
260
  },
202
261
 
203
262
  // todo: out of bounds properly
204
- charAt: (pointer, length, wIndex, _, arrayShell) => {
263
+ charAt: (pointer, length, wIndex, _1, _2, arrayShell) => {
205
264
  const [ newOut, newPointer ] = arrayShell(1, 'i16');
206
265
 
207
266
  return [
@@ -232,39 +291,120 @@ 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
306
+ [ Opcodes.local_get, iTmp ],
307
+ ...length.getCachedI32(),
308
+ [ Opcodes.i32_ge_s ],
309
+
310
+ ...(noUnlikelyChecks ? [] : [ [ Opcodes.i32_or ] ]),
311
+ [ Opcodes.if, Blocktype.void ],
312
+ ...number(NaN),
313
+ [ Opcodes.br, 1 ],
314
+ [ Opcodes.end ],
315
+
239
316
  [ Opcodes.local_get, iTmp ],
240
- ...number(0, Valtype.i32),
241
- [ Opcodes.i32_lt_s ],
242
317
  ]),
243
318
 
244
- // index >= length
319
+ ...number(ValtypeSize.i16, Valtype.i32),
320
+ [ Opcodes.i32_mul ],
321
+
322
+ // load current string ind {arg}
323
+ [ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128(pointer + ValtypeSize.i32) ],
324
+ Opcodes.i32_from_u
325
+ ];
326
+ },
327
+
328
+ isWellFormed: (pointer, length, wIndex, iTmp, iTmp2, arrayShell, { wellFormed } = {}) => {
329
+ // aot approx metadata
330
+ if (wellFormed != null) return number(wellFormed ? 1 : 0);
331
+
332
+ return [
333
+ // note: we cannot presume it begins as 0 in case it was used previously
334
+ ...number(0, Valtype.i32),
335
+ [ Opcodes.local_set, iTmp ],
336
+
337
+ [ Opcodes.loop, Blocktype.void ],
338
+
339
+ [ Opcodes.block, Blocktype.void ],
340
+
245
341
  [ Opcodes.local_get, iTmp ],
246
- ...length.cachedI32,
342
+ [ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128(pointer + ValtypeSize.i32) ],
343
+ [ Opcodes.local_set, iTmp2 ],
344
+
345
+ // if not surrogate, continue
346
+ [ Opcodes.local_get, iTmp2 ],
347
+ ...number(0xF800, Valtype.i32),
348
+ [ Opcodes.i32_and ],
349
+ ...number(0xD800, Valtype.i32),
350
+ [ Opcodes.i32_ne ],
351
+ [ Opcodes.br_if, 0 ],
352
+
353
+ // if not leading surrogate, return false
354
+ [ Opcodes.local_get, iTmp2 ],
355
+ ...number(0xDC00, Valtype.i32),
247
356
  [ Opcodes.i32_ge_s ],
357
+ [ Opcodes.if, Blocktype.void ],
358
+ ...number(0),
359
+ [ Opcodes.br, 3 ],
360
+ [ Opcodes.end ],
248
361
 
249
- ...(noUnlikelyChecks ? [] : [ [ Opcodes.i32_or ] ]),
362
+ // if not followed by trailing surrogate, return false
363
+ [ Opcodes.local_get, iTmp ],
364
+ [ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128(pointer + ValtypeSize.i32 + ValtypeSize.i16) ],
365
+ ...number(0xFC00, Valtype.i32),
366
+ [ Opcodes.i32_and ],
367
+ ...number(0xDC00, Valtype.i32),
368
+ [ Opcodes.i32_ne ],
250
369
  [ Opcodes.if, Blocktype.void ],
251
- ...number(NaN),
252
- [ Opcodes.br, 1 ],
370
+ ...number(0),
371
+ [ Opcodes.br, 3 ],
253
372
  [ Opcodes.end ],
254
373
 
374
+ // bump index again since gone through two valid chars
255
375
  [ Opcodes.local_get, iTmp ],
256
376
  ...number(ValtypeSize.i16, Valtype.i32),
377
+ [ Opcodes.i32_add ],
378
+ [ Opcodes.local_set, iTmp ],
379
+
380
+ [ Opcodes.end ],
381
+
382
+ // bump pointer and loop if not at the end
383
+ [ Opcodes.local_get, iTmp ],
384
+ ...number(ValtypeSize.i16, Valtype.i32),
385
+ [ Opcodes.i32_add ],
386
+ [ Opcodes.local_tee, iTmp ],
387
+
388
+ ...length.getCachedI32(),
389
+ ...number(ValtypeSize.i16, Valtype.i32),
257
390
  [ Opcodes.i32_mul ],
391
+ [ Opcodes.i32_ne ],
392
+ [ Opcodes.br_if, 0 ],
258
393
 
259
- // load current string ind {arg}
260
- [ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128(pointer + ValtypeSize.i32) ],
261
- Opcodes.i32_from_u
262
- ];
263
- },
394
+ [ Opcodes.end ],
395
+
396
+ // return true
397
+ ...number(1)
398
+ ]
399
+ }
264
400
  };
265
401
 
266
402
  this[TYPES.string].at.local = Valtype.i32;
267
403
  this[TYPES.string].at.returnType = TYPES.string;
268
404
  this[TYPES.string].charAt.returnType = TYPES.string;
269
405
  this[TYPES.string].charCodeAt.local = Valtype.i32;
406
+
407
+ this[TYPES.string].isWellFormed.local = Valtype.i32;
408
+ this[TYPES.string].isWellFormed.local2 = Valtype.i32;
409
+ this[TYPES.string].isWellFormed.returnType = TYPES.boolean;
270
410
  };
@@ -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
 
@@ -20,7 +20,7 @@ const chHint = (topTier, baselineTier, strategy) => {
20
20
  return (strategy | (baselineTier << 2) | (topTier << 4));
21
21
  };
22
22
 
23
- export default (funcs, globals, tags, pages, flags) => {
23
+ export default (funcs, globals, tags, pages, data, flags) => {
24
24
  const types = [], typeCache = {};
25
25
 
26
26
  const optLevel = parseInt(process.argv.find(x => x.startsWith('-O'))?.[2] ?? 1);
@@ -76,6 +76,7 @@ export default (funcs, globals, tags, pages, flags) => {
76
76
  }
77
77
  }
78
78
  }
79
+ globalThis.importFuncs = importFuncs;
79
80
 
80
81
  if (optLog) log('sections', `treeshake: using ${importFuncs.length}/${importedFuncs.length} imports`);
81
82
 
@@ -104,6 +105,8 @@ export default (funcs, globals, tags, pages, flags) => {
104
105
 
105
106
  const exports = funcs.filter(x => x.export).map((x, i) => [ ...encodeString(x.name === 'main' ? 'm' : x.name), ExportDesc.func, x.index ]);
106
107
 
108
+ if (process.argv.includes('-always-memory') && pages.size === 0) pages.set('-always-memory', 0);
109
+
107
110
  const usesMemory = pages.size > 0;
108
111
  const memorySection = !usesMemory ? [] : createSection(
109
112
  Section.memory,
@@ -154,13 +157,24 @@ export default (funcs, globals, tags, pages, flags) => {
154
157
  encodeVector(types)
155
158
  );
156
159
 
160
+ const dataSection = data.length === 0 ? [] : createSection(
161
+ Section.data,
162
+ encodeVector(data.map(x => [ 0x00, Opcodes.i32_const, ...signedLEB128(x.offset), Opcodes.end, ...encodeVector(x.bytes) ]))
163
+ );
164
+
165
+ const dataCountSection = data.length === 0 ? [] : createSection(
166
+ Section.data_count,
167
+ unsignedLEB128(data.length)
168
+ );
169
+
157
170
  if (process.argv.includes('-sections')) console.log({
158
171
  typeSection: typeSection.map(x => x.toString(16)),
159
172
  importSection: importSection.map(x => x.toString(16)),
160
173
  funcSection: funcSection.map(x => x.toString(16)),
161
174
  globalSection: globalSection.map(x => x.toString(16)),
162
175
  exportSection: exportSection.map(x => x.toString(16)),
163
- codeSection: codeSection.map(x => x.toString(16))
176
+ codeSection: codeSection.map(x => x.toString(16)),
177
+ dataSection: dataSection.map(x => x.toString(16)),
164
178
  });
165
179
 
166
180
  return Uint8Array.from([
@@ -174,6 +188,8 @@ export default (funcs, globals, tags, pages, flags) => {
174
188
  ...tagSection,
175
189
  ...globalSection,
176
190
  ...exportSection,
177
- ...codeSection
191
+ ...dataCountSection,
192
+ ...codeSection,
193
+ ...dataSection
178
194
  ]);
179
195
  };
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
package/hi.c ADDED
@@ -0,0 +1,37 @@
1
+ #include <stdio.h>
2
+
3
+ double inline f64_f(double x, double y) {
4
+ return x - (int)(x / y) * y;
5
+ }
6
+
7
+ double isPrime(double number) {
8
+ double i;
9
+
10
+ if (number < 2e+0) {
11
+ return 0e+0;
12
+ }
13
+ i = 2e+0;
14
+ while (i < number) {
15
+ if (f64_f(number, i) == 0e+0) {
16
+ return 0e+0;
17
+ }
18
+ i = i + 1e+0;
19
+ }
20
+ return 1e+0;
21
+ }
22
+
23
+ int main() {
24
+ double sum;
25
+ double counter;
26
+
27
+ sum = 0e+0;
28
+ counter = 0e+0;
29
+ while (counter <= 1e+5) {
30
+ if (isPrime(counter) == 1e+0) {
31
+ sum = sum + counter;
32
+ }
33
+ counter = counter + 1e+0;
34
+ }
35
+ printf("%f\n", sum);
36
+ }
37
+
package/out ADDED
Binary file
package/out.exe ADDED
Binary file