porffor 0.16.0-fe07da0f4 → 0.17.0-05070e1f0
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 +146 -81
- package/compiler/allocators.js +128 -0
- package/compiler/assemble.js +12 -5
- package/compiler/builtins/array.ts +94 -5
- package/compiler/builtins/base64.ts +28 -24
- package/compiler/builtins/date.ts +3 -30
- package/compiler/builtins/math.ts +6 -2
- package/compiler/builtins/number.ts +10 -21
- package/compiler/builtins/porffor.d.ts +10 -0
- package/compiler/builtins/set.ts +9 -14
- package/compiler/builtins/string_f64.ts +10 -0
- package/compiler/builtins/typedarray.js +42 -0
- package/compiler/builtins/z_ecma262.ts +62 -0
- package/compiler/builtins.js +51 -6
- package/compiler/codegen.js +824 -409
- package/compiler/cyclone.js +535 -0
- package/compiler/decompile.js +3 -1
- package/compiler/generated_builtins.js +420 -162
- 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 +220 -0
- package/compiler/precompile.js +12 -7
- package/compiler/prefs.js +8 -4
- package/compiler/prototype.js +34 -43
- package/compiler/types.js +31 -5
- package/compiler/wasmSpec.js +4 -2
- package/compiler/wrap.js +120 -21
- package/package.json +3 -5
- package/rhemyn/README.md +7 -4
- package/rhemyn/compile.js +138 -66
- package/runner/debug.js +1 -1
- package/runner/index.js +31 -14
- package/runner/{profiler.js → profile.js} +1 -1
- package/runner/repl.js +16 -11
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})`;
|
@@ -130,7 +182,15 @@ export default ({ funcs, globals, tags, data, exceptions, pages }) => {
|
|
130
182
|
}, {});
|
131
183
|
const invGlobals = inv(globals, x => x.idx);
|
132
184
|
|
133
|
-
const
|
185
|
+
const codeToSanitizedStr = code => {
|
186
|
+
let out = '';
|
187
|
+
while (code > 0) {
|
188
|
+
out += String.fromCharCode(97 + code % 26);
|
189
|
+
code -= 26;
|
190
|
+
}
|
191
|
+
return out;
|
192
|
+
};
|
193
|
+
const sanitize = str => str.replace(/[^0-9a-zA-Z_]/g, _ => codeToSanitizedStr(_.charCodeAt(0)));
|
134
194
|
|
135
195
|
for (const x in invGlobals) {
|
136
196
|
invGlobals[x] = sanitize(invGlobals[x]);
|
@@ -152,11 +212,16 @@ export default ({ funcs, globals, tags, data, exceptions, pages }) => {
|
|
152
212
|
|
153
213
|
if (pages.size > 0) {
|
154
214
|
prepend.set('_memory', `char _memory[${pages.size * pageSize}];\n`);
|
155
|
-
includes.set('string.h', true);
|
215
|
+
if (Prefs['2cMemcpy']) includes.set('string.h', true);
|
156
216
|
}
|
157
217
|
|
158
218
|
if (data.length > 0) {
|
159
|
-
|
219
|
+
if (Prefs['2cMemcpy']) {
|
220
|
+
prependMain.set('_data', data.map(x => `memcpy(_memory + ${x.offset}, (unsigned char[]){${x.bytes.join(',')}}, ${x.bytes.length});`).join('\n '));
|
221
|
+
includes.set('string.h', true);
|
222
|
+
} else {
|
223
|
+
prependMain.set('_data', data.map(x => x.bytes.reduce((acc, y, i) => acc + `_memory[${x.offset + i}]=(u8)${y};`, '')).join('\n '));
|
224
|
+
}
|
160
225
|
}
|
161
226
|
|
162
227
|
if (importFuncs.find(x => x.name === '__Porffor_readArgv')) {
|
@@ -168,10 +233,10 @@ export default ({ funcs, globals, tags, data, exceptions, pages }) => {
|
|
168
233
|
|
169
234
|
let depth = 1;
|
170
235
|
let brDepth = 0;
|
171
|
-
const line = (str, semi = true) => out += `${' '.repeat(depth
|
236
|
+
const line = (str, semi = true) => out += `${' '.repeat((depth + brDepth) * 2)}${str}${semi ? ';' : ''}\n`;
|
172
237
|
const lines = lines => {
|
173
238
|
for (const x of lines) {
|
174
|
-
out += `${' '.repeat(depth * 2)}${x}\n`;
|
239
|
+
out += `${' '.repeat((depth + brDepth) * 2)}${x}\n`;
|
175
240
|
}
|
176
241
|
};
|
177
242
|
|
@@ -216,13 +281,14 @@ export default ({ funcs, globals, tags, data, exceptions, pages }) => {
|
|
216
281
|
}
|
217
282
|
|
218
283
|
const returns = f.returns.length > 0;
|
284
|
+
const typedReturns = f.returnType == null;
|
219
285
|
|
220
286
|
const shouldInline = false; // f.internal;
|
221
287
|
if (f.name === 'main') out += `int main(${prependMain.has('argv') ? 'int argc, char* argv[]' : ''}) {\n`;
|
222
|
-
else out += `${
|
288
|
+
else out += `${!typedReturns ? (returns ? CValtype[f.returns[0]] : 'void') : 'struct ReturnValue'} ${shouldInline ? 'inline ' : ''}${sanitize(f.name)}(${f.params.map((x, i) => `${CValtype[x]} ${invLocals[i]}`).join(', ')}) {\n`;
|
223
289
|
|
224
290
|
if (f.name === 'main') {
|
225
|
-
out += [...prependMain.values()].join('\n');
|
291
|
+
out += ' ' + [...prependMain.values()].join('\n ');
|
226
292
|
if (prependMain.size > 0) out += '\n\n';
|
227
293
|
}
|
228
294
|
|
@@ -283,12 +349,12 @@ export default ({ funcs, globals, tags, data, exceptions, pages }) => {
|
|
283
349
|
switch (i[1]) {
|
284
350
|
// i32_trunc_sat_f64_s
|
285
351
|
case 0x02:
|
286
|
-
vals.push(`(
|
352
|
+
vals.push(`(i32)(${vals.pop()})`);
|
287
353
|
break;
|
288
354
|
|
289
355
|
// i32_trunc_sat_f64_u
|
290
356
|
case 0x03:
|
291
|
-
vals.push(`(
|
357
|
+
vals.push(`(u32)(${vals.pop()})`);
|
292
358
|
break;
|
293
359
|
}
|
294
360
|
|
@@ -337,7 +403,7 @@ export default ({ funcs, globals, tags, data, exceptions, pages }) => {
|
|
337
403
|
|
338
404
|
case Opcodes.f64_trunc:
|
339
405
|
// vals.push(`trunc(${vals.pop()})`);
|
340
|
-
vals.push(`(
|
406
|
+
vals.push(`(i32)(${removeBrackets(vals.pop())})`); // this is ~10x faster with clang??
|
341
407
|
break;
|
342
408
|
|
343
409
|
case Opcodes.f64_convert_i32_u:
|
@@ -345,7 +411,7 @@ export default ({ funcs, globals, tags, data, exceptions, pages }) => {
|
|
345
411
|
case Opcodes.f64_convert_i64_u:
|
346
412
|
case Opcodes.f64_convert_i64_s:
|
347
413
|
// int to f64
|
348
|
-
vals.push(`(
|
414
|
+
vals.push(`(f64)(${removeBrackets(vals.pop())})`);
|
349
415
|
break;
|
350
416
|
|
351
417
|
case Opcodes.i32_eqz:
|
@@ -353,7 +419,7 @@ export default ({ funcs, globals, tags, data, exceptions, pages }) => {
|
|
353
419
|
vals.push(`!(${removeBrackets(vals.pop())})`);
|
354
420
|
} else {
|
355
421
|
let cond = '(' + removeBrackets(vals.pop());
|
356
|
-
if (cond.startsWith(`(
|
422
|
+
if (cond.startsWith(`(i32)`)) cond = `${cond.slice(5)} == 0e+0`;
|
357
423
|
else cond += ') == 0';
|
358
424
|
vals.push(cond);
|
359
425
|
}
|
@@ -361,14 +427,20 @@ export default ({ funcs, globals, tags, data, exceptions, pages }) => {
|
|
361
427
|
continue;
|
362
428
|
|
363
429
|
case Opcodes.return:
|
364
|
-
|
365
|
-
|
430
|
+
if (!typedReturns) {
|
431
|
+
line(`return${returns ? ` ${removeBrackets(vals.pop())}` : ''}`);
|
432
|
+
break;
|
433
|
+
}
|
434
|
+
|
435
|
+
const b = returns ? vals.pop() : -1;
|
436
|
+
const a = returns ? vals.pop() : -1;
|
437
|
+
line(`return${returns ? ` (struct ReturnValue){ ${removeBrackets(a)}, ${removeBrackets(b)} }` : ''}`);
|
366
438
|
break;
|
367
439
|
|
368
440
|
case Opcodes.if: {
|
369
441
|
let cond = removeBrackets(vals.pop());
|
370
442
|
if (!lastCond) {
|
371
|
-
if (cond.startsWith(`(
|
443
|
+
if (cond.startsWith(`(i32)`)) cond = `${cond.slice(5)} != 0e+0`;
|
372
444
|
else cond = `(${cond}) != 0`;
|
373
445
|
}
|
374
446
|
|
@@ -434,28 +506,16 @@ export default ({ funcs, globals, tags, data, exceptions, pages }) => {
|
|
434
506
|
const importFunc = importFuncs[i[1]];
|
435
507
|
switch (importFunc.name) {
|
436
508
|
case 'print':
|
437
|
-
// line(`printf("%f\\n", ${vals.pop()})`);
|
438
509
|
line(`printf("${valtype === 'f64' ? '%g' : '%i'}\\n", ${vals.pop()})`);
|
439
510
|
includes.set('stdio.h', true);
|
440
511
|
break;
|
441
512
|
case 'printChar':
|
442
|
-
line(`
|
513
|
+
line(`putchar((int)(${vals.pop()}))`);
|
443
514
|
includes.set('stdio.h', true);
|
444
515
|
break;
|
445
516
|
|
446
517
|
case 'time':
|
447
518
|
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
519
|
platformSpecific(
|
460
520
|
`LARGE_INTEGER _time_freq, _time_t;
|
461
521
|
QueryPerformanceFrequency(&_time_freq);
|
@@ -470,14 +530,11 @@ _time_out = _time.tv_nsec / 1000000. + _time.tv_sec * 1000.;`);
|
|
470
530
|
winIncludes.set('windows.h', true);
|
471
531
|
break;
|
472
532
|
|
473
|
-
case '__Porffor_readArgv':
|
474
|
-
includes.set('stdlib.h', true);
|
475
|
-
|
533
|
+
case '__Porffor_readArgv': {
|
476
534
|
prepend.set('__Porffor_readArgv',
|
477
|
-
`
|
535
|
+
`i32 __Porffor_readArgv(u32 index, u32 outPtr) {
|
478
536
|
if (index >= _argc) {
|
479
|
-
|
480
|
-
exit(1);
|
537
|
+
return -1;
|
481
538
|
}
|
482
539
|
|
483
540
|
char* arg = _argv[index];
|
@@ -489,24 +546,30 @@ _time_out = _time.tv_nsec / 1000000. + _time.tv_sec * 1000.;`);
|
|
489
546
|
out[read++] = ch;
|
490
547
|
}
|
491
548
|
|
492
|
-
|
549
|
+
*((i32*)(_memory + outPtr)) = (i32)read;
|
550
|
+
return read;
|
493
551
|
}`);
|
494
552
|
|
495
|
-
|
496
|
-
vals.pop();
|
553
|
+
const outPtr = vals.pop();
|
554
|
+
const index = vals.pop();
|
555
|
+
vals.push(`(f64)__Porffor_readArgv((u32)(${index}), (u32)(${outPtr}))`);
|
497
556
|
break;
|
557
|
+
}
|
498
558
|
|
499
|
-
case '__Porffor_readFile':
|
559
|
+
case '__Porffor_readFile': {
|
500
560
|
includes.set('stdio.h', true);
|
501
|
-
includes.set('stdlib.h', true);
|
502
561
|
|
503
562
|
prepend.set('__Porffor_readFile',
|
504
|
-
`
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
563
|
+
`i32 __Porffor_readFile(u32 pathPtr, u32 outPtr) {
|
564
|
+
FILE* fp;
|
565
|
+
if (pathPtr == 0) {
|
566
|
+
fp = stdin;
|
567
|
+
} else {
|
568
|
+
char* path = _memory + pathPtr + 4;
|
569
|
+
fp = fopen(path, "r");
|
570
|
+
if (fp == NULL) {
|
571
|
+
return -1;
|
572
|
+
}
|
510
573
|
}
|
511
574
|
|
512
575
|
u32 read = 0;
|
@@ -518,11 +581,14 @@ _time_out = _time.tv_nsec / 1000000. + _time.tv_sec * 1000.;`);
|
|
518
581
|
|
519
582
|
fclose(fp);
|
520
583
|
|
521
|
-
|
584
|
+
*((i32*)(_memory + outPtr)) = (i32)read;
|
585
|
+
return read;
|
522
586
|
}`);
|
523
|
-
|
524
|
-
vals.pop();
|
587
|
+
const outPtr = vals.pop();
|
588
|
+
const pathPtr = vals.pop();
|
589
|
+
vals.push(`(f64)__Porffor_readFile((u32)(${pathPtr}), (u32)(${outPtr}))`);
|
525
590
|
break;
|
591
|
+
}
|
526
592
|
|
527
593
|
default:
|
528
594
|
log.warning('2c', `unimplemented import: ${importFunc.name}`);
|
@@ -536,12 +602,13 @@ _time_out = _time.tv_nsec / 1000000. + _time.tv_sec * 1000.;`);
|
|
536
602
|
for (let j = 0; j < func.params.length; j++) args.unshift(removeBrackets(vals.pop()));
|
537
603
|
|
538
604
|
if (func.returns.length > 0) {
|
539
|
-
if (func.
|
605
|
+
if (func.returnType != null) {
|
540
606
|
vals.push(`${sanitize(func.name)}(${args.join(', ')})`);
|
541
607
|
} else {
|
542
|
-
|
543
|
-
|
544
|
-
vals.push(`_.
|
608
|
+
const id = retTmpId++;
|
609
|
+
line(`const struct ReturnValue _${id} = ${sanitize(func.name)}(${args.join(', ')})`);
|
610
|
+
vals.push(`_${id}.value`);
|
611
|
+
vals.push(`_${id}.type`);
|
545
612
|
}
|
546
613
|
} else line(`${sanitize(func.name)}(${args.join(', ')})`);
|
547
614
|
|
@@ -571,7 +638,7 @@ _time_out = _time.tv_nsec / 1000000. + _time.tv_sec * 1000.;`);
|
|
571
638
|
|
572
639
|
let cond = removeBrackets(vals.pop());
|
573
640
|
if (!lastCond) {
|
574
|
-
if (cond.startsWith(`(
|
641
|
+
if (cond.startsWith(`(i32)`)) cond = `${cond.slice(5)} != 0e+0`;
|
575
642
|
else cond = `(${cond}) != 0`;
|
576
643
|
}
|
577
644
|
|
@@ -601,8 +668,7 @@ _time_out = _time.tv_nsec / 1000000. + _time.tv_sec * 1000.;`);
|
|
601
668
|
const name = invOpcodes[i[0]];
|
602
669
|
const func = CMemFuncs[i[0]];
|
603
670
|
if (!prepend.has(name)) {
|
604
|
-
prepend.set(name, `${func.returns || 'void'} ${name}(
|
605
|
-
// generate func c and prepend
|
671
|
+
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
672
|
}
|
607
673
|
|
608
674
|
const immediates = [ i[1], read_unsignedLEB128(i.slice(2)) ];
|
@@ -637,7 +703,6 @@ _time_out = _time.tv_nsec / 1000000. + _time.tv_sec * 1000.;`);
|
|
637
703
|
depth = 0;
|
638
704
|
|
639
705
|
const makeIncludes = includes => [...includes.keys()].map(x => `#include <${x}>\n`).join('');
|
640
|
-
|
641
706
|
out = platformSpecific(makeIncludes(winIncludes), makeIncludes(unixIncludes), false) + '\n' + makeIncludes(includes) + '\n' + alwaysPreface + [...prepend.values()].join('\n') + '\n\n' + out;
|
642
707
|
|
643
708
|
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
|
+
}
|