porffor 0.0.0-828ee15 → 0.0.0-a2afb57

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.
@@ -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]}`);
@@ -36,7 +51,7 @@ export default (funcs, globals, tags, pages, flags) => {
36
51
  // tree shake imports
37
52
  for (const f of funcs) {
38
53
  for (const inst of f.wasm) {
39
- if (inst[0] === Opcodes.call && inst[1] < importedFuncs.length) {
54
+ if ((inst[0] === Opcodes.call || inst[0] === Opcodes.return_call) && inst[1] < importedFuncs.length) {
40
55
  const idx = inst[1];
41
56
  const func = importedFuncs[idx];
42
57
 
@@ -51,15 +66,17 @@ export default (funcs, globals, tags, pages, flags) => {
51
66
  // fix call indexes for non-imports
52
67
  const delta = importedFuncs.length - importFuncs.length;
53
68
  for (const f of funcs) {
69
+ f.originalIndex = f.index;
54
70
  f.index -= delta;
55
71
 
56
72
  for (const inst of f.wasm) {
57
- if (inst[0] === Opcodes.call && inst[1] >= importedFuncs.length) {
73
+ if ((inst[0] === Opcodes.call || inst[0] === Opcodes.return_call) && inst[1] >= importedFuncs.length) {
58
74
  inst[1] -= delta;
59
75
  }
60
76
  }
61
77
  }
62
78
  }
79
+ globalThis.importFuncs = importFuncs;
63
80
 
64
81
  if (optLog) log('sections', `treeshake: using ${importFuncs.length}/${importedFuncs.length} imports`);
65
82
 
@@ -73,6 +90,14 @@ export default (funcs, globals, tags, pages, flags) => {
73
90
  encodeVector(funcs.map(x => getType(x.params, x.returns))) // type indexes
74
91
  );
75
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
+
76
101
  const globalSection = Object.keys(globals).length === 0 ? [] : createSection(
77
102
  Section.global,
78
103
  encodeVector(Object.keys(globals).map(x => [ globals[x].type, 0x01, ...number(globals[x].init ?? 0, globals[x].type).flat(), Opcodes.end ]))
@@ -80,6 +105,8 @@ export default (funcs, globals, tags, pages, flags) => {
80
105
 
81
106
  const exports = funcs.filter(x => x.export).map((x, i) => [ ...encodeString(x.name === 'main' ? 'm' : x.name), ExportDesc.func, x.index ]);
82
107
 
108
+ if (process.argv.includes('-always-memory') && pages.size === 0) pages.set('-always-memory', 0);
109
+
83
110
  const usesMemory = pages.size > 0;
84
111
  const memorySection = !usesMemory ? [] : createSection(
85
112
  Section.memory,
@@ -130,13 +157,24 @@ export default (funcs, globals, tags, pages, flags) => {
130
157
  encodeVector(types)
131
158
  );
132
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
+
133
170
  if (process.argv.includes('-sections')) console.log({
134
171
  typeSection: typeSection.map(x => x.toString(16)),
135
172
  importSection: importSection.map(x => x.toString(16)),
136
173
  funcSection: funcSection.map(x => x.toString(16)),
137
174
  globalSection: globalSection.map(x => x.toString(16)),
138
175
  exportSection: exportSection.map(x => x.toString(16)),
139
- codeSection: codeSection.map(x => x.toString(16))
176
+ codeSection: codeSection.map(x => x.toString(16)),
177
+ dataSection: dataSection.map(x => x.toString(16)),
140
178
  });
141
179
 
142
180
  return Uint8Array.from([
@@ -145,10 +183,13 @@ export default (funcs, globals, tags, pages, flags) => {
145
183
  ...typeSection,
146
184
  ...importSection,
147
185
  ...funcSection,
186
+ ...chSection,
148
187
  ...memorySection,
149
188
  ...tagSection,
150
189
  ...globalSection,
151
190
  ...exportSection,
152
- ...codeSection
191
+ ...dataCountSection,
192
+ ...codeSection,
193
+ ...dataSection
153
194
  ]);
154
195
  };
package/compiler/wrap.js CHANGED
@@ -5,6 +5,7 @@ import decompile from './decompile.js';
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)) => {
@@ -90,6 +92,15 @@ export default async (source, flags = [ 'module' ], customImports = {}, print =
90
92
  return Array.from(new Uint16Array(memory.buffer, pointer + 4, length)).map(x => String.fromCharCode(x)).join('');
91
93
  }
92
94
 
95
+ case 'function': {
96
+ // wasm func index, including all imports
97
+ const func = funcs.find(x => (x.originalIndex ?? x.index) === ret);
98
+ if (!func) return ret;
99
+
100
+ // make fake empty func for repl/etc
101
+ return {[func.name]() {}}[func.name];
102
+ }
103
+
93
104
  default: return ret;
94
105
  }
95
106
  } catch (e) {
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
package/package.json CHANGED
@@ -1,7 +1,7 @@
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.0.0-828ee15",
4
+ "version": "0.0.0-a2afb57",
5
5
  "author": "CanadaHonk",
6
6
  "license": "MIT",
7
7
  "dependencies": {
package/r.js ADDED
@@ -0,0 +1,39 @@
1
+ compareArray.isSameValue = function(a, b) {
2
+ if (a === 0 && b === 0) return 1 / a === 1 / b;
3
+ if (a !== a && b !== b) return true;
4
+
5
+ return a === b;
6
+ };
7
+
8
+ function compareArray(a, b) {
9
+ // if either are nullish
10
+ if (a == null || b == null) return false;
11
+
12
+ // megahack: all arrays from now on will be >0 pointer
13
+ const _hack = '';
14
+
15
+ // hack: enforce type inference of being arrays
16
+ a ??= [];
17
+ b ??= [];
18
+
19
+ if (b.length !== a.length) {
20
+ return false;
21
+ }
22
+
23
+ for (var i = 0; i < a.length; i++) {
24
+ if (!compareArray.isSameValue(b[i], a[i])) {
25
+ return false;
26
+ }
27
+ }
28
+
29
+ return true;
30
+ }
31
+
32
+ console.log(compareArray(null, []));
33
+ console.log(compareArray(undefined, []));
34
+
35
+ console.log(compareArray([], []));
36
+ console.log(compareArray([ 1 ], []));
37
+ console.log(compareArray([ 1 ], [ 1 ]));
38
+ console.log(compareArray([ 1, 2 ], [ 1 ]));
39
+ console.log(compareArray([ 1, 2 ], [ 1, 2 ]));
@@ -0,0 +1,37 @@
1
+ # rhemyn
2
+ a basic experimental wip regex engine/aot wasm compiler in js. regex engine for porffor. uses own regex parser, no dependencies (excluding porffor internals). <br>
3
+ age: ~1 day
4
+
5
+ made for use with porffor but could possibly be adapted, implementation/library notes:
6
+ - exposes functions for each regex "operation" (eg test, match)
7
+ - given a regex pattern string (eg `a+`), it returns a "function" object
8
+ - wasm function returned expects an i32 pointer to a utf-16 string (can add utf-8 option later if someone else actually wants to use this)
9
+
10
+ ## syntax
11
+ 🟢 supported 🟡 partial 🟠 parsed only 🔴 unsupported
12
+
13
+ - 🟢 literal characters (eg `a`)
14
+ - 🟢 escaping (eg `\.\n\cJ\x0a\u000a`)
15
+ - 🟢 character itself (eg `\.`)
16
+ - 🟢 escape sequences (eg `\n`)
17
+ - 🟢 control character (eg `\cJ`)
18
+ - 🟢 unicode code points (eg `\x00`, `\u0000`)
19
+ - 🟢 sets (eg `[ab]`)
20
+ - 🟢 ranges (eg `[a-z]`)
21
+ - 🟢 negated sets (eg `[^ab]`)
22
+ - 🟢 metacharacters
23
+ - 🟢 dot (eg `a.b`)
24
+ - 🟢 digit, not digit (eg `\d\D`)
25
+ - 🟢 word, not word (eg `\w\W`)
26
+ - 🟢 whitespace, not whitespace (eg `\s\S`)
27
+ - 🟠 quantifiers
28
+ - 🟠 star (eg `a*`)
29
+ - 🟠 plus (eg `a+`)
30
+ - 🟠 optional (eg `a?`)
31
+ - 🟠 lazy modifier (eg `a*?`)
32
+ - 🔴 n repetitions (eg `a{4}`)
33
+ - 🔴 n-m repetitions (eg `a{2,4}`)
34
+ - 🔴 assertions
35
+ - 🔴 beginning (eg `^a`)
36
+ - 🔴 end (eg `a$`)
37
+ - 🔴 word boundary assertion (eg `\b\B`)
@@ -0,0 +1,214 @@
1
+ import { Blocktype, Opcodes, Valtype, PageSize, ValtypeSize } from '../compiler/wasmSpec.js';
2
+ import { number } from '../compiler/embedding.js';
3
+ import { signedLEB128, unsignedLEB128 } from '../compiler/encoding.js';
4
+ import parse from './parse.js';
5
+
6
+ // local indexes
7
+ const BasePointer = 0; // base string pointer
8
+ const IterPointer = 1; // this iteration base pointer
9
+ const EndPointer = 2; // pointer for the end
10
+ const Counter = 3; // what char we are running on
11
+ const Pointer = 4; // next char BYTE pointer
12
+ const Length = 5;
13
+ const Tmp = 6;
14
+
15
+ let exprLastGet = false;
16
+ const generate = (node, negated = false, get = true, func = 'test') => {
17
+ let out = [];
18
+ switch (node.type) {
19
+ case 'Expression':
20
+ exprLastGet = false;
21
+ out = [
22
+ // set length local
23
+ [ Opcodes.local_get, BasePointer ],
24
+ [ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, 0 ],
25
+ [ Opcodes.local_set, Length ],
26
+
27
+ // set iter pointer local as base + sizeof i32 initially
28
+ [ Opcodes.local_get, BasePointer ],
29
+ ...number(ValtypeSize.i32, Valtype.i32),
30
+ [ Opcodes.i32_add ],
31
+ [ Opcodes.local_set, IterPointer ],
32
+
33
+ [ Opcodes.loop, Blocktype.void ],
34
+
35
+ // reset pointer as iter pointer
36
+ [ Opcodes.local_get, IterPointer ],
37
+ [ Opcodes.local_set, Pointer ],
38
+
39
+ [ Opcodes.block, Blocktype.void ],
40
+
41
+ // generate checks
42
+ ...node.body.flatMap((x, i) => {
43
+ exprLastGet = x.type !== 'Group' && i === (node.body.length - 1);
44
+ return generate(x, negated);
45
+ }),
46
+
47
+ // reached end without branching out, successful match
48
+ ...({
49
+ test: number(1, Valtype.i32),
50
+ search: [
51
+ [ Opcodes.local_get, Counter ]
52
+ ]
53
+ })[func],
54
+ [ Opcodes.return ],
55
+
56
+ [ Opcodes.end ],
57
+
58
+ // increment iter pointer by sizeof i16
59
+ [ Opcodes.local_get, IterPointer ],
60
+ ...number(ValtypeSize.i16, Valtype.i32),
61
+ [ Opcodes.i32_add ],
62
+ [ Opcodes.local_set, IterPointer ],
63
+
64
+ // increment counter by 1, check if eq length, if not loop
65
+ [ Opcodes.local_get, Counter ],
66
+ ...number(1, Valtype.i32),
67
+ [ Opcodes.i32_add ],
68
+ [ Opcodes.local_tee, Counter ],
69
+
70
+ [ Opcodes.local_get, Length ],
71
+ [ Opcodes.i32_ne ],
72
+
73
+ [ Opcodes.br_if, 0 ],
74
+ [ Opcodes.end ],
75
+
76
+ // no match, return 0
77
+ ...number(({
78
+ test: 0,
79
+ search: -1
80
+ })[func], Valtype.i32)
81
+ ];
82
+
83
+ if (globalThis.regexLog) {
84
+ const underline = x => `\u001b[4m\u001b[1m${x}\u001b[0m`;
85
+ console.log(`\n${underline('ast')}`);
86
+ console.log(node);
87
+ console.log(`\n${underline('wasm bytecode')}\n` + decompile(out) + '\n');
88
+ }
89
+
90
+ break;
91
+
92
+ case 'Character':
93
+ out = generateChar(node, node.negated ^ negated, get);
94
+ break;
95
+
96
+ case 'Set':
97
+ out = generateSet(node, node.negated, get);
98
+ break;
99
+
100
+ case 'Group':
101
+ out = generateGroup(node, negated, get);
102
+ break;
103
+
104
+ case 'Range':
105
+ out = generateRange(node, negated, get);
106
+ break;
107
+ }
108
+
109
+ return out;
110
+ };
111
+
112
+ const getNextChar = () => [
113
+ // get char from pointer
114
+ [ Opcodes.local_get, Pointer ],
115
+ [ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128(0) ],
116
+
117
+ ...(exprLastGet ? [] : [
118
+ // pointer += sizeof i16
119
+ [ Opcodes.local_get, Pointer ],
120
+ ...number(ValtypeSize.i16, Valtype.i32),
121
+ [ Opcodes.i32_add ],
122
+ [ Opcodes.local_set, Pointer ]
123
+ ])
124
+ ];
125
+
126
+ const checkFailure = () => [
127
+ // surely we do not need to do this for every single mismatch, right?
128
+ /* [ Opcodes.if, Blocktype.void ],
129
+ ...number(0, Valtype.i32),
130
+ [ Opcodes.return ],
131
+ [ Opcodes.end ], */
132
+
133
+ [ Opcodes.br_if, 0 ]
134
+ ];
135
+
136
+ const generateChar = (node, negated, get) => {
137
+ return [
138
+ ...(get ? getNextChar() : []),
139
+ ...number(node.char.charCodeAt(0), Valtype.i32),
140
+ negated ? [ Opcodes.i32_eq ] : [ Opcodes.i32_ne ],
141
+ ...(get ? checkFailure(): [])
142
+ ];
143
+ };
144
+
145
+ const generateSet = (node, negated, get) => {
146
+ // for a single char we do not need a tmp, it is like just
147
+ const singleChar = node.body.length === 1 && node.body[0].type === 'Character';
148
+
149
+ let out = [
150
+ ...(get ? getNextChar() : []),
151
+ ...(singleChar ? [] : [ [ Opcodes.local_set, Tmp ] ]),
152
+ ];
153
+
154
+ for (const x of node.body) {
155
+ out = [
156
+ ...out,
157
+ ...(singleChar ? [] : [ [ Opcodes.local_get, Tmp ] ]),
158
+ ...generate(x, negated, false)
159
+ ];
160
+ }
161
+
162
+ out = out.concat(new Array(node.body.length - 1).fill(negated ? [ Opcodes.i32_or ] : [ Opcodes.i32_and ]));
163
+
164
+ return [
165
+ ...out,
166
+ ...checkFailure()
167
+ ];
168
+ };
169
+
170
+ const generateRange = (node, negated, get) => {
171
+ return [
172
+ ...(get ? getNextChar() : []),
173
+ ...(get ? [ [ Opcodes.local_tee, Tmp ] ] : []),
174
+
175
+ ...number(node.from.charCodeAt(0), Valtype.i32),
176
+ // negated ? [ Opcodes.i32_lt_s ] : [ Opcodes.i32_ge_s ],
177
+ negated ? [ Opcodes.i32_ge_s ] : [ Opcodes.i32_lt_s ],
178
+
179
+ [ Opcodes.local_get, Tmp ],
180
+ ...number(node.to.charCodeAt(0), Valtype.i32),
181
+ // negated ? [ Opcodes.i32_gt_s ] : [ Opcodes.i32_le_s ],
182
+ negated ? [ Opcodes.i32_le_s ] : [ Opcodes.i32_gt_s ],
183
+
184
+ negated ? [ Opcodes.i32_and ] : [ Opcodes.i32_or ],
185
+ ...(get ? checkFailure(): [])
186
+ ];
187
+ };
188
+
189
+ const generateGroup = (node, negated, get) => {
190
+
191
+ };
192
+
193
+ export const test = (regex, index = 0, name = 'regex_test_' + regex) => outputFunc(generate(parse(regex), false, true, 'test'), name, index);
194
+ export const search = (regex, index = 0, name = 'regex_search_' + regex) => outputFunc(generate(parse(regex), false, true, 'search'), name, index);
195
+
196
+ const outputFunc = (wasm, name, index) => ({
197
+ name,
198
+ index,
199
+ wasm,
200
+
201
+ export: true,
202
+ params: [ Valtype.i32 ],
203
+ returns: [ Valtype.i32 ],
204
+ returnType: 0xffffffffffff1, // boolean - todo: do not hardcode this
205
+ locals: {
206
+ basePointer: { idx: 0, type: Valtype.i32 },
207
+ iterPointer: { idx: 1, type: Valtype.i32 },
208
+ endPointer: { idx: 2, type: Valtype.i32 },
209
+ counter: { idx: 3, type: Valtype.i32 },
210
+ pointer: { idx: 4, type: Valtype.i32 },
211
+ length: { idx: 5, type: Valtype.i32 },
212
+ tmp: { idx: 6, type: Valtype.i32 },
213
+ }
214
+ });