porffor 0.16.0-fe07da0f4 → 0.17.0-048c6f2ee
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.
- package/CONTRIBUTING.md +2 -2
- package/README.md +5 -17
- package/compiler/2c.js +123 -75
- package/compiler/allocators.js +128 -0
- package/compiler/assemble.js +12 -5
- package/compiler/builtins/array.ts +72 -5
- package/compiler/builtins/base64.ts +28 -24
- package/compiler/builtins/date.ts +3 -30
- package/compiler/builtins/number.ts +10 -21
- package/compiler/builtins/porffor.d.ts +10 -0
- package/compiler/builtins/set.ts +7 -5
- package/compiler/builtins/string_f64.ts +10 -0
- package/compiler/builtins/z_ecma262.ts +62 -0
- package/compiler/builtins.js +26 -6
- package/compiler/codegen.js +364 -393
- package/compiler/cyclone.js +535 -0
- package/compiler/decompile.js +3 -1
- package/compiler/generated_builtins.js +153 -77
- package/compiler/havoc.js +93 -0
- package/compiler/index.js +104 -7
- package/compiler/opt.js +10 -44
- package/compiler/parse.js +2 -8
- package/compiler/pgo.js +212 -0
- package/compiler/precompile.js +12 -7
- package/compiler/prefs.js +8 -4
- package/compiler/prototype.js +34 -43
- package/compiler/wasmSpec.js +2 -2
- package/compiler/wrap.js +72 -20
- package/package.json +3 -5
- package/runner/index.js +26 -11
- /package/runner/{profiler.js → profile.js} +0 -0
package/CONTRIBUTING.md
CHANGED
@@ -26,7 +26,7 @@ You can also swap out `node` in the alias to use another runtime like Deno (`den
|
|
26
26
|
|
27
27
|
### Precompile
|
28
28
|
|
29
|
-
**If you update any file inside `compiler/builtins` you will need to do this for it to update inside Porffor otherwise your changes will have no effect.** Run
|
29
|
+
**If you update any file inside `compiler/builtins` you will need to do this for it to update inside Porffor otherwise your changes will have no effect.** Run `./porf precompile` to precompile. It may error during this, if so, you might have an error in your code or there could be a compiler error with Porffor (feel free to ask for help as soon as you encounter any errors with it).
|
30
30
|
|
31
31
|
<br>
|
32
32
|
|
@@ -233,7 +233,7 @@ builtins/tostring_number: impl radix
|
|
233
233
|
|
234
234
|
## Test262
|
235
235
|
|
236
|
-
|
236
|
+
For the first time, ensure you run `./test262/setup.sh`.
|
237
237
|
|
238
238
|
Run `node test262` to run all the tests and get an output of total overall test results.
|
239
239
|
|
package/README.md
CHANGED
@@ -29,7 +29,7 @@ Expect nothing to work! Only very limited JS is currently supported. See files i
|
|
29
29
|
> [!WARNING]
|
30
30
|
> Compiling to native binaries uses [2c](#2c), Porffor's own Wasm -> C compiler, which is experimental.
|
31
31
|
|
32
|
-
**`porf native path/to/script.js out(.exe)`**. You can specify the compiler with `--compiler=clang
|
32
|
+
**`porf native path/to/script.js out(.exe)`**. You can specify the compiler with `--compiler=clang|gcc|zig` (`clang` by default), and which optimization level to use with `--cO=Ofast|O3|O2|O1|O0` (`Ofast` by default). Output binaries are also stripped by default.
|
33
33
|
|
34
34
|
### Compiling to C
|
35
35
|
> [!WARNING]
|
@@ -49,7 +49,7 @@ Expect nothing to work! Only very limited JS is currently supported. See files i
|
|
49
49
|
|
50
50
|
**`porf debug path/to/script.js`**
|
51
51
|
|
52
|
-
###
|
52
|
+
### Debugging the compiled Wasm of a JS file
|
53
53
|
> [!WARNING]
|
54
54
|
> Very experimental WIP feature!
|
55
55
|
|
@@ -63,26 +63,14 @@ Expect nothing to work! Only very limited JS is currently supported. See files i
|
|
63
63
|
- `--valtype=i32|i64|f64` (default: `f64`) to set valtype
|
64
64
|
- `-O0` to disable opt
|
65
65
|
- `-O1` (default) to enable basic opt (simplify insts, treeshake wasm imports)
|
66
|
-
- `-O2` to enable advanced opt (inlining). unstable
|
67
|
-
- `-O3` to enable advanceder opt (precompute const math). unstable
|
68
|
-
- `--no-run` to not run wasm output, just compile
|
69
|
-
- `--opt-log` to log some opts
|
70
|
-
- `--code-log` to log some codegen (you probably want `-funcs`)
|
71
|
-
- `--regex-log` to log some regex
|
72
|
-
- `--funcs` to log funcs
|
73
|
-
- `--ast-log` to log AST
|
74
|
-
- `--opt-funcs` to log funcs after opt
|
75
|
-
- `--sections` to log sections as hex
|
76
|
-
- `--opt-no-inline` to not inline any funcs
|
77
|
-
- `--tail-call` to enable tail calls (experimental + not widely implemented)
|
78
|
-
- `--compile-hints` to enable V8 compilation hints (experimental + doesn't seem to do much?)
|
66
|
+
- `-O2` to enable advanced opt (inlining). unstable!
|
67
|
+
- `-O3` to enable advanceder opt (precompute const math). unstable!
|
79
68
|
|
80
69
|
## Limitations
|
81
70
|
- No full object support yet
|
82
71
|
- Little built-ins/prototype
|
83
72
|
- No async/promise/await
|
84
73
|
- No variables between scopes (except args and globals)
|
85
|
-
- Literal callees only in calls (eg `print()` works, `a = print; a()` does not)
|
86
74
|
- No `eval()` etc (since it is AOT)
|
87
75
|
|
88
76
|
## Sub-engines
|
@@ -112,7 +100,7 @@ These include some early (stage 1/0) and/or dead (last commit years ago) proposa
|
|
112
100
|
|
113
101
|
- Number literals
|
114
102
|
- Declaring functions
|
115
|
-
- Calling functions
|
103
|
+
- Calling functions
|
116
104
|
- `return`
|
117
105
|
- `let`/`const`/`var` basic declarations
|
118
106
|
- Some basic integer operators (`+-/*%`)
|
package/compiler/2c.js
CHANGED
@@ -2,10 +2,11 @@ import { read_ieee754_binary64, read_signedLEB128, read_unsignedLEB128 } from '.
|
|
2
2
|
import { Blocktype, Opcodes, Valtype } from './wasmSpec.js';
|
3
3
|
import { operatorOpcode } from './expression.js';
|
4
4
|
import { log } from './log.js';
|
5
|
+
import Prefs from './prefs.js';
|
5
6
|
|
6
7
|
const CValtype = {
|
7
|
-
i8: '
|
8
|
-
i16: '
|
8
|
+
i8: 'u8',
|
9
|
+
i16: 'u16',
|
9
10
|
i32: 'i32',
|
10
11
|
u32: 'u32',
|
11
12
|
i64: 'i64',
|
@@ -17,8 +18,8 @@ const CValtype = {
|
|
17
18
|
undefined: 'void'
|
18
19
|
};
|
19
20
|
|
20
|
-
const alwaysPreface = `typedef uint8_t
|
21
|
-
typedef uint16_t
|
21
|
+
const alwaysPreface = `typedef uint8_t u8;
|
22
|
+
typedef uint16_t u16;
|
22
23
|
typedef int32_t i32;
|
23
24
|
typedef uint32_t u32;
|
24
25
|
typedef int64_t i64;
|
@@ -29,72 +30,123 @@ typedef double f64;
|
|
29
30
|
f64 NAN = 0e+0/0e+0;
|
30
31
|
|
31
32
|
struct ReturnValue {
|
32
|
-
|
33
|
-
|
33
|
+
f64 value;
|
34
|
+
i32 type;
|
34
35
|
};\n\n`;
|
35
36
|
|
36
|
-
// todo:
|
37
|
+
// todo: review whether 2cMemcpy should be default or not
|
37
38
|
|
38
39
|
// all:
|
39
40
|
// immediates: ['align', 'offset']
|
40
|
-
const CMemFuncs = {
|
41
|
+
const CMemFuncs = Prefs['2cMemcpy'] ? {
|
41
42
|
[Opcodes.i32_store]: {
|
42
43
|
c: `memcpy(_memory + offset + pointer, &value, sizeof(value));`,
|
43
44
|
args: ['pointer', 'value'],
|
44
|
-
argTypes: [
|
45
|
+
argTypes: ['i32', 'i32'],
|
45
46
|
returns: false
|
46
47
|
},
|
47
48
|
[Opcodes.i32_store16]: {
|
48
49
|
c: `memcpy(_memory + offset + pointer, &value, sizeof(value));`,
|
49
50
|
args: ['pointer', 'value'],
|
50
|
-
argTypes: [
|
51
|
+
argTypes: ['i32', 'u16'],
|
51
52
|
returns: false
|
52
53
|
},
|
53
54
|
[Opcodes.i32_store8]: {
|
54
55
|
c: `memcpy(_memory + offset + pointer, &value, sizeof(value));`,
|
55
56
|
args: ['pointer', 'value'],
|
56
|
-
argTypes: [
|
57
|
+
argTypes: ['i32', 'u8'],
|
57
58
|
returns: false
|
58
59
|
},
|
59
60
|
|
60
61
|
[Opcodes.i32_load]: {
|
61
|
-
c:
|
62
|
+
c: `i32 out;
|
62
63
|
memcpy(&out, _memory + offset + pointer, sizeof(out));
|
63
64
|
return out;`,
|
64
65
|
args: ['pointer'],
|
65
|
-
argTypes: [
|
66
|
-
returns:
|
66
|
+
argTypes: ['i32'],
|
67
|
+
returns: 'i32'
|
67
68
|
},
|
68
69
|
[Opcodes.i32_load16_u]: {
|
69
|
-
c:
|
70
|
+
c: `u16 out;
|
70
71
|
memcpy(&out, _memory + offset + pointer, sizeof(out));
|
71
72
|
return out;`,
|
72
73
|
args: ['pointer'],
|
73
|
-
argTypes: [
|
74
|
-
returns:
|
74
|
+
argTypes: ['i32'],
|
75
|
+
returns: 'i32'
|
75
76
|
},
|
76
77
|
[Opcodes.i32_load8_u]: {
|
77
|
-
c:
|
78
|
+
c: `u8 out;
|
78
79
|
memcpy(&out, _memory + offset + pointer, sizeof(out));
|
79
80
|
return out;`,
|
80
81
|
args: ['pointer'],
|
81
|
-
argTypes: [
|
82
|
-
returns:
|
82
|
+
argTypes: ['i32'],
|
83
|
+
returns: 'i32'
|
83
84
|
},
|
84
85
|
|
85
86
|
[Opcodes.f64_store]: {
|
86
87
|
c: `memcpy(_memory + offset + pointer, &value, sizeof(value));`,
|
87
88
|
args: ['pointer', 'value'],
|
88
|
-
argTypes: [
|
89
|
+
argTypes: ['i32', 'f64'],
|
89
90
|
returns: false
|
90
91
|
},
|
91
92
|
[Opcodes.f64_load]: {
|
92
|
-
c:
|
93
|
+
c: `f64 out;
|
93
94
|
memcpy(&out, _memory + offset + pointer, sizeof(out));
|
94
95
|
return out;`,
|
95
96
|
args: ['pointer'],
|
96
|
-
argTypes: [
|
97
|
-
returns:
|
97
|
+
argTypes: ['i32'],
|
98
|
+
returns: 'f64'
|
99
|
+
},
|
100
|
+
} : {
|
101
|
+
[Opcodes.i32_store]: {
|
102
|
+
c: `*((i32*)(_memory + offset + pointer)) = value;`,
|
103
|
+
args: ['pointer', 'value'],
|
104
|
+
argTypes: ['i32', 'i32'],
|
105
|
+
returns: false
|
106
|
+
},
|
107
|
+
[Opcodes.i32_store16]: {
|
108
|
+
c: `*((u16*)(_memory + offset + pointer)) = value;`,
|
109
|
+
args: ['pointer', 'value'],
|
110
|
+
argTypes: ['i32', 'u16'],
|
111
|
+
returns: false
|
112
|
+
},
|
113
|
+
[Opcodes.i32_store8]: {
|
114
|
+
c: `*((u8*)(_memory + offset + pointer)) = value;`,
|
115
|
+
args: ['pointer', 'value'],
|
116
|
+
argTypes: ['i32', 'u8'],
|
117
|
+
returns: false
|
118
|
+
},
|
119
|
+
|
120
|
+
[Opcodes.i32_load]: {
|
121
|
+
c: `return *((i32*)(_memory + offset + pointer));`,
|
122
|
+
args: ['pointer'],
|
123
|
+
argTypes: ['i32'],
|
124
|
+
returns: 'i32'
|
125
|
+
},
|
126
|
+
[Opcodes.i32_load16_u]: {
|
127
|
+
c: `return *((u16*)(_memory + offset + pointer));`,
|
128
|
+
args: ['pointer'],
|
129
|
+
argTypes: ['i32'],
|
130
|
+
returns: 'i32'
|
131
|
+
},
|
132
|
+
[Opcodes.i32_load8_u]: {
|
133
|
+
c: `return *((u8*)(_memory + offset + pointer));`,
|
134
|
+
args: ['pointer'],
|
135
|
+
argTypes: ['i32'],
|
136
|
+
returns: 'i32'
|
137
|
+
},
|
138
|
+
|
139
|
+
[Opcodes.f64_store]: {
|
140
|
+
c: `*((f64*)(_memory + offset + pointer)) = value;`,
|
141
|
+
args: ['pointer', 'value'],
|
142
|
+
argTypes: ['i32', 'f64'],
|
143
|
+
returns: false
|
144
|
+
},
|
145
|
+
[Opcodes.f64_load]: {
|
146
|
+
c: `return *((f64*)(_memory + offset + pointer));`,
|
147
|
+
args: ['pointer'],
|
148
|
+
argTypes: ['i32'],
|
149
|
+
returns: 'f64'
|
98
150
|
},
|
99
151
|
};
|
100
152
|
|
@@ -108,7 +160,7 @@ for (const x in CValtype) {
|
|
108
160
|
|
109
161
|
const removeBrackets = str => {
|
110
162
|
// return str;
|
111
|
-
// if (str.startsWith(
|
163
|
+
// if (str.startsWith('(i32)(u32)')) return '(i32)(u32)(' + removeBrackets(str.slice(22, -1)) + ')';
|
112
164
|
|
113
165
|
for (const x in CValtype) {
|
114
166
|
const p = `(${x})`;
|
@@ -152,11 +204,16 @@ export default ({ funcs, globals, tags, data, exceptions, pages }) => {
|
|
152
204
|
|
153
205
|
if (pages.size > 0) {
|
154
206
|
prepend.set('_memory', `char _memory[${pages.size * pageSize}];\n`);
|
155
|
-
includes.set('string.h', true);
|
207
|
+
if (Prefs['2cMemcpy']) includes.set('string.h', true);
|
156
208
|
}
|
157
209
|
|
158
210
|
if (data.length > 0) {
|
159
|
-
|
211
|
+
if (Prefs['2cMemcpy']) {
|
212
|
+
prependMain.set('_data', data.map(x => `memcpy(_memory + ${x.offset}, (unsigned char[]){${x.bytes.join(',')}}, ${x.bytes.length});`).join('\n '));
|
213
|
+
includes.set('string.h', true);
|
214
|
+
} else {
|
215
|
+
prependMain.set('_data', data.map(x => x.bytes.reduce((acc, y, i) => acc + `_memory[${x.offset + i}]=(u8)${y};`, '')).join('\n '));
|
216
|
+
}
|
160
217
|
}
|
161
218
|
|
162
219
|
if (importFuncs.find(x => x.name === '__Porffor_readArgv')) {
|
@@ -168,10 +225,10 @@ export default ({ funcs, globals, tags, data, exceptions, pages }) => {
|
|
168
225
|
|
169
226
|
let depth = 1;
|
170
227
|
let brDepth = 0;
|
171
|
-
const line = (str, semi = true) => out += `${' '.repeat(depth
|
228
|
+
const line = (str, semi = true) => out += `${' '.repeat((depth + brDepth) * 2)}${str}${semi ? ';' : ''}\n`;
|
172
229
|
const lines = lines => {
|
173
230
|
for (const x of lines) {
|
174
|
-
out += `${' '.repeat(depth * 2)}${x}\n`;
|
231
|
+
out += `${' '.repeat((depth + brDepth) * 2)}${x}\n`;
|
175
232
|
}
|
176
233
|
};
|
177
234
|
|
@@ -219,10 +276,10 @@ export default ({ funcs, globals, tags, data, exceptions, pages }) => {
|
|
219
276
|
|
220
277
|
const shouldInline = false; // f.internal;
|
221
278
|
if (f.name === 'main') out += `int main(${prependMain.has('argv') ? 'int argc, char* argv[]' : ''}) {\n`;
|
222
|
-
else out += `${f.internal ? (returns ?
|
279
|
+
else out += `${f.internal ? (returns ? 'f64' : 'void') : 'struct ReturnValue'} ${shouldInline ? 'inline ' : ''}${sanitize(f.name)}(${f.params.map((x, i) => `${CValtype[x]} ${invLocals[i]}`).join(', ')}) {\n`;
|
223
280
|
|
224
281
|
if (f.name === 'main') {
|
225
|
-
out += [...prependMain.values()].join('\n');
|
282
|
+
out += ' ' + [...prependMain.values()].join('\n ');
|
226
283
|
if (prependMain.size > 0) out += '\n\n';
|
227
284
|
}
|
228
285
|
|
@@ -283,12 +340,12 @@ export default ({ funcs, globals, tags, data, exceptions, pages }) => {
|
|
283
340
|
switch (i[1]) {
|
284
341
|
// i32_trunc_sat_f64_s
|
285
342
|
case 0x02:
|
286
|
-
vals.push(`(
|
343
|
+
vals.push(`(i32)(${vals.pop()})`);
|
287
344
|
break;
|
288
345
|
|
289
346
|
// i32_trunc_sat_f64_u
|
290
347
|
case 0x03:
|
291
|
-
vals.push(`(
|
348
|
+
vals.push(`(u32)(${vals.pop()})`);
|
292
349
|
break;
|
293
350
|
}
|
294
351
|
|
@@ -337,7 +394,7 @@ export default ({ funcs, globals, tags, data, exceptions, pages }) => {
|
|
337
394
|
|
338
395
|
case Opcodes.f64_trunc:
|
339
396
|
// vals.push(`trunc(${vals.pop()})`);
|
340
|
-
vals.push(`(
|
397
|
+
vals.push(`(i32)(${removeBrackets(vals.pop())})`); // this is ~10x faster with clang??
|
341
398
|
break;
|
342
399
|
|
343
400
|
case Opcodes.f64_convert_i32_u:
|
@@ -345,7 +402,7 @@ export default ({ funcs, globals, tags, data, exceptions, pages }) => {
|
|
345
402
|
case Opcodes.f64_convert_i64_u:
|
346
403
|
case Opcodes.f64_convert_i64_s:
|
347
404
|
// int to f64
|
348
|
-
vals.push(`(
|
405
|
+
vals.push(`(f64)(${removeBrackets(vals.pop())})`);
|
349
406
|
break;
|
350
407
|
|
351
408
|
case Opcodes.i32_eqz:
|
@@ -353,7 +410,7 @@ export default ({ funcs, globals, tags, data, exceptions, pages }) => {
|
|
353
410
|
vals.push(`!(${removeBrackets(vals.pop())})`);
|
354
411
|
} else {
|
355
412
|
let cond = '(' + removeBrackets(vals.pop());
|
356
|
-
if (cond.startsWith(`(
|
413
|
+
if (cond.startsWith(`(i32)`)) cond = `${cond.slice(5)} == 0e+0`;
|
357
414
|
else cond += ') == 0';
|
358
415
|
vals.push(cond);
|
359
416
|
}
|
@@ -362,13 +419,16 @@ export default ({ funcs, globals, tags, data, exceptions, pages }) => {
|
|
362
419
|
|
363
420
|
case Opcodes.return:
|
364
421
|
// line(`return${returns ? ` ${removeBrackets(vals.pop())}` : ''}`);
|
365
|
-
|
422
|
+
const b = vals.pop();
|
423
|
+
const a = vals.pop();
|
424
|
+
line(`return${returns ? ` (struct ReturnValue){ ${removeBrackets(a)}, ${removeBrackets(b)} }` : ''}`);
|
425
|
+
// line(`return${returns ? ` (struct ReturnValue){ ${removeBrackets(vals.pop())}, ${removeBrackets(vals.pop())} }` : ''}`);
|
366
426
|
break;
|
367
427
|
|
368
428
|
case Opcodes.if: {
|
369
429
|
let cond = removeBrackets(vals.pop());
|
370
430
|
if (!lastCond) {
|
371
|
-
if (cond.startsWith(`(
|
431
|
+
if (cond.startsWith(`(i32)`)) cond = `${cond.slice(5)} != 0e+0`;
|
372
432
|
else cond = `(${cond}) != 0`;
|
373
433
|
}
|
374
434
|
|
@@ -434,28 +494,16 @@ export default ({ funcs, globals, tags, data, exceptions, pages }) => {
|
|
434
494
|
const importFunc = importFuncs[i[1]];
|
435
495
|
switch (importFunc.name) {
|
436
496
|
case 'print':
|
437
|
-
// line(`printf("%f\\n", ${vals.pop()})`);
|
438
497
|
line(`printf("${valtype === 'f64' ? '%g' : '%i'}\\n", ${vals.pop()})`);
|
439
498
|
includes.set('stdio.h', true);
|
440
499
|
break;
|
441
500
|
case 'printChar':
|
442
|
-
line(`
|
501
|
+
line(`putchar((int)(${vals.pop()}))`);
|
443
502
|
includes.set('stdio.h', true);
|
444
503
|
break;
|
445
504
|
|
446
505
|
case 'time':
|
447
506
|
line(`double _time_out`);
|
448
|
-
/* platformSpecific(
|
449
|
-
`FILETIME _time_filetime;
|
450
|
-
GetSystemTimeAsFileTime(&_time_filetime);
|
451
|
-
|
452
|
-
ULARGE_INTEGER _time_ularge;
|
453
|
-
_time_ularge.LowPart = _time_filetime.dwLowDateTime;
|
454
|
-
_time_ularge.HighPart = _time_filetime.dwHighDateTime;
|
455
|
-
_time_out = (_time_ularge.QuadPart - 116444736000000000i64) / 10000.;`,
|
456
|
-
`struct timespec _time;
|
457
|
-
clock_gettime(CLOCK_MONOTONIC, &_time);
|
458
|
-
_time_out = _time.tv_nsec / 1000000.;`); */
|
459
507
|
platformSpecific(
|
460
508
|
`LARGE_INTEGER _time_freq, _time_t;
|
461
509
|
QueryPerformanceFrequency(&_time_freq);
|
@@ -470,14 +518,11 @@ _time_out = _time.tv_nsec / 1000000. + _time.tv_sec * 1000.;`);
|
|
470
518
|
winIncludes.set('windows.h', true);
|
471
519
|
break;
|
472
520
|
|
473
|
-
case '__Porffor_readArgv':
|
474
|
-
includes.set('stdlib.h', true);
|
475
|
-
|
521
|
+
case '__Porffor_readArgv': {
|
476
522
|
prepend.set('__Porffor_readArgv',
|
477
|
-
`
|
523
|
+
`i32 __Porffor_readArgv(u32 index, u32 outPtr) {
|
478
524
|
if (index >= _argc) {
|
479
|
-
|
480
|
-
exit(1);
|
525
|
+
return -1;
|
481
526
|
}
|
482
527
|
|
483
528
|
char* arg = _argv[index];
|
@@ -489,24 +534,25 @@ _time_out = _time.tv_nsec / 1000000. + _time.tv_sec * 1000.;`);
|
|
489
534
|
out[read++] = ch;
|
490
535
|
}
|
491
536
|
|
492
|
-
|
537
|
+
*((i32*)(_memory + outPtr)) = (i32)read;
|
538
|
+
return read;
|
493
539
|
}`);
|
494
540
|
|
495
|
-
|
496
|
-
vals.pop();
|
541
|
+
const outPtr = vals.pop();
|
542
|
+
const index = vals.pop();
|
543
|
+
vals.push(`(f64)__Porffor_readArgv((u32)(${index}), (u32)(${outPtr}))`);
|
497
544
|
break;
|
545
|
+
}
|
498
546
|
|
499
|
-
case '__Porffor_readFile':
|
547
|
+
case '__Porffor_readFile': {
|
500
548
|
includes.set('stdio.h', true);
|
501
|
-
includes.set('stdlib.h', true);
|
502
549
|
|
503
550
|
prepend.set('__Porffor_readFile',
|
504
|
-
`
|
551
|
+
`i32 __Porffor_readFile(u32 pathPtr, u32 outPtr) {
|
505
552
|
char* path = _memory + pathPtr + 4;
|
506
553
|
FILE* fp = fopen(path, "r");
|
507
554
|
if (fp == NULL) {
|
508
|
-
|
509
|
-
exit(1);
|
555
|
+
return -1;
|
510
556
|
}
|
511
557
|
|
512
558
|
u32 read = 0;
|
@@ -518,11 +564,14 @@ _time_out = _time.tv_nsec / 1000000. + _time.tv_sec * 1000.;`);
|
|
518
564
|
|
519
565
|
fclose(fp);
|
520
566
|
|
521
|
-
|
567
|
+
*((i32*)(_memory + outPtr)) = (i32)read;
|
568
|
+
return read;
|
522
569
|
}`);
|
523
|
-
|
524
|
-
vals.pop();
|
570
|
+
const outPtr = vals.pop();
|
571
|
+
const pathPtr = vals.pop();
|
572
|
+
vals.push(`(f64)__Porffor_readFile((u32)(${pathPtr}), (u32)(${outPtr}))`);
|
525
573
|
break;
|
574
|
+
}
|
526
575
|
|
527
576
|
default:
|
528
577
|
log.warning('2c', `unimplemented import: ${importFunc.name}`);
|
@@ -539,9 +588,10 @@ _time_out = _time.tv_nsec / 1000000. + _time.tv_sec * 1000.;`);
|
|
539
588
|
if (func.internal) {
|
540
589
|
vals.push(`${sanitize(func.name)}(${args.join(', ')})`);
|
541
590
|
} else {
|
542
|
-
|
543
|
-
|
544
|
-
vals.push(`_.
|
591
|
+
const id = retTmpId++;
|
592
|
+
line(`const struct ReturnValue _${id} = ${sanitize(func.name)}(${args.join(', ')})`);
|
593
|
+
vals.push(`_${id}.value`);
|
594
|
+
vals.push(`_${id}.type`);
|
545
595
|
}
|
546
596
|
} else line(`${sanitize(func.name)}(${args.join(', ')})`);
|
547
597
|
|
@@ -571,7 +621,7 @@ _time_out = _time.tv_nsec / 1000000. + _time.tv_sec * 1000.;`);
|
|
571
621
|
|
572
622
|
let cond = removeBrackets(vals.pop());
|
573
623
|
if (!lastCond) {
|
574
|
-
if (cond.startsWith(`(
|
624
|
+
if (cond.startsWith(`(i32)`)) cond = `${cond.slice(5)} != 0e+0`;
|
575
625
|
else cond = `(${cond}) != 0`;
|
576
626
|
}
|
577
627
|
|
@@ -601,8 +651,7 @@ _time_out = _time.tv_nsec / 1000000. + _time.tv_sec * 1000.;`);
|
|
601
651
|
const name = invOpcodes[i[0]];
|
602
652
|
const func = CMemFuncs[i[0]];
|
603
653
|
if (!prepend.has(name)) {
|
604
|
-
prepend.set(name, `${func.returns || 'void'} ${name}(
|
605
|
-
// generate func c and prepend
|
654
|
+
prepend.set(name, `${func.returns || 'void'} ${name}(i32 align, i32 offset, ${func.args.map((x, i) => `${func.argTypes[i]} ${x}`).join(', ')}) {\n ${func.c.replaceAll('\n', '\n ')}\n}\n`);
|
606
655
|
}
|
607
656
|
|
608
657
|
const immediates = [ i[1], read_unsignedLEB128(i.slice(2)) ];
|
@@ -637,7 +686,6 @@ _time_out = _time.tv_nsec / 1000000. + _time.tv_sec * 1000.;`);
|
|
637
686
|
depth = 0;
|
638
687
|
|
639
688
|
const makeIncludes = includes => [...includes.keys()].map(x => `#include <${x}>\n`).join('');
|
640
|
-
|
641
689
|
out = platformSpecific(makeIncludes(winIncludes), makeIncludes(unixIncludes), false) + '\n' + makeIncludes(includes) + '\n' + alwaysPreface + [...prepend.values()].join('\n') + '\n\n' + out;
|
642
690
|
|
643
691
|
return out.trim();
|
@@ -0,0 +1,128 @@
|
|
1
|
+
import { Opcodes, PageSize, Valtype } from './wasmSpec.js';
|
2
|
+
import { number } from './embedding.js';
|
3
|
+
import Prefs from './prefs.js';
|
4
|
+
|
5
|
+
// we currently have 3 allocators:
|
6
|
+
// - static (default): a static/compile-time allocator. fast (no grow/run-time alloc needed) but can break some code
|
7
|
+
// - grow: perform a memory.grow every allocation. simple but maybe slow?
|
8
|
+
// - chunk: perform large memory.grow's in chunks when needed. needs investigation
|
9
|
+
|
10
|
+
export default name => {
|
11
|
+
switch (name) {
|
12
|
+
case 'static': return new StaticAllocator();
|
13
|
+
case 'grow': return new GrowAllocator();
|
14
|
+
case 'chunk': return new ChunkAllocator();
|
15
|
+
default: throw new Error(`unknown allocator: ${name}`);
|
16
|
+
}
|
17
|
+
};
|
18
|
+
|
19
|
+
export class StaticAllocator {
|
20
|
+
constructor() {
|
21
|
+
}
|
22
|
+
|
23
|
+
allocType(itemType) {
|
24
|
+
switch (itemType) {
|
25
|
+
case 'i8': return 'bytestring';
|
26
|
+
case 'i16': return 'string';
|
27
|
+
|
28
|
+
default: return 'array';
|
29
|
+
}
|
30
|
+
}
|
31
|
+
|
32
|
+
ptr(ind) {
|
33
|
+
if (ind === 0) return 4;
|
34
|
+
return ind * PageSize;
|
35
|
+
}
|
36
|
+
|
37
|
+
alloc({ scope, pages }, name, { itemType }) {
|
38
|
+
const reason = `${this.allocType(itemType)}: ${Prefs.scopedPageNames ? (scope.name + '/') : ''}${name}`;
|
39
|
+
|
40
|
+
if (pages.has(reason)) return number(this.ptr(pages.get(reason).ind), Valtype.i32);
|
41
|
+
|
42
|
+
if (reason.startsWith('array:')) pages.hasArray = true;
|
43
|
+
if (reason.startsWith('string:')) pages.hasString = true;
|
44
|
+
if (reason.startsWith('bytestring:')) pages.hasByteString = true;
|
45
|
+
if (reason.includes('string:')) pages.hasAnyString = true;
|
46
|
+
|
47
|
+
let ind = pages.size;
|
48
|
+
pages.set(reason, { ind, type: itemType });
|
49
|
+
|
50
|
+
scope.pages ??= new Map();
|
51
|
+
scope.pages.set(reason, { ind, type: itemType });
|
52
|
+
|
53
|
+
return number(this.ptr(ind), Valtype.i32);
|
54
|
+
}
|
55
|
+
}
|
56
|
+
|
57
|
+
export class GrowAllocator {
|
58
|
+
constructor() {
|
59
|
+
Prefs.rmUnusedTypes = false;
|
60
|
+
}
|
61
|
+
|
62
|
+
alloc() {
|
63
|
+
return [
|
64
|
+
// grow by 1 page
|
65
|
+
[ Opcodes.i32_const, 1 ],
|
66
|
+
[ Opcodes.memory_grow, 0 ], // returns old page count
|
67
|
+
|
68
|
+
// get ptr (page count * page size)
|
69
|
+
number(65536, Valtype.i32)[0],
|
70
|
+
[ Opcodes.i32_mul ]
|
71
|
+
];
|
72
|
+
}
|
73
|
+
}
|
74
|
+
|
75
|
+
export class ChunkAllocator {
|
76
|
+
constructor(chunkSize) {
|
77
|
+
Prefs.rmUnusedTypes = false;
|
78
|
+
|
79
|
+
// 64KiB * chunk size each growth
|
80
|
+
// 16: 1MiB chunks
|
81
|
+
this.chunkSize = chunkSize ?? Prefs.chunkAllocatorSize ?? 16;
|
82
|
+
}
|
83
|
+
|
84
|
+
alloc({ asmFunc, funcIndex }) {
|
85
|
+
const func = funcIndex['#chunkallocator_alloc'] ?? asmFunc('#chunkallocator_alloc', {
|
86
|
+
wasm: [
|
87
|
+
[ Opcodes.global_get, 0 ],
|
88
|
+
[ Opcodes.global_get, 1 ],
|
89
|
+
[ Opcodes.i32_ge_s ],
|
90
|
+
[ Opcodes.if, Valtype.i32 ], // ptr >= next
|
91
|
+
// grow by chunk size pages
|
92
|
+
[ Opcodes.i32_const, this.chunkSize ],
|
93
|
+
[ Opcodes.memory_grow, 0 ],
|
94
|
+
|
95
|
+
// ptr = prev memory size * PageSize
|
96
|
+
number(65536, Valtype.i32)[0],
|
97
|
+
[ Opcodes.i32_mul ],
|
98
|
+
[ Opcodes.global_set, 0 ],
|
99
|
+
|
100
|
+
// next = ptr + ((chunkSize - 1) * PageSize)
|
101
|
+
[ Opcodes.global_get, 0 ],
|
102
|
+
number(65536 * (this.chunkSize - 1), Valtype.i32)[0],
|
103
|
+
[ Opcodes.i32_add ],
|
104
|
+
[ Opcodes.global_set, 1 ],
|
105
|
+
|
106
|
+
// return ptr
|
107
|
+
[ Opcodes.global_get, 0 ],
|
108
|
+
[ Opcodes.else ],
|
109
|
+
// return ptr = ptr + PageSize
|
110
|
+
[ Opcodes.global_get, 0 ],
|
111
|
+
number(65536, Valtype.i32)[0],
|
112
|
+
[ Opcodes.i32_add ],
|
113
|
+
[ Opcodes.global_set, 0 ],
|
114
|
+
[ Opcodes.global_get, 0 ],
|
115
|
+
[ Opcodes.end ],
|
116
|
+
],
|
117
|
+
params: [],
|
118
|
+
locals: [],
|
119
|
+
globals: [ Valtype.i32, Valtype.i32 ],
|
120
|
+
globalNames: ['#chunkallocator_ptr', '#chunkallocator_next'],
|
121
|
+
returns: [ Valtype.i32 ],
|
122
|
+
}).index;
|
123
|
+
|
124
|
+
return [
|
125
|
+
[ Opcodes.call, func ]
|
126
|
+
];
|
127
|
+
}
|
128
|
+
}
|
package/compiler/assemble.js
CHANGED
@@ -21,7 +21,7 @@ const chHint = (topTier, baselineTier, strategy) => {
|
|
21
21
|
return (strategy | (baselineTier << 2) | (topTier << 4));
|
22
22
|
};
|
23
23
|
|
24
|
-
export default (funcs, globals, tags, pages, data, flags) => {
|
24
|
+
export default (funcs, globals, tags, pages, data, flags, noTreeshake = false) => {
|
25
25
|
const types = [], typeCache = {};
|
26
26
|
|
27
27
|
const optLevel = parseInt(process.argv.find(x => x.startsWith('-O'))?.[2] ?? 1);
|
@@ -44,7 +44,7 @@ export default (funcs, globals, tags, pages, data, flags) => {
|
|
44
44
|
|
45
45
|
let importFuncs = [];
|
46
46
|
|
47
|
-
if (optLevel < 1 || !Prefs.treeshakeWasmImports) {
|
47
|
+
if (optLevel < 1 || !Prefs.treeshakeWasmImports || noTreeshake) {
|
48
48
|
importFuncs = importedFuncs;
|
49
49
|
} else {
|
50
50
|
let imports = new Map();
|
@@ -94,7 +94,7 @@ export default (funcs, globals, tags, pages, data, flags) => {
|
|
94
94
|
|
95
95
|
const importSection = importFuncs.length === 0 ? [] : createSection(
|
96
96
|
Section.import,
|
97
|
-
encodeVector(importFuncs.map(x => [ 0, ...encodeString(x.import), ExportDesc.func, getType(
|
97
|
+
encodeVector(importFuncs.map(x => [ 0, ...encodeString(x.import), ExportDesc.func, getType(typeof x.params === 'object' ? x.params : new Array(x.params).fill(valtypeBinary), new Array(x.returns).fill(valtypeBinary)) ]))
|
98
98
|
);
|
99
99
|
|
100
100
|
const funcSection = createSection(
|
@@ -116,7 +116,7 @@ export default (funcs, globals, tags, pages, data, flags) => {
|
|
116
116
|
] ])
|
117
117
|
);
|
118
118
|
|
119
|
-
if (pages.has('func argc lut')) {
|
119
|
+
if (pages.has('func argc lut') && !data.addedFuncArgcLut) {
|
120
120
|
// generate func argc lut data
|
121
121
|
const bytes = [];
|
122
122
|
for (let i = 0; i < funcs.length; i++) {
|
@@ -128,6 +128,7 @@ export default (funcs, globals, tags, pages, data, flags) => {
|
|
128
128
|
offset: pages.get('func argc lut').ind * pageSize,
|
129
129
|
bytes
|
130
130
|
});
|
131
|
+
data.addedFuncArgcLut = true;
|
131
132
|
}
|
132
133
|
|
133
134
|
// const t0 = performance.now();
|
@@ -239,7 +240,13 @@ export default (funcs, globals, tags, pages, data, flags) => {
|
|
239
240
|
|
240
241
|
const dataSection = data.length === 0 ? [] : createSection(
|
241
242
|
Section.data,
|
242
|
-
encodeVector(data.map(x =>
|
243
|
+
encodeVector(data.map(x => {
|
244
|
+
// type: active
|
245
|
+
if (x.offset != null) return [ 0x00, Opcodes.i32_const, ...signedLEB128(x.offset), Opcodes.end, ...encodeVector(x.bytes) ];
|
246
|
+
|
247
|
+
// type: passive
|
248
|
+
return [ 0x01, ...encodeVector(x.bytes) ];
|
249
|
+
}))
|
243
250
|
);
|
244
251
|
|
245
252
|
const dataCountSection = data.length === 0 ? [] : createSection(
|