porffor 0.2.0-536e463 → 0.2.0-623cdf0
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/README.md +36 -14
- package/compiler/2c.js +321 -71
- package/compiler/builtins.js +177 -25
- package/compiler/codeGen.js +456 -221
- package/compiler/decompile.js +4 -4
- package/compiler/encoding.js +2 -116
- package/compiler/index.js +15 -4
- package/compiler/opt.js +24 -2
- package/compiler/parse.js +11 -12
- package/compiler/prototype.js +171 -16
- package/compiler/sections.js +1 -1
- package/compiler/wasmSpec.js +6 -2
- package/compiler/wrap.js +101 -8
- package/filesize.cmd +2 -0
- package/package.json +1 -1
- package/runner/index.js +15 -2
- package/runner/repl.js +2 -2
- package/tmp.c +0 -69
package/README.md
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
# Porffor <sup><sub>/ˈpɔrfɔr/ *(poor-for)*</sup></sub>
|
2
|
-
A from-scratch experimental **AOT** optimizing JS -> Wasm/C engine/compiler/runtime in JS. Not serious/intended for (real) use. (this is a straight forward, honest readme)<br>
|
2
|
+
A from-scratch experimental **AOT** optimizing JS/TS -> Wasm/C engine/compiler/runtime in JS. Not serious/intended for (real) use. (this is a straight forward, honest readme)<br>
|
3
3
|
Age: ~6 months (very on and off)
|
4
4
|
|
5
5
|
## Design
|
@@ -118,10 +118,10 @@ No particular order and no guarentees, just what could happen soon™
|
|
118
118
|
- Objects
|
119
119
|
- Basic object expressions (eg `{}`, `{ a: 0 }`)
|
120
120
|
- Wasm
|
121
|
-
- *Basic* Wasm engine (interpreter) in
|
121
|
+
- *Basic* Wasm engine (interpreter) in JS
|
122
122
|
- More math operators (`**`, etc)
|
123
123
|
- `do { ... } while (...)`
|
124
|
-
-
|
124
|
+
- Typed export inputs (array)
|
125
125
|
- Exceptions
|
126
126
|
- Rewrite to use actual strings (optional?)
|
127
127
|
- `try { } finally { }`
|
@@ -130,10 +130,24 @@ No particular order and no guarentees, just what could happen soon™
|
|
130
130
|
- Rewrite local indexes per func for smallest local header and remove unused idxs
|
131
131
|
- Smarter inline selection (snapshots?)
|
132
132
|
- Remove const ifs (`if (true)`, etc)
|
133
|
-
-
|
133
|
+
- Memory alignment
|
134
|
+
- Runtime
|
135
|
+
- WASI target
|
136
|
+
- Run precompiled Wasm file if given
|
137
|
+
- Cool proposals
|
138
|
+
- [Optional Chaining Assignment](https://github.com/tc39/proposal-optional-chaining-assignment)
|
139
|
+
- [Modulus and Additional Integer Math](https://github.com/tc39/proposal-integer-and-modulus-math)
|
140
|
+
- [Array Equality](https://github.com/tc39/proposal-array-equality)
|
141
|
+
- [Declarations in Conditionals](https://github.com/tc39/proposal-Declarations-in-Conditionals)
|
142
|
+
- [Seeded Pseudo-Random Numbers](https://github.com/tc39/proposal-seeded-random)
|
143
|
+
- [`do` expressions](https://github.com/tc39/proposal-do-expressions)
|
144
|
+
- [String Trim Characters](https://github.com/Kingwl/proposal-string-trim-characters)
|
145
|
+
- Posts
|
146
|
+
- Inlining investigation
|
147
|
+
- Self hosted testing?
|
134
148
|
|
135
149
|
## Performance
|
136
|
-
*For the things 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
|
150
|
+
*For the things 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.
|
137
151
|
|
138
152
|

|
139
153
|
|
@@ -157,10 +171,12 @@ Mostly for reducing size. I do not really care about compiler perf/time as long
|
|
157
171
|
- Remove unneeded blocks (no `br`s inside)
|
158
172
|
- Remove unused imports
|
159
173
|
- Use data segments for initing arrays/strings
|
174
|
+
- (Likely more not documented yet, todo)
|
160
175
|
|
161
176
|
### Wasm module
|
162
177
|
- Type cache/index (no repeated types)
|
163
178
|
- No main func if empty (and other exports)
|
179
|
+
- No tags if unused/optimized out
|
164
180
|
|
165
181
|
## Test262
|
166
182
|
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 >10% (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.
|
@@ -180,7 +196,7 @@ Porffor can run Test262 via some hacks/transforms which remove unsupported featu
|
|
180
196
|
- `wasmSpec.js`: "enums"/info from wasm spec
|
181
197
|
- `wrap.js`: wrapper for compiler which instantiates and produces nice exports
|
182
198
|
|
183
|
-
- `runner`: contains utils for running
|
199
|
+
- `runner`: contains utils for running JS with the compiler
|
184
200
|
- `index.js`: the main file, you probably want to use this
|
185
201
|
- `info.js`: runs with extra info printed
|
186
202
|
- `repl.js`: basic repl (uses `node:repl`)
|
@@ -193,10 +209,13 @@ Porffor can run Test262 via some hacks/transforms which remove unsupported featu
|
|
193
209
|
- `test262`: test262 runner and utils
|
194
210
|
|
195
211
|
## Usecases
|
196
|
-
Basically none (other than giving people headaches). Potential ideas
|
212
|
+
Basically none right now (other than giving people headaches). Potential ideas:
|
213
|
+
- Safety. As Porffor is written in JS, a memory-safe language\*, and compiles JS to Wasm, a fully sandboxed environment\*, it is quite safe. (\* These rely on the underlying implementations being secure. You could also run Wasm, or even Porffor itself, with an interpreter instead of a JIT for bonus security points too.)
|
214
|
+
- Compiling JS to native binaries. This is still very early, [`2c`](#2c) is not that good yet :(
|
215
|
+
- More in future probably?
|
197
216
|
|
198
217
|
## Usage
|
199
|
-
Basically nothing will work :). See files in `test` for examples.
|
218
|
+
Basically nothing will work :). See files in `test` and `bench` for examples.
|
200
219
|
|
201
220
|
1. Clone repo
|
202
221
|
2. `npm install`
|
@@ -212,11 +231,14 @@ You can also use Deno (`deno run -A ...` instead of `node ...`), or Bun (`bun ..
|
|
212
231
|
- `-target=native` only:
|
213
232
|
- `-compiler=clang` to set compiler binary (path/name) to use to compile
|
214
233
|
- `-cO=O3` to set compiler opt argument
|
234
|
+
- `-parser=acorn|@babel/parser|meriyah|hermes-parser` (default: `acorn`) to set which parser to use
|
235
|
+
- `-parse-types` to enable parsing type annotations/typescript. if `-parser` is unset, changes default to `@babel/parser`. does not type check
|
236
|
+
- `-opt-types` to perform optimizations using type annotations as compiler hints. does not type check
|
215
237
|
- `-valtype=i32|i64|f64` (default: `f64`) to set valtype
|
216
238
|
- `-O0` to disable opt
|
217
239
|
- `-O1` (default) to enable basic opt (simplify insts, treeshake wasm imports)
|
218
|
-
- `-O2` to enable advanced opt (inlining)
|
219
|
-
- `-O3` to enable advanceder opt (precompute const math)
|
240
|
+
- `-O2` to enable advanced opt (inlining). unstable
|
241
|
+
- `-O3` to enable advanceder opt (precompute const math). unstable
|
220
242
|
- `-no-run` to not run wasm output, just compile
|
221
243
|
- `-opt-log` to log some opts
|
222
244
|
- `-code-log` to log some codegen (you probably want `-funcs`)
|
@@ -230,20 +252,20 @@ You can also use Deno (`deno run -A ...` instead of `node ...`), or Bun (`bun ..
|
|
230
252
|
- `-compile-hints` to enable V8 compilation hints (experimental + doesn't seem to do much?)
|
231
253
|
|
232
254
|
## VSCode extension
|
233
|
-
There is a vscode extension in `porffor-for-vscode` which tweaks
|
255
|
+
There is a vscode extension in `porffor-for-vscode` which tweaks JS syntax highlighting to be nicer with porffor features (eg highlighting wasm inside of inline asm).
|
234
256
|
|
235
257
|
## Isn't this the same as AssemblyScript/other Wasm langs?
|
236
258
|
No. they are not alike at all internally and have very different goals/ideals:
|
237
259
|
- Porffor is made as a generic JS engine, not for Wasm stuff specifically
|
238
|
-
- Porffor
|
239
|
-
- Porffor is
|
260
|
+
- Porffor primarily consumes JS
|
261
|
+
- Porffor is written in pure JS and compiles itself, not using Binaryen/etc
|
240
262
|
- (Also I didn't know it existed when I started this, lol)
|
241
263
|
|
242
264
|
## FAQ
|
243
265
|
|
244
266
|
### 1. Why the name?
|
245
267
|
`purple` in Welsh is `porffor`. Why purple?
|
246
|
-
- No other
|
268
|
+
- No other JS engine is purple colored
|
247
269
|
- Purple is pretty cool
|
248
270
|
- Purple apparently represents "ambition", which is.. one word to describe this project
|
249
271
|
- The hard to speak name is also the noise your brain makes in reaction to this idea!
|
package/compiler/2c.js
CHANGED
@@ -1,24 +1,107 @@
|
|
1
|
-
import { read_ieee754_binary64, read_signedLEB128 } from './encoding.js';
|
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
4
|
import { log } from "./log.js";
|
5
5
|
|
6
6
|
const CValtype = {
|
7
|
-
i8: '
|
8
|
-
i16: '
|
9
|
-
i32: '
|
10
|
-
|
11
|
-
i64: '
|
12
|
-
|
7
|
+
i8: 'i8',
|
8
|
+
i16: 'i16',
|
9
|
+
i32: 'i32',
|
10
|
+
u32: 'u32',
|
11
|
+
i64: 'i64',
|
12
|
+
u64: 'u64',
|
13
13
|
|
14
|
-
f32: '
|
15
|
-
f64: '
|
14
|
+
f32: 'f32',
|
15
|
+
f64: 'f64',
|
16
16
|
|
17
17
|
undefined: 'void'
|
18
18
|
};
|
19
19
|
|
20
|
+
const alwaysPreface = `typedef unsigned char i8;
|
21
|
+
typedef unsigned short i16;
|
22
|
+
typedef long i32;
|
23
|
+
typedef unsigned long u32;
|
24
|
+
typedef long long i64;
|
25
|
+
typedef unsigned long long u64;
|
26
|
+
typedef float f32;
|
27
|
+
typedef double f64;
|
28
|
+
|
29
|
+
f64 NAN = 0e+0/0e+0;
|
30
|
+
|
31
|
+
struct ReturnValue {
|
32
|
+
${CValtype.f64} value;
|
33
|
+
${CValtype.i32} type;
|
34
|
+
};
|
35
|
+
\n`;
|
36
|
+
|
37
|
+
// todo: is memcpy/etc safe with host endianness?
|
38
|
+
|
39
|
+
// all:
|
40
|
+
// immediates: ['align', 'offset']
|
41
|
+
const CMemFuncs = {
|
42
|
+
[Opcodes.i32_store]: {
|
43
|
+
c: `memcpy(_memory + offset + pointer, &value, sizeof(value));`,
|
44
|
+
args: ['pointer', 'value'],
|
45
|
+
argTypes: [CValtype.i32, CValtype.i32],
|
46
|
+
returns: false
|
47
|
+
},
|
48
|
+
[Opcodes.i32_store16]: {
|
49
|
+
c: `memcpy(_memory + offset + pointer, &value, sizeof(value));`,
|
50
|
+
args: ['pointer', 'value'],
|
51
|
+
argTypes: [CValtype.i32, CValtype.i16],
|
52
|
+
returns: false
|
53
|
+
},
|
54
|
+
[Opcodes.i32_store8]: {
|
55
|
+
c: `memcpy(_memory + offset + pointer, &value, sizeof(value));`,
|
56
|
+
args: ['pointer', 'value'],
|
57
|
+
argTypes: [CValtype.i32, CValtype.i8],
|
58
|
+
returns: false
|
59
|
+
},
|
60
|
+
|
61
|
+
[Opcodes.i32_load]: {
|
62
|
+
c: `${CValtype.i32} out;
|
63
|
+
memcpy(&out, _memory + offset + pointer, sizeof(out));
|
64
|
+
return out;`,
|
65
|
+
args: ['pointer'],
|
66
|
+
argTypes: [CValtype.i32],
|
67
|
+
returns: CValtype.i32
|
68
|
+
},
|
69
|
+
[Opcodes.i32_load16_u]: {
|
70
|
+
c: `${CValtype.i16} out;
|
71
|
+
memcpy(&out, _memory + offset + pointer, sizeof(out));
|
72
|
+
return out;`,
|
73
|
+
args: ['pointer'],
|
74
|
+
argTypes: [CValtype.i32],
|
75
|
+
returns: CValtype.i32
|
76
|
+
},
|
77
|
+
[Opcodes.i32_load8_u]: {
|
78
|
+
c: `${CValtype.i8} out;
|
79
|
+
memcpy(&out, _memory + offset + pointer, sizeof(out));
|
80
|
+
return out;`,
|
81
|
+
args: ['pointer'],
|
82
|
+
argTypes: [CValtype.i32],
|
83
|
+
returns: CValtype.i32
|
84
|
+
},
|
85
|
+
|
86
|
+
[Opcodes.f64_store]: {
|
87
|
+
c: `memcpy(_memory + offset + pointer, &value, sizeof(value));`,
|
88
|
+
args: ['pointer', 'value'],
|
89
|
+
argTypes: [CValtype.i32, CValtype.f64],
|
90
|
+
returns: false
|
91
|
+
},
|
92
|
+
[Opcodes.f64_load]: {
|
93
|
+
c: `${CValtype.f64} out;
|
94
|
+
memcpy(&out, _memory + offset + pointer, sizeof(out));
|
95
|
+
return out;`,
|
96
|
+
args: ['pointer'],
|
97
|
+
argTypes: [CValtype.i32],
|
98
|
+
returns: CValtype.f64
|
99
|
+
},
|
100
|
+
};
|
101
|
+
|
20
102
|
const inv = (obj, keyMap = x => x) => Object.keys(obj).reduce((acc, x) => { acc[keyMap(obj[x])] = x; return acc; }, {});
|
21
103
|
const invOpcodes = inv(Opcodes);
|
104
|
+
const invValtype = inv(Valtype);
|
22
105
|
|
23
106
|
for (const x in CValtype) {
|
24
107
|
if (Valtype[x]) CValtype[Valtype[x]] = CValtype[x];
|
@@ -36,11 +119,18 @@ const todo = msg => {
|
|
36
119
|
};
|
37
120
|
|
38
121
|
const removeBrackets = str => {
|
39
|
-
|
122
|
+
// return str;
|
123
|
+
// if (str.startsWith(`(${CValtype.i32})(${CValtype.u32})`)) return `(${CValtype.i32})(${CValtype.u32})(` + removeBrackets(str.slice(22, -1)) + ')';
|
124
|
+
|
125
|
+
for (const x in CValtype) {
|
126
|
+
const p = `(${x})`;
|
127
|
+
if (str.startsWith(p)) return p + removeBrackets(str.slice(p.length));
|
128
|
+
}
|
129
|
+
|
40
130
|
return str.startsWith('(') && str.endsWith(')') ? str.slice(1, -1) : str;
|
41
131
|
};
|
42
132
|
|
43
|
-
export default ({ funcs, globals, tags, exceptions, pages }) => {
|
133
|
+
export default ({ funcs, globals, tags, data, exceptions, pages }) => {
|
44
134
|
const invOperatorOpcode = Object.values(operatorOpcode).reduce((acc, x) => {
|
45
135
|
for (const k in x) {
|
46
136
|
acc[x[k]] = k;
|
@@ -56,12 +146,10 @@ export default ({ funcs, globals, tags, exceptions, pages }) => {
|
|
56
146
|
}
|
57
147
|
|
58
148
|
const includes = new Map(), unixIncludes = new Map(), winIncludes = new Map();
|
149
|
+
const prepend = new Map(), prependMain = new Map();
|
59
150
|
|
60
|
-
//
|
61
|
-
let out =
|
62
|
-
${CValtype.f64} value;
|
63
|
-
${CValtype.i32} type;
|
64
|
-
};\n\n`;
|
151
|
+
// presume all <i32 work is unsigned
|
152
|
+
let out = ``;
|
65
153
|
|
66
154
|
for (const x in globals) {
|
67
155
|
const g = globals[x];
|
@@ -70,6 +158,15 @@ export default ({ funcs, globals, tags, exceptions, pages }) => {
|
|
70
158
|
out += ';\n';
|
71
159
|
}
|
72
160
|
|
161
|
+
if (pages.size > 0) {
|
162
|
+
prepend.set('_memory', `char _memory[${pages.size * pageSize}];\n`);
|
163
|
+
includes.set('string.h', true);
|
164
|
+
}
|
165
|
+
|
166
|
+
if (data.length > 0) {
|
167
|
+
prependMain.set('_data', data.map(x => `memcpy(_memory + ${x.offset}, (char[]){${x.bytes.join(',')}}, ${x.bytes.length});`).join('\n'));
|
168
|
+
}
|
169
|
+
|
73
170
|
// for (const [ x, p ] of pages) {
|
74
171
|
// out += `${CValtype[p.type]} ${x.replace(': ', '_').replace(/[^0-9a-zA-Z_]/g, '')}[100]`;
|
75
172
|
// out += ';\n';
|
@@ -78,7 +175,8 @@ export default ({ funcs, globals, tags, exceptions, pages }) => {
|
|
78
175
|
if (out) out += '\n';
|
79
176
|
|
80
177
|
let depth = 1;
|
81
|
-
|
178
|
+
let brDepth = 0;
|
179
|
+
const line = (str, semi = true) => out += `${' '.repeat(depth * 2 + brDepth * 2)}${str}${semi ? ';' : ''}\n`;
|
82
180
|
const lines = lines => {
|
83
181
|
for (const x of lines) {
|
84
182
|
out += `${' '.repeat(depth * 2)}${x}\n`;
|
@@ -111,6 +209,8 @@ export default ({ funcs, globals, tags, exceptions, pages }) => {
|
|
111
209
|
return tmp;
|
112
210
|
};
|
113
211
|
|
212
|
+
let brId = 0;
|
213
|
+
|
114
214
|
for (const f of funcs) {
|
115
215
|
depth = 1;
|
116
216
|
|
@@ -124,7 +224,12 @@ export default ({ funcs, globals, tags, exceptions, pages }) => {
|
|
124
224
|
const returns = f.returns.length > 0;
|
125
225
|
|
126
226
|
const shouldInline = f.internal;
|
127
|
-
out += `${f.name === 'main' ? 'int' : (f.internal ? '
|
227
|
+
out += `${f.name === 'main' ? 'int' : (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
|
+
|
229
|
+
if (f.name === 'main') {
|
230
|
+
out += [...prependMain.values()].join('\n');
|
231
|
+
if (prependMain.size > 0) out += '\n\n';
|
232
|
+
}
|
128
233
|
|
129
234
|
const localKeys = Object.keys(f.locals).sort((a, b) => f.locals[a].idx - f.locals[b].idx).slice(f.params.length).sort((a, b) => f.locals[a].idx - f.locals[b].idx);
|
130
235
|
for (const x of localKeys) {
|
@@ -134,11 +239,58 @@ export default ({ funcs, globals, tags, exceptions, pages }) => {
|
|
134
239
|
|
135
240
|
if (localKeys.length !== 0) out += '\n';
|
136
241
|
|
242
|
+
const rets = [];
|
243
|
+
const runOnEnd = [];
|
244
|
+
|
137
245
|
let vals = [];
|
138
|
-
const endNeedsCurly = []
|
139
|
-
|
246
|
+
const endNeedsCurly = [];
|
247
|
+
const brs = [];
|
248
|
+
let lastCond = false;
|
249
|
+
|
250
|
+
// let brDepth = 0;
|
251
|
+
|
252
|
+
const blockStart = (i, loop) => {
|
253
|
+
// reset "stack"
|
254
|
+
// vals = [];
|
255
|
+
|
256
|
+
rets.push(i[1]);
|
257
|
+
|
258
|
+
const br = brId++;
|
259
|
+
brs.push(br);
|
260
|
+
if (loop) {
|
261
|
+
line(`j${br}:;`, false);
|
262
|
+
runOnEnd.push(null);
|
263
|
+
} else {
|
264
|
+
runOnEnd.push(() => line(`j${br}:;`, false));
|
265
|
+
}
|
266
|
+
|
267
|
+
if (i[1] !== Blocktype.void) line(`${CValtype[i[1]]} _r${br}`);
|
268
|
+
|
269
|
+
brDepth++;
|
270
|
+
};
|
271
|
+
|
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
|
+
|
140
291
|
for (let _ = 0; _ < f.wasm.length; _++) {
|
141
292
|
const i = f.wasm[_];
|
293
|
+
if (!i || !i[0]) continue;
|
142
294
|
|
143
295
|
if (invOperatorOpcode[i[0]]) {
|
144
296
|
const b = vals.pop();
|
@@ -160,12 +312,12 @@ export default ({ funcs, globals, tags, exceptions, pages }) => {
|
|
160
312
|
switch (i[1]) {
|
161
313
|
// i32_trunc_sat_f64_s
|
162
314
|
case 0x02:
|
163
|
-
vals.push(`(${CValtype.i32})${vals.pop()}`);
|
315
|
+
vals.push(`(${CValtype.i32})(${vals.pop()})`);
|
164
316
|
break;
|
165
317
|
|
166
318
|
// i32_trunc_sat_f64_u
|
167
319
|
case 0x03:
|
168
|
-
vals.push(`(${CValtype.
|
320
|
+
vals.push(`(${CValtype.u32})(${vals.pop()})`);
|
169
321
|
break;
|
170
322
|
}
|
171
323
|
|
@@ -176,12 +328,19 @@ export default ({ funcs, globals, tags, exceptions, pages }) => {
|
|
176
328
|
switch (i[0]) {
|
177
329
|
case Opcodes.i32_const:
|
178
330
|
case Opcodes.i64_const:
|
179
|
-
vals.push(read_signedLEB128(i.slice(1)).toString());
|
331
|
+
// vals.push(read_signedLEB128(i.slice(1)).toString());
|
332
|
+
vals.push(new String(read_signedLEB128(i.slice(1)).toString()));
|
333
|
+
vals.at(-1).offset = _;
|
180
334
|
break;
|
181
335
|
|
182
|
-
case Opcodes.f64_const:
|
183
|
-
|
336
|
+
case Opcodes.f64_const: {
|
337
|
+
// const val = read_ieee754_binary64(i.slice(1)).toExponential();
|
338
|
+
const val = new String(read_ieee754_binary64(i.slice(1)).toExponential());
|
339
|
+
// vals.push(val == 'NaN' ? 'NAN' : val);
|
340
|
+
vals.push(val == 'NaN' ? new String('NAN') : val);
|
341
|
+
vals.at(-1).offset = _;
|
184
342
|
break;
|
343
|
+
}
|
185
344
|
|
186
345
|
case Opcodes.local_get:
|
187
346
|
vals.push(`${invLocals[i[1]]}`);
|
@@ -192,9 +351,9 @@ export default ({ funcs, globals, tags, exceptions, pages }) => {
|
|
192
351
|
break;
|
193
352
|
|
194
353
|
case Opcodes.local_tee:
|
195
|
-
|
196
|
-
|
197
|
-
vals.push(`((${invLocals[i[1]]} = ${vals.pop()}))`);
|
354
|
+
line(`${invLocals[i[1]]} = ${removeBrackets(vals.pop())}`);
|
355
|
+
vals.push(`${invLocals[i[1]]}`);
|
356
|
+
// vals.push(`((${invLocals[i[1]]} = ${vals.pop()}))`);
|
198
357
|
break;
|
199
358
|
|
200
359
|
case Opcodes.global_get:
|
@@ -207,78 +366,102 @@ export default ({ funcs, globals, tags, exceptions, pages }) => {
|
|
207
366
|
|
208
367
|
case Opcodes.f64_trunc:
|
209
368
|
// vals.push(`trunc(${vals.pop()})`);
|
210
|
-
vals.push(`(
|
369
|
+
vals.push(`(${CValtype.i32})(${removeBrackets(vals.pop())})`); // this is ~10x faster with clang??
|
211
370
|
break;
|
212
371
|
|
213
372
|
case Opcodes.f64_convert_i32_u:
|
214
373
|
case Opcodes.f64_convert_i32_s:
|
215
374
|
case Opcodes.f64_convert_i64_u:
|
216
375
|
case Opcodes.f64_convert_i64_s:
|
217
|
-
// int to
|
218
|
-
vals.push(`(
|
376
|
+
// int to f64
|
377
|
+
vals.push(`(${CValtype.f64})(${removeBrackets(vals.pop())})`);
|
219
378
|
break;
|
220
379
|
|
380
|
+
case Opcodes.i32_eqz:
|
381
|
+
if (lastCond) {
|
382
|
+
vals.push(`!(${removeBrackets(vals.pop())})`);
|
383
|
+
} else {
|
384
|
+
let cond = '(' + removeBrackets(vals.pop());
|
385
|
+
if (cond.startsWith(`(${CValtype.i32})`)) cond = `${cond.slice(`(${CValtype.i32})`.length)}) == 0e+0`;
|
386
|
+
else cond += ') == 0';
|
387
|
+
vals.push(cond);
|
388
|
+
}
|
389
|
+
lastCond = true;
|
390
|
+
continue;
|
391
|
+
|
221
392
|
case Opcodes.return:
|
222
393
|
// line(`return${returns ? ` ${removeBrackets(vals.pop())}` : ''}`);
|
223
394
|
line(`return${returns ? ` (struct ReturnValue){ ${removeBrackets(vals.pop())}, ${removeBrackets(vals.pop())} }` : ''}`);
|
224
395
|
break;
|
225
396
|
|
226
|
-
case Opcodes.if:
|
397
|
+
case Opcodes.if: {
|
227
398
|
let cond = removeBrackets(vals.pop());
|
228
399
|
if (!lastCond) {
|
229
|
-
if (cond.startsWith(
|
230
|
-
else cond
|
400
|
+
if (cond.startsWith(`(${CValtype.i32})`)) cond = `(${cond.slice(`(${CValtype.i32})`.length)}) != 0e+0`;
|
401
|
+
else cond = `(${cond}) != 0`;
|
231
402
|
}
|
232
403
|
|
233
|
-
|
234
|
-
|
235
|
-
ifTernary = cond;
|
236
|
-
break;
|
237
|
-
}
|
238
|
-
|
239
|
-
if (beginLoop) {
|
240
|
-
beginLoop = false;
|
241
|
-
line(`while (${cond}) {`, false);
|
242
|
-
|
243
|
-
depth++;
|
244
|
-
endNeedsCurly.push(true);
|
245
|
-
ignoreEnd.push(false, true);
|
246
|
-
break;
|
247
|
-
}
|
404
|
+
line(`// if ${invValtype[i[1]] ?? ''}`, false);
|
405
|
+
blockStart(i, false);
|
248
406
|
|
249
407
|
line(`if (${cond}) {`, false);
|
250
408
|
|
251
409
|
depth++;
|
252
410
|
endNeedsCurly.push(true);
|
253
|
-
ignoreEnd.push(false);
|
254
411
|
break;
|
412
|
+
}
|
255
413
|
|
256
|
-
case Opcodes.else:
|
257
|
-
|
414
|
+
case Opcodes.else: {
|
415
|
+
const br = brs.at(-1);
|
416
|
+
const ret = rets.at(-1);
|
417
|
+
if (ret && ret !== Blocktype.void) {
|
418
|
+
// console.log(vals, ret);
|
419
|
+
// console.log(decompile(f.wasm.slice(_ - 5, _ + 1)));
|
420
|
+
if (vals.length > 0) line(`_r${br} = ${removeBrackets(vals.pop())}`);
|
421
|
+
// vals.push(`_r${br}`);
|
422
|
+
}
|
258
423
|
|
259
424
|
depth--;
|
260
425
|
line(`} else {`, false);
|
261
426
|
depth++;
|
427
|
+
|
428
|
+
// reset "stack"
|
429
|
+
// vals = [];
|
262
430
|
break;
|
431
|
+
}
|
263
432
|
|
264
|
-
case Opcodes.loop:
|
265
|
-
|
266
|
-
|
433
|
+
case Opcodes.loop: {
|
434
|
+
line(`// loop ${invValtype[i[1]] ?? ''}`, false);
|
435
|
+
blockStart(i, true);
|
436
|
+
endNeedsCurly.push(false);
|
267
437
|
break;
|
438
|
+
}
|
268
439
|
|
269
|
-
case Opcodes.end:
|
270
|
-
|
440
|
+
case Opcodes.end: {
|
441
|
+
const br = brs.pop();
|
442
|
+
const ret = rets.pop();
|
443
|
+
if (ret && ret !== Blocktype.void) {
|
444
|
+
// console.log(vals, ret);
|
445
|
+
// console.log(decompile(f.wasm.slice(_ - 5, _ + 1)));
|
446
|
+
if (vals.length > 0) line(`_r${br} = ${removeBrackets(vals.pop())}`);
|
447
|
+
vals.push(`_r${br}`);
|
448
|
+
}
|
271
449
|
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
break;
|
450
|
+
const enc = endNeedsCurly.pop() === true;
|
451
|
+
if (enc) {
|
452
|
+
depth--;
|
453
|
+
line('}', false);
|
277
454
|
}
|
278
455
|
|
279
|
-
|
280
|
-
|
456
|
+
brDepth--;
|
457
|
+
|
458
|
+
line(`// end`, false);
|
459
|
+
|
460
|
+
const roe = runOnEnd.pop();
|
461
|
+
if (roe) roe();
|
462
|
+
|
281
463
|
break;
|
464
|
+
}
|
282
465
|
|
283
466
|
case Opcodes.call:
|
284
467
|
let func = funcs.find(x => x.index === i[1]);
|
@@ -286,7 +469,8 @@ export default ({ funcs, globals, tags, exceptions, pages }) => {
|
|
286
469
|
const importFunc = importFuncs[i[1]];
|
287
470
|
switch (importFunc.name) {
|
288
471
|
case 'print':
|
289
|
-
line(`printf("%f\\n", ${vals.pop()})`);
|
472
|
+
// line(`printf("%f\\n", ${vals.pop()})`);
|
473
|
+
line(`printf("${valtype === 'f64' ? '%g' : '%i'}\\n", ${vals.pop()})`);
|
290
474
|
includes.set('stdio.h', true);
|
291
475
|
break;
|
292
476
|
case 'printChar':
|
@@ -349,13 +533,74 @@ _time_out = _time.tv_nsec / 1000000. + _time.tv_sec * 1000.;`);
|
|
349
533
|
vals.pop();
|
350
534
|
break;
|
351
535
|
|
352
|
-
case Opcodes.
|
353
|
-
|
354
|
-
|
355
|
-
|
536
|
+
case Opcodes.block:
|
537
|
+
line(`// block ${invValtype[i[1]] ?? ''}`, false);
|
538
|
+
blockStart(i, false);
|
539
|
+
endNeedsCurly.push(false);
|
540
|
+
break;
|
541
|
+
|
542
|
+
case Opcodes.br: {
|
543
|
+
const ret = rets[brDepth - i[1] - 1];
|
544
|
+
// console.log(rets, brDepth, i[1], brDepth - i[1] - 1, ret, vals);
|
545
|
+
if (ret !== Blocktype.void) line(`_r${brs[brDepth - i[1] - 1]} = ${removeBrackets(vals.pop())}`);
|
546
|
+
line(`goto j${brs[brDepth - i[1] - 1]}`);
|
547
|
+
|
548
|
+
// // reset "stack"
|
549
|
+
// vals = [];
|
356
550
|
break;
|
551
|
+
}
|
552
|
+
|
553
|
+
case Opcodes.br_if: {
|
554
|
+
const ret = rets[brDepth - i[1] - 1];
|
555
|
+
// console.log(rets, brDepth, i[1], brDepth - i[1] - 1, ret, vals);
|
556
|
+
|
557
|
+
let cond = removeBrackets(vals.pop());
|
558
|
+
if (!lastCond) {
|
559
|
+
if (cond.startsWith(`(${CValtype.i32})`)) cond = `(${cond.slice(`(${CValtype.i32})`.length)}) != 0e+0`;
|
560
|
+
else cond = `(${cond}) != 0`;
|
561
|
+
}
|
562
|
+
|
563
|
+
line(`if (${cond}) {`, false);
|
564
|
+
depth++;
|
565
|
+
if (ret !== Blocktype.void) line(`_r${brs[brDepth - i[1] - 1]} = ${removeBrackets(vals.at(-1))}`);
|
566
|
+
line(`goto j${brs[brDepth - i[1] - 1]}`);
|
567
|
+
depth--;
|
568
|
+
line(`}`, false);
|
569
|
+
|
570
|
+
break;
|
571
|
+
}
|
572
|
+
|
573
|
+
case Opcodes.throw: {
|
574
|
+
const id = vals.pop();
|
575
|
+
|
576
|
+
line(`printf("Uncaught ${exceptions[id].constructor}: ${exceptions[id].message}\\n")`);
|
577
|
+
line(`exit(1)`);
|
578
|
+
|
579
|
+
includes.set('stdlib.h', true);
|
580
|
+
|
581
|
+
break;
|
582
|
+
}
|
357
583
|
|
358
584
|
default:
|
585
|
+
if (CMemFuncs[i[0]]) {
|
586
|
+
const name = invOpcodes[i[0]];
|
587
|
+
const func = CMemFuncs[i[0]];
|
588
|
+
if (!prepend.has(name)) {
|
589
|
+
prepend.set(name, `${func.returns || 'void'} ${name}(${CValtype.i32} align, ${CValtype.i32} offset, ${func.args.map((x, i) => `${func.argTypes[i]} ${x}`).join(', ')}) {\n ${func.c.replaceAll('\n', '\n ')}\n}\n`);
|
590
|
+
// generate func c and prepend
|
591
|
+
}
|
592
|
+
|
593
|
+
const immediates = [ i[1], read_unsignedLEB128(i.slice(2)) ];
|
594
|
+
|
595
|
+
let args = [];
|
596
|
+
for (let j = 0; j < func.args.length; j++) args.unshift(removeBrackets(vals.pop()));
|
597
|
+
|
598
|
+
if (func.returns !== false) {
|
599
|
+
vals.push(`${name}(${immediates[0]}, ${immediates[1]}, ${args.join(', ')})`);
|
600
|
+
} else line(`${name}(${immediates[0]}, ${immediates[1]}, ${args.join(', ')})`);
|
601
|
+
break;
|
602
|
+
}
|
603
|
+
|
359
604
|
log.warning('2c', `unimplemented op: ${invOpcodes[i[0]]}`);
|
360
605
|
// todo(`unimplemented op: ${invOpcodes[i[0]]}`);
|
361
606
|
}
|
@@ -367,6 +612,11 @@ _time_out = _time.tv_nsec / 1000000. + _time.tv_sec * 1000.;`);
|
|
367
612
|
line(`return ${vals.pop()}`);
|
368
613
|
}
|
369
614
|
|
615
|
+
if (f.name === 'main') {
|
616
|
+
out += '\n';
|
617
|
+
line(`return 0`);
|
618
|
+
}
|
619
|
+
|
370
620
|
out += '}\n\n';
|
371
621
|
}
|
372
622
|
|
@@ -374,7 +624,7 @@ _time_out = _time.tv_nsec / 1000000. + _time.tv_sec * 1000.;`);
|
|
374
624
|
|
375
625
|
const makeIncludes = includes => [...includes.keys()].map(x => `#include <${x}>\n`).join('');
|
376
626
|
|
377
|
-
out = platformSpecific(makeIncludes(winIncludes), makeIncludes(unixIncludes), false) + '\n' + makeIncludes(includes) + '\n' + out;
|
627
|
+
out = alwaysPreface + platformSpecific(makeIncludes(winIncludes), makeIncludes(unixIncludes), false) + '\n' + makeIncludes(includes) + '\n' + [...prepend.values()].join('\n') + '\n\n' + out;
|
378
628
|
|
379
|
-
return out;
|
629
|
+
return out.trim();
|
380
630
|
};
|