porffor 0.2.0-fdf0fc5 โ 0.14.0-032e4ad08
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 +9 -3
- package/README.md +17 -14
- package/asur/index.js +1 -1
- package/byg/index.js +3 -24
- package/compiler/2c.js +66 -54
- package/compiler/assemble.js +51 -11
- package/compiler/builtins/annexb_string.js +10 -10
- package/compiler/builtins/annexb_string.ts +4 -3
- package/compiler/builtins/array.ts +85 -9
- package/compiler/builtins/base64.ts +2 -1
- package/compiler/builtins/boolean.ts +2 -2
- package/compiler/builtins/crypto.ts +2 -1
- package/compiler/builtins/date.ts +2 -3
- package/compiler/builtins/error.js +22 -0
- package/compiler/builtins/escape.ts +2 -3
- package/compiler/builtins/function.ts +1 -1
- package/compiler/builtins/int.ts +1 -1
- package/compiler/builtins/math.ts +410 -0
- package/compiler/builtins/number.ts +4 -7
- package/compiler/builtins/object.ts +1 -1
- package/compiler/builtins/porffor.d.ts +9 -8
- package/compiler/builtins/set.ts +197 -3
- package/compiler/builtins/string.ts +2 -1
- package/compiler/builtins/symbol.ts +62 -0
- package/compiler/builtins.js +41 -15
- package/compiler/codegen.js +653 -366
- package/compiler/decompile.js +3 -3
- package/compiler/embedding.js +2 -2
- package/compiler/encoding.js +0 -14
- package/compiler/expression.js +1 -1
- package/compiler/generated_builtins.js +771 -187
- package/compiler/index.js +5 -11
- package/compiler/opt.js +7 -7
- package/compiler/parse.js +2 -4
- package/compiler/precompile.js +18 -25
- package/compiler/prefs.js +6 -2
- package/compiler/prototype.js +185 -162
- package/compiler/wasmSpec.js +5 -0
- package/compiler/wrap.js +150 -90
- package/package.json +1 -1
- package/porffor_tmp.c +202 -0
- package/runner/compare.js +0 -1
- package/runner/debug.js +1 -6
- package/runner/index.js +5 -4
- package/runner/profiler.js +15 -42
- package/runner/repl.js +20 -10
- package/runner/sizes.js +2 -2
- package/runner/version.js +10 -8
package/CONTRIBUTING.md
CHANGED
@@ -198,11 +198,13 @@ Store the character code into the `out` pointer variable, and increment it.
|
|
198
198
|
|
199
199
|
## Porffor-specific TS notes
|
200
200
|
|
201
|
-
- For declaring variables, you must use explicit type annotations currently (eg `let a: number = 1`, not `let a = 1`)
|
201
|
+
- For declaring variables, you must use explicit type annotations currently (eg `let a: number = 1`, not `let a = 1`).
|
202
202
|
- You might spot `Porffor.fastOr`/`Porffor.fastAnd`, these are non-short circuiting versions of `||`/`&&`, taking any number of conditions as arguments. You shouldn't don't need to use or worry about these.
|
203
|
-
- **There are ~no objects, you cannot use them
|
203
|
+
- **There are ~no objects, you cannot use them.**
|
204
204
|
- Attempt to avoid string/array-heavy code and use more variables instead if possible, easier on memory and CPU/perf.
|
205
205
|
- Do not set a return type for prototype methods, it can cause errors/unexpected results.
|
206
|
+
- You cannot use other functions in the file not exported, or variables not inside the current function.
|
207
|
+
- `if (...)` uses a fast truthy implementation which is not spec-compliant as most conditions should be strictly checked. To use spec-compliant behavior, use `if (Boolean(...))`.
|
206
208
|
|
207
209
|
<br>
|
208
210
|
|
@@ -231,7 +233,11 @@ builtins/tostring_number: impl radix
|
|
231
233
|
|
232
234
|
Make sure you have Test262 cloned already **inside of `test262/`** (`git clone https://github.com/tc39/test262.git test262/test262`) and run `npm install` inside `test262/` too.
|
233
235
|
|
234
|
-
Run `node test262` to run all the tests and get an output of total overall test results.
|
236
|
+
Run `node test262` to run all the tests and get an output of total overall test results.
|
237
|
+
|
238
|
+
Warning: this will consume 1-6GB of memory and ~90% of all CPU cores while running (depending on thread count), it should take 15-120s depending on machine. You can specify how many threads with `--threads=N`, it will use the number of CPU threads by default.
|
239
|
+
|
240
|
+
The main thing you want to pay attention to is the emoji summary (lol):
|
235
241
|
```
|
236
242
|
๐งช 50005 | ๐ค 7007 (-89) | โ 1914 (-32) | ๐ 13904 (-61) | ๐ 23477 (-120) | โฐ 2 | ๐ 2073 (+302) | ๐ฅ 1628
|
237
243
|
```
|
package/README.md
CHANGED
@@ -13,7 +13,7 @@ Porffor is primarily built from scratch, the only thing that is not is the parse
|
|
13
13
|
## Usage
|
14
14
|
Expect nothing to work! Only very limited JS is currently supported. See files in `bench` for examples.
|
15
15
|
|
16
|
-
###
|
16
|
+
### Install
|
17
17
|
**`npm install -g porffor`**. It's that easy (hopefully) :)
|
18
18
|
|
19
19
|
### Trying a REPL
|
@@ -189,6 +189,13 @@ These include some early (stage 1/0) and/or dead (last commit years ago) proposa
|
|
189
189
|
- Intrinsic functions (see below)
|
190
190
|
- Inlining wasm via ``asm`...``\` "macro"
|
191
191
|
|
192
|
+
## Versioning
|
193
|
+
Porffor uses a unique versioning system, here's an example: `0.14.0-15cb49f07`. Let's break it down:
|
194
|
+
1. `0` - major, always `0` as Porffor is not ready yet
|
195
|
+
2. `14` - minor, total Test262 pass percentage (floored to nearest int)
|
196
|
+
3. `0` - micro, always `0` as unused
|
197
|
+
4. `15cb49f07` - commit hash
|
198
|
+
|
192
199
|
## Performance
|
193
200
|
*For the features it supports most of the time*, Porffor is *blazingly fast* compared to most interpreters and common engines running without JIT. For those with JIT, it is usually slower by default, but can catch up with compiler arguments and typed input, even more so when compiling to native binaries.
|
194
201
|
|
@@ -220,7 +227,7 @@ Mostly for reducing size. I do not really care about compiler perf/time as long
|
|
220
227
|
- No tags if unused/optimized out
|
221
228
|
|
222
229
|
## Test262
|
223
|
-
Porffor can run Test262 via some hacks/transforms which remove unsupported features whilst still doing the same asserts (eg simpler error messages using literals only). It currently passes >
|
230
|
+
Porffor can run Test262 via some hacks/transforms which remove unsupported features whilst still doing the same asserts (eg simpler error messages using literals only). It currently passes >14% (see latest commit desc for latest and details). Use `node test262` to test, it will also show a difference of overall results between the last commit and current results.
|
224
231
|
|
225
232
|
## Codebase
|
226
233
|
- `compiler`: contains the compiler itself
|
@@ -259,8 +266,6 @@ Basically none right now (other than giving people headaches). Potential ideas:
|
|
259
266
|
No particular order and no guarentees, just what could happen soonโข
|
260
267
|
|
261
268
|
- Arrays
|
262
|
-
- More of `Array` prototype
|
263
|
-
- Arrays/strings inside arrays
|
264
269
|
- Destructuring
|
265
270
|
- Objects
|
266
271
|
- Basic object expressions (eg `{}`, `{ a: 0 }`)
|
@@ -308,16 +313,10 @@ Porffor intentionally does not use Wasm proposals which are not commonly impleme
|
|
308
313
|
|
309
314
|
- Multi-value **(required)**
|
310
315
|
- Non-trapping float-to-int conversions **(required)**
|
311
|
-
- Bulk memory operations (
|
312
|
-
- Exception handling (optional, for errors)
|
316
|
+
- Bulk memory operations (optional, can get away without sometimes)
|
317
|
+
- Exception handling (optional, only for errors)
|
313
318
|
- Tail calls (opt-in, off by default)
|
314
319
|
|
315
|
-
## Isn't this the same as AssemblyScript/other Wasm langs?
|
316
|
-
No. they are not alike at all internally and have very different goals/ideals:
|
317
|
-
- Porffor is made as a generic JS engine, not for Wasm stuff specifically
|
318
|
-
- Porffor primarily consumes JS
|
319
|
-
- Porffor is written in pure JS and compiles itself, not using Binaryen/etc
|
320
|
-
- (Also I didn't know it existed when I started this, lol)
|
321
320
|
|
322
321
|
## FAQ
|
323
322
|
|
@@ -331,5 +330,9 @@ No. they are not alike at all internally and have very different goals/ideals:
|
|
331
330
|
### 2. Why at all?
|
332
331
|
Yes!
|
333
332
|
|
334
|
-
|
335
|
-
|
333
|
+
## 3. Isn't this the same as AssemblyScript/other Wasm langs?
|
334
|
+
No. they are not alike at all internally and have very different goals/ideals:
|
335
|
+
- Porffor is made as a generic JS engine, not for Wasm stuff specifically
|
336
|
+
- Porffor primarily consumes JS
|
337
|
+
- Porffor is written in pure JS and compiles itself, not using Binaryen/etc
|
338
|
+
- (Also I didn't know it existed when I started this, lol)
|
package/asur/index.js
CHANGED
@@ -1155,7 +1155,7 @@ if (bc.porfFunc && paused && op) {
|
|
1155
1155
|
switch (byg(
|
1156
1156
|
paused,
|
1157
1157
|
funcLines[currentFunc] + currentLine,
|
1158
|
-
'\x1b[1masur\x1b[22m: ' + callStack.join(' -> ') + (parents.length > 1 ? \` | \${parents.slice(1).map(x => invOpcodes[x.opcode]).join(' -> ')}\` : ''),
|
1158
|
+
'\x1b[1masur debugger\x1b[22m: ' + callStack.join(' -> ') + (parents.length > 1 ? \` | \${parents.slice(1).map(x => invOpcodes[x.opcode]).join(' -> ')}\` : ''),
|
1159
1159
|
[
|
1160
1160
|
{
|
1161
1161
|
x: termWidth - 1 - width - 6,
|
package/byg/index.js
CHANGED
@@ -2,7 +2,6 @@ import fs from 'node:fs';
|
|
2
2
|
|
3
3
|
const noAnsi = s => s.replace(/\u001b\[[0-9]+m/g, '');
|
4
4
|
const printLine = (line, number, breakpoint = false, current = false, selected = false) => {
|
5
|
-
// console.log(`\x1b[${breakpoint ? (selected ? '106' : '46') : (selected ? '47' : '100')}m\x1b[${selected ? '30' : '97'}m${number.toFixed(0).padStart(4, ' ')}\x1b[${breakpoint ? (selected ? '96' : '36') : (selected ? '37' : '90')}m\x1b[${current ? '47' : '40'}mโ \x1b[${current ? '47' : '40'}m\x1b[${current ? '30' : '37'}m${current ? noAnsi(line) : line}\x1b[0K`);
|
6
5
|
console.log(`\x1b[${breakpoint ? (selected ? '43' : '103') : (selected ? '47' : '100')}m\x1b[${selected || breakpoint ? '30' : '97'}m${number.toFixed(0).padStart(4, ' ')}\x1b[${breakpoint ? (selected ? '33' : '93') : (selected ? '37' : '90')}m\x1b[${current ? '47' : '40'}mโ \x1b[${current ? '47' : '40'}m\x1b[${current ? '30' : '37'}m${current ? noAnsi(line) : line}\x1b[0K`);
|
7
6
|
};
|
8
7
|
|
@@ -13,13 +12,10 @@ const box = (x, y, width, height, title = '', content = [], color = ['90', '100'
|
|
13
12
|
|
14
13
|
// top
|
15
14
|
process.stdout.write(`\x1b[48m\x1b[${y + 1};${x + 1}H\x1b[${color[0]}m` + 'โ'.repeat(width));
|
16
|
-
|
17
15
|
// bottom
|
18
16
|
process.stdout.write(`\x1b[${y + height + 1};${x + 1}Hโ` + 'โ'.repeat(width - 1) + 'โ');
|
19
|
-
|
20
17
|
// left
|
21
18
|
process.stdout.write(`\x1b[${y + 1};${x + 1}Hโ` + '\x1b[1B\x1b[1Dโ'.repeat(height - 1));
|
22
|
-
|
23
19
|
// right
|
24
20
|
process.stdout.write(`\x1b[${y + 1};${x + width + 1}Hโ` + '\x1b[1B\x1b[1Dโ'.repeat(height - 1));
|
25
21
|
|
@@ -33,8 +29,6 @@ const box = (x, y, width, height, title = '', content = [], color = ['90', '100'
|
|
33
29
|
process.stdout.write(`\x1b[${y + 1};${x + 1}H\x1b[${color[1]}m` + ' '.repeat(width) + (`\x1b[1B\x1b[${width}D` + ' '.repeat(width)).repeat(Math.max(0, height - 1)));
|
34
30
|
|
35
31
|
// title
|
36
|
-
// if (title) process.stdout.write(`\x1b[${y + 1};${x + ((width - title.length) / 2 | 0) + 1}H\x1b[${color[1]}m\x1b[${color[2]}m\x1b[1m${title}\x1b[22m`);
|
37
|
-
// if (title) process.stdout.write(`\x1b[${y};${x}H\x1b[${color[3]}โ\x1b[${color[4]}m\x1b[${color[2]}m\x1b[1m${' '.repeat((width - title.length) / 2 | 0)}${title}${' '.repeat(width - (((width - title.length) / 2 | 0)) - title.length)}\x1b[0m\x1b[${color[4]}mโ`);
|
38
32
|
if (title) process.stdout.write(`\x1b[${y};${x}H\x1b[0m\x1b[${color[3]}mโ\x1b[${color[4]}m\x1b[${color[2]}m\x1b[1m${' '.repeat((width - title.length) / 2 | 0)}${title}${' '.repeat(width - (((width - title.length) / 2 | 0)) - title.length)}\x1b[0m\x1b[${color[3]}mโ`);
|
39
33
|
|
40
34
|
// content
|
@@ -66,8 +60,6 @@ export default ({ lines, pause, breakpoint }) => {
|
|
66
60
|
process.exit();
|
67
61
|
}
|
68
62
|
|
69
|
-
// process.stdout.write(s);
|
70
|
-
|
71
63
|
if (!paused) pause();
|
72
64
|
});
|
73
65
|
|
@@ -83,7 +75,6 @@ export default ({ lines, pause, breakpoint }) => {
|
|
83
75
|
|
84
76
|
process.on('exit', () => {
|
85
77
|
process.stdout.write('\x1b[0m');
|
86
|
-
// console.clear();
|
87
78
|
});
|
88
79
|
|
89
80
|
console.clear();
|
@@ -130,18 +121,9 @@ export default ({ lines, pause, breakpoint }) => {
|
|
130
121
|
|
131
122
|
for (const x of boxes) {
|
132
123
|
const y = x.y({ currentLinePos });
|
133
|
-
const height = x.height;
|
134
124
|
if (y < 0 || y >= termHeight) continue;
|
135
125
|
|
136
|
-
|
137
|
-
// if (y + height >= termHeight) {
|
138
|
-
// const excess = y + height - termHeight;
|
139
|
-
// height -= excess;
|
140
|
-
|
141
|
-
// content = content.slice(0, height - 2);
|
142
|
-
// }
|
143
|
-
|
144
|
-
box(x.x, y, x.width, height, x.title, x.content);
|
126
|
+
box(x.x, y, x.width, x.height, x.title, x.content);
|
145
127
|
}
|
146
128
|
|
147
129
|
// text += ` | rss: ${(process.memoryUsage.rss() / 1024 / 1024).toFixed(2)}mb`;
|
@@ -179,7 +161,8 @@ export default ({ lines, pause, breakpoint }) => {
|
|
179
161
|
}
|
180
162
|
|
181
163
|
case 'b': {
|
182
|
-
if (!lastSpecial) {
|
164
|
+
if (!lastSpecial) {
|
165
|
+
// b pressed normally
|
183
166
|
breakpoints[currentLine + scrollOffset] = !breakpoints[currentLine + scrollOffset];
|
184
167
|
draw();
|
185
168
|
|
@@ -188,7 +171,6 @@ export default ({ lines, pause, breakpoint }) => {
|
|
188
171
|
}
|
189
172
|
|
190
173
|
// arrow down
|
191
|
-
// if (screenOffset + termHeight <= lines.length) scrollOffset++;
|
192
174
|
if (scrollOffset < lines.length - currentLine - 1) scrollOffset++;
|
193
175
|
draw();
|
194
176
|
break;
|
@@ -198,7 +180,6 @@ export default ({ lines, pause, breakpoint }) => {
|
|
198
180
|
if (!lastSpecial) break;
|
199
181
|
|
200
182
|
// arrow up
|
201
|
-
// if (screenOffset > 0) scrollOffset--;
|
202
183
|
if (scrollOffset > -currentLine) scrollOffset--;
|
203
184
|
draw();
|
204
185
|
|
@@ -209,7 +190,6 @@ export default ({ lines, pause, breakpoint }) => {
|
|
209
190
|
if (!lastSpecial) break;
|
210
191
|
|
211
192
|
// page up
|
212
|
-
// scrollOffset -= Math.min(screenOffset, termHeight - 1);
|
213
193
|
scrollOffset -= Math.min(scrollOffset + currentLine, termHeight - 1);
|
214
194
|
draw();
|
215
195
|
break;
|
@@ -219,7 +199,6 @@ export default ({ lines, pause, breakpoint }) => {
|
|
219
199
|
if (!lastSpecial) break;
|
220
200
|
|
221
201
|
// page down
|
222
|
-
// scrollOffset += Math.min((lines.length + 1) - (screenOffset + termHeight), termHeight - 1);
|
223
202
|
scrollOffset += Math.min(lines.length - (scrollOffset + currentLine) - 1, termHeight - 1);
|
224
203
|
draw();
|
225
204
|
break;
|
package/compiler/2c.js
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
import { read_ieee754_binary64, read_signedLEB128, read_unsignedLEB128 } from './encoding.js';
|
2
2
|
import { Blocktype, Opcodes, Valtype } from './wasmSpec.js';
|
3
3
|
import { operatorOpcode } from './expression.js';
|
4
|
-
import { log } from
|
4
|
+
import { log } from './log.js';
|
5
5
|
|
6
6
|
const CValtype = {
|
7
7
|
i8: 'i8',
|
@@ -106,17 +106,6 @@ for (const x in CValtype) {
|
|
106
106
|
if (Valtype[x]) CValtype[Valtype[x]] = CValtype[x];
|
107
107
|
}
|
108
108
|
|
109
|
-
const todo = msg => {
|
110
|
-
class TodoError extends Error {
|
111
|
-
constructor(message) {
|
112
|
-
super(message);
|
113
|
-
this.name = 'TodoError';
|
114
|
-
}
|
115
|
-
}
|
116
|
-
|
117
|
-
throw new TodoError(msg);
|
118
|
-
};
|
119
|
-
|
120
109
|
const removeBrackets = str => {
|
121
110
|
// return str;
|
122
111
|
// if (str.startsWith(`(${CValtype.i32})(${CValtype.u32})`)) return `(${CValtype.i32})(${CValtype.u32})(` + removeBrackets(str.slice(22, -1)) + ')';
|
@@ -130,6 +119,9 @@ const removeBrackets = str => {
|
|
130
119
|
};
|
131
120
|
|
132
121
|
export default ({ funcs, globals, tags, data, exceptions, pages }) => {
|
122
|
+
// fix declaring order for c
|
123
|
+
funcs.reverse();
|
124
|
+
|
133
125
|
const invOperatorOpcode = Object.values(operatorOpcode).reduce((acc, x) => {
|
134
126
|
for (const k in x) {
|
135
127
|
acc[x[k]] = k;
|
@@ -167,10 +159,10 @@ export default ({ funcs, globals, tags, data, exceptions, pages }) => {
|
|
167
159
|
prependMain.set('_data', data.map(x => `memcpy(_memory + ${x.offset}, (unsigned char[]){${x.bytes.join(',')}}, ${x.bytes.length});`).join('\n'));
|
168
160
|
}
|
169
161
|
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
162
|
+
if (importFuncs.find(x => x.name === '__Porffor_readArgv')) {
|
163
|
+
prepend.set('argv', `int _argc; char** _argv;`);
|
164
|
+
prependMain.set('argv', `_argc = argc; _argv = argv;`);
|
165
|
+
}
|
174
166
|
|
175
167
|
if (out) out += '\n';
|
176
168
|
|
@@ -213,9 +205,9 @@ export default ({ funcs, globals, tags, data, exceptions, pages }) => {
|
|
213
205
|
|
214
206
|
for (const f of funcs) {
|
215
207
|
depth = 1;
|
208
|
+
brDepth = 0;
|
216
209
|
|
217
210
|
const invLocals = inv(f.locals, x => x.idx);
|
218
|
-
// if (f.returns.length > 1) todo('funcs returning >1 value unsupported');
|
219
211
|
|
220
212
|
for (const x in invLocals) {
|
221
213
|
invLocals[x] = sanitize(invLocals[x]);
|
@@ -223,8 +215,9 @@ export default ({ funcs, globals, tags, data, exceptions, pages }) => {
|
|
223
215
|
|
224
216
|
const returns = f.returns.length > 0;
|
225
217
|
|
226
|
-
const shouldInline = f.internal;
|
227
|
-
|
218
|
+
const shouldInline = false; // f.internal;
|
219
|
+
if (f.name === 'main') out += `int main(${prependMain.has('argv') ? 'int argc, char* argv[]' : ''}) {\n`;
|
220
|
+
else out += `${f.internal ? (returns ? CValtype.f64 : 'void') : 'struct ReturnValue'} ${shouldInline ? 'inline ' : ''}${sanitize(f.name)}(${f.params.map((x, i) => `${CValtype[x]} ${invLocals[i]}`).join(', ')}) {\n`;
|
228
221
|
|
229
222
|
if (f.name === 'main') {
|
230
223
|
out += [...prependMain.values()].join('\n');
|
@@ -247,12 +240,7 @@ export default ({ funcs, globals, tags, data, exceptions, pages }) => {
|
|
247
240
|
const brs = [];
|
248
241
|
let lastCond = false;
|
249
242
|
|
250
|
-
// let brDepth = 0;
|
251
|
-
|
252
243
|
const blockStart = (i, loop) => {
|
253
|
-
// reset "stack"
|
254
|
-
// vals = [];
|
255
|
-
|
256
244
|
rets.push(i[1]);
|
257
245
|
|
258
246
|
const br = brId++;
|
@@ -269,25 +257,6 @@ export default ({ funcs, globals, tags, data, exceptions, pages }) => {
|
|
269
257
|
brDepth++;
|
270
258
|
};
|
271
259
|
|
272
|
-
const highlight = i => {
|
273
|
-
const surrounding = 6;
|
274
|
-
|
275
|
-
const decomp = decompile(f.wasm.slice(i - surrounding, i + surrounding + 1), '', 0, f.locals, f.params, f.returns, funcs, globals, exceptions).slice(0, -1).split('\n');
|
276
|
-
|
277
|
-
const noAnsi = s => s.replace(/\u001b\[[0-9]+m/g, '');
|
278
|
-
let longest = 0;
|
279
|
-
for (let j = 0; j < decomp.length; j++) {
|
280
|
-
longest = Math.max(longest, noAnsi(decomp[j]).length);
|
281
|
-
}
|
282
|
-
|
283
|
-
const middle = Math.floor(decomp.length / 2);
|
284
|
-
decomp[middle] = `\x1B[47m\x1B[30m${noAnsi(decomp[middle])}${'\u00a0'.repeat(longest - noAnsi(decomp[middle]).length)}\x1B[0m`;
|
285
|
-
|
286
|
-
console.log('\x1B[90m...\x1B[0m');
|
287
|
-
console.log(decomp.join('\n'));
|
288
|
-
console.log('\x1B[90m...\x1B[0m\n');
|
289
|
-
};
|
290
|
-
|
291
260
|
for (let _ = 0; _ < f.wasm.length; _++) {
|
292
261
|
const i = f.wasm[_];
|
293
262
|
if (!i || !i[0]) continue;
|
@@ -415,8 +384,6 @@ export default ({ funcs, globals, tags, data, exceptions, pages }) => {
|
|
415
384
|
const br = brs.at(-1);
|
416
385
|
const ret = rets.at(-1);
|
417
386
|
if (ret && ret !== Blocktype.void) {
|
418
|
-
// console.log(vals, ret);
|
419
|
-
// console.log(decompile(f.wasm.slice(_ - 5, _ + 1)));
|
420
387
|
if (vals.length > 0) line(`_r${br} = ${removeBrackets(vals.pop())}`);
|
421
388
|
// vals.push(`_r${br}`);
|
422
389
|
}
|
@@ -425,8 +392,6 @@ export default ({ funcs, globals, tags, data, exceptions, pages }) => {
|
|
425
392
|
line(`} else {`, false);
|
426
393
|
depth++;
|
427
394
|
|
428
|
-
// reset "stack"
|
429
|
-
// vals = [];
|
430
395
|
break;
|
431
396
|
}
|
432
397
|
|
@@ -441,8 +406,6 @@ export default ({ funcs, globals, tags, data, exceptions, pages }) => {
|
|
441
406
|
const br = brs.pop();
|
442
407
|
const ret = rets.pop();
|
443
408
|
if (ret && ret !== Blocktype.void) {
|
444
|
-
// console.log(vals, ret);
|
445
|
-
// console.log(decompile(f.wasm.slice(_ - 5, _ + 1)));
|
446
409
|
if (vals.length > 0) line(`_r${br} = ${removeBrackets(vals.pop())}`);
|
447
410
|
vals.push(`_r${br}`);
|
448
411
|
}
|
@@ -505,6 +468,60 @@ _time_out = _time.tv_nsec / 1000000. + _time.tv_sec * 1000.;`);
|
|
505
468
|
winIncludes.set('windows.h', true);
|
506
469
|
break;
|
507
470
|
|
471
|
+
case '__Porffor_readArgv':
|
472
|
+
includes.set('stdlib.h', true);
|
473
|
+
|
474
|
+
prepend.set('__Porffor_readArgv',
|
475
|
+
`void __Porffor_readArgv(u32 index, u32 outPtr) {
|
476
|
+
if (index >= _argc) {
|
477
|
+
printf("expected %d arguments\\n", index);
|
478
|
+
exit(1);
|
479
|
+
}
|
480
|
+
|
481
|
+
char* arg = _argv[index];
|
482
|
+
|
483
|
+
u32 read = 0;
|
484
|
+
char* out = _memory + outPtr + 4;
|
485
|
+
char ch;
|
486
|
+
while ((ch = *(arg++)) != 0) {
|
487
|
+
out[read++] = ch;
|
488
|
+
}
|
489
|
+
|
490
|
+
memcpy(_memory + outPtr, &read, sizeof(read));
|
491
|
+
}`);
|
492
|
+
|
493
|
+
line(`__Porffor_readArgv((u32)(${vals.at(-2)}), (u32)(${vals.pop()}))`);
|
494
|
+
vals.pop();
|
495
|
+
break;
|
496
|
+
|
497
|
+
case '__Porffor_readFile':
|
498
|
+
includes.set('stdio.h', true);
|
499
|
+
includes.set('stdlib.h', true);
|
500
|
+
|
501
|
+
prepend.set('__Porffor_readFile',
|
502
|
+
`void __Porffor_readFile(u32 pathPtr, u32 outPtr) {
|
503
|
+
char* path = _memory + pathPtr + 4;
|
504
|
+
FILE* fp = fopen(path, "r");
|
505
|
+
if (fp == NULL) {
|
506
|
+
printf("failed to open file: %s\\n", path);
|
507
|
+
exit(1);
|
508
|
+
}
|
509
|
+
|
510
|
+
u32 read = 0;
|
511
|
+
char* out = _memory + outPtr + 4;
|
512
|
+
char ch;
|
513
|
+
while ((ch = fgetc(fp)) != EOF) {
|
514
|
+
out[read++] = ch;
|
515
|
+
}
|
516
|
+
|
517
|
+
fclose(fp);
|
518
|
+
|
519
|
+
memcpy(_memory + outPtr, &read, sizeof(read));
|
520
|
+
}`);
|
521
|
+
line(`__Porffor_readFile((u32)(${vals.at(-2)}), (u32)(${vals.pop()}))`);
|
522
|
+
vals.pop();
|
523
|
+
break;
|
524
|
+
|
508
525
|
default:
|
509
526
|
log.warning('2c', `unimplemented import: ${importFunc.name}`);
|
510
527
|
break;
|
@@ -541,18 +558,14 @@ _time_out = _time.tv_nsec / 1000000. + _time.tv_sec * 1000.;`);
|
|
541
558
|
|
542
559
|
case Opcodes.br: {
|
543
560
|
const ret = rets[brDepth - i[1] - 1];
|
544
|
-
// console.log(rets, brDepth, i[1], brDepth - i[1] - 1, ret, vals);
|
545
561
|
if (ret !== Blocktype.void) line(`_r${brs[brDepth - i[1] - 1]} = ${removeBrackets(vals.pop())}`);
|
546
562
|
line(`goto j${brs[brDepth - i[1] - 1]}`);
|
547
563
|
|
548
|
-
// // reset "stack"
|
549
|
-
// vals = [];
|
550
564
|
break;
|
551
565
|
}
|
552
566
|
|
553
567
|
case Opcodes.br_if: {
|
554
568
|
const ret = rets[brDepth - i[1] - 1];
|
555
|
-
// console.log(rets, brDepth, i[1], brDepth - i[1] - 1, ret, vals);
|
556
569
|
|
557
570
|
let cond = removeBrackets(vals.pop());
|
558
571
|
if (!lastCond) {
|
@@ -602,7 +615,6 @@ _time_out = _time.tv_nsec / 1000000. + _time.tv_sec * 1000.;`);
|
|
602
615
|
}
|
603
616
|
|
604
617
|
log.warning('2c', `unimplemented op: ${invOpcodes[i[0]]}`);
|
605
|
-
// todo(`unimplemented op: ${invOpcodes[i[0]]}`);
|
606
618
|
}
|
607
619
|
|
608
620
|
lastCond = false;
|
package/compiler/assemble.js
CHANGED
@@ -1,8 +1,7 @@
|
|
1
|
-
import { Valtype, FuncType,
|
2
|
-
import { encodeVector, encodeString, encodeLocal, unsignedLEB128, signedLEB128,
|
3
|
-
// import { number } from './embedding.js';
|
1
|
+
import { Valtype, FuncType, ExportDesc, Section, Magic, ModuleVersion, Opcodes, PageSize, Reftype } from './wasmSpec.js';
|
2
|
+
import { encodeVector, encodeString, encodeLocal, unsignedLEB128, signedLEB128, unsignedLEB128_into, signedLEB128_into, ieee754_binary64_into } from './encoding.js';
|
4
3
|
import { importedFuncs } from './builtins.js';
|
5
|
-
import { log } from
|
4
|
+
import { log } from './log.js';
|
6
5
|
import Prefs from './prefs.js';
|
7
6
|
|
8
7
|
const createSection = (type, data) => [
|
@@ -66,6 +65,7 @@ export default (funcs, globals, tags, pages, data, flags) => {
|
|
66
65
|
importFuncs = [...imports.values()];
|
67
66
|
|
68
67
|
// fix call indexes for non-imports
|
68
|
+
// also fix call_indirect types
|
69
69
|
const delta = importedFuncs.length - importFuncs.length;
|
70
70
|
for (const f of funcs) {
|
71
71
|
f.originalIndex = f.index;
|
@@ -75,6 +75,16 @@ export default (funcs, globals, tags, pages, data, flags) => {
|
|
75
75
|
if ((inst[0] === Opcodes.call || inst[0] === Opcodes.return_call) && inst[1] >= importedFuncs.length) {
|
76
76
|
inst[1] -= delta;
|
77
77
|
}
|
78
|
+
|
79
|
+
if (inst[0] === Opcodes.call_indirect) {
|
80
|
+
const params = [];
|
81
|
+
for (let i = 0; i < inst[1]; i++) {
|
82
|
+
params.push(valtypeBinary, Valtype.i32);
|
83
|
+
}
|
84
|
+
|
85
|
+
const returns = [ valtypeBinary, Valtype.i32 ];
|
86
|
+
inst[1] = getType(params, returns);
|
87
|
+
}
|
78
88
|
}
|
79
89
|
}
|
80
90
|
}
|
@@ -92,17 +102,37 @@ export default (funcs, globals, tags, pages, data, flags) => {
|
|
92
102
|
encodeVector(funcs.map(x => getType(x.params, x.returns))) // type indexes
|
93
103
|
);
|
94
104
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
'compilationHints',
|
99
|
-
// for now just do everything as optimize eager
|
100
|
-
encodeVector(funcs.map(_ => chHint(0x02, 0x02, 0x02)))
|
105
|
+
const tableSection = !funcs.table ? [] : createSection(
|
106
|
+
Section.table,
|
107
|
+
encodeVector([ [ Reftype.funcref, 0x00, funcs.length ] ])
|
101
108
|
);
|
102
109
|
|
110
|
+
const elementSection = !funcs.table ? [] : createSection(
|
111
|
+
Section.element,
|
112
|
+
encodeVector([ [
|
113
|
+
0x00,
|
114
|
+
Opcodes.i32_const, 0, Opcodes.end,
|
115
|
+
...encodeVector(funcs.map(x => x.index))
|
116
|
+
] ])
|
117
|
+
);
|
118
|
+
|
119
|
+
if (pages.has('func argc lut')) {
|
120
|
+
// generate func argc lut data
|
121
|
+
const bytes = [];
|
122
|
+
for (let i = 0; i < funcs.length; i++) {
|
123
|
+
const argc = Math.floor(funcs[i].params.length / 2);
|
124
|
+
bytes.push(argc % 256, (argc / 256 | 0) % 256);
|
125
|
+
}
|
126
|
+
|
127
|
+
data.push({
|
128
|
+
offset: pages.get('func argc lut').ind * pageSize,
|
129
|
+
bytes
|
130
|
+
});
|
131
|
+
}
|
132
|
+
|
103
133
|
// const t0 = performance.now();
|
104
134
|
|
105
|
-
// specially optimized assembly for globals as this version is much (>5x) faster than traditional createSection(
|
135
|
+
// specially optimized assembly for globals as this version is much (>5x) faster than traditional createSection()
|
106
136
|
const globalsValues = Object.values(globals);
|
107
137
|
|
108
138
|
let globalSection = [];
|
@@ -227,6 +257,14 @@ export default (funcs, globals, tags, pages, data, flags) => {
|
|
227
257
|
dataSection: dataSection.map(x => x.toString(16)),
|
228
258
|
});
|
229
259
|
|
260
|
+
// compilation hints section - unspecd, v8 only
|
261
|
+
// https://github.com/WebAssembly/design/issues/1473#issuecomment-1431274746
|
262
|
+
const chSection = !compileHints ? [] : customSection(
|
263
|
+
'compilationHints',
|
264
|
+
// for now just do everything as optimize eager
|
265
|
+
encodeVector(funcs.map(_ => chHint(0x02, 0x02, 0x02)))
|
266
|
+
);
|
267
|
+
|
230
268
|
return Uint8Array.from([
|
231
269
|
...Magic,
|
232
270
|
...ModuleVersion,
|
@@ -234,10 +272,12 @@ export default (funcs, globals, tags, pages, data, flags) => {
|
|
234
272
|
...importSection,
|
235
273
|
...funcSection,
|
236
274
|
...chSection,
|
275
|
+
...tableSection,
|
237
276
|
...memorySection,
|
238
277
|
...tagSection,
|
239
278
|
...globalSection,
|
240
279
|
...exportSection,
|
280
|
+
...elementSection,
|
241
281
|
...dataCountSection,
|
242
282
|
...codeSection,
|
243
283
|
...dataSection
|
@@ -2,7 +2,7 @@ export default () => {
|
|
2
2
|
let out = `// @porf --funsafe-no-unlikely-proto-checks --valtype=i32
|
3
3
|
`;
|
4
4
|
|
5
|
-
const
|
5
|
+
const noArgs = (a0, a1) => out += `
|
6
6
|
export const __String_prototype_${a0} = (_this: string) => {
|
7
7
|
let out: string = Porffor.s\`<${a1}>\`;
|
8
8
|
|
@@ -58,15 +58,15 @@ ${[...a1].map((x, i) => ` Porffor.wasm.i32.store8(outPtr, ${x.charCodeAt(0)}, 0
|
|
58
58
|
};
|
59
59
|
`;
|
60
60
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
61
|
+
noArgs('big', 'big');
|
62
|
+
noArgs('blink', 'blink');
|
63
|
+
noArgs('bold', 'b');
|
64
|
+
noArgs('fixed', 'tt');
|
65
|
+
noArgs('italics', 'i');
|
66
|
+
noArgs('small', 'small');
|
67
|
+
noArgs('strike', 'strike');
|
68
|
+
noArgs('sub', 'sub');
|
69
|
+
noArgs('sup', 'sup');
|
70
70
|
|
71
71
|
return out;
|
72
72
|
};
|
@@ -1,4 +1,5 @@
|
|
1
|
-
// @porf --
|
1
|
+
// @porf --valtype=i32
|
2
|
+
import type {} from './porffor.d.ts';
|
2
3
|
|
3
4
|
export const __String_prototype_trimLeft = (_this: string) => {
|
4
5
|
return __String_prototype_trimStart(_this);
|
@@ -13,6 +14,6 @@ export const __String_prototype_trimRight = (_this: string) => {
|
|
13
14
|
return __String_prototype_trimEnd(_this);
|
14
15
|
};
|
15
16
|
|
16
|
-
export const
|
17
|
-
return
|
17
|
+
export const __ByteString_prototype_trimRight = (_this: string) => {
|
18
|
+
return __ByteString_prototype_trimEnd(_this);
|
18
19
|
};
|