porffor 0.14.0-f67c123a1 โ 0.16.0-08983b6b1
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 +16 -10
- package/README.md +14 -30
- package/asur/index.js +1 -1
- package/compiler/2c.js +172 -59
- package/compiler/allocators.js +128 -0
- package/compiler/assemble.js +37 -5
- package/compiler/builtins/annexb_string.ts +1 -0
- package/compiler/builtins/array.ts +154 -7
- package/compiler/builtins/base64.ts +1 -0
- package/compiler/builtins/boolean.ts +3 -1
- package/compiler/builtins/console.ts +6 -0
- package/compiler/builtins/crypto.ts +1 -0
- package/compiler/builtins/date.ts +5 -30
- package/compiler/builtins/error.js +22 -0
- package/compiler/builtins/escape.ts +1 -2
- package/compiler/builtins/function.ts +2 -0
- package/compiler/builtins/int.ts +2 -0
- package/compiler/builtins/math.ts +410 -0
- package/compiler/builtins/number.ts +12 -21
- package/compiler/builtins/object.ts +2 -0
- package/compiler/builtins/porffor.d.ts +18 -0
- package/compiler/builtins/set.ts +22 -12
- package/compiler/builtins/string.ts +1 -0
- package/compiler/builtins/string_f64.ts +10 -0
- package/compiler/builtins/symbol.ts +62 -0
- package/compiler/builtins/z_ecma262.ts +62 -0
- package/compiler/builtins.js +50 -12
- package/compiler/codegen.js +826 -552
- package/compiler/cyclone.js +535 -0
- package/compiler/decompile.js +7 -1
- package/compiler/generated_builtins.js +635 -84
- package/compiler/havoc.js +93 -0
- package/compiler/index.js +108 -15
- package/compiler/opt.js +10 -44
- package/compiler/parse.js +3 -9
- package/compiler/pgo.js +212 -0
- package/compiler/precompile.js +17 -11
- package/compiler/prefs.js +13 -5
- package/compiler/prototype.js +200 -186
- package/compiler/wasmSpec.js +2 -2
- package/compiler/wrap.js +128 -44
- package/package.json +3 -5
- package/runner/index.js +31 -15
- package/runner/repl.js +18 -2
- /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
|
|
@@ -98,7 +98,7 @@ Loads the character code at the pointer `pointer` **for a String**.[^1]
|
|
98
98
|
Porffor.wasm.i32.store(pointer, length, 0, 0)
|
99
99
|
```
|
100
100
|
|
101
|
-
Stores the length `length` at pointer `pointer`, setting the length of an object. This is mostly unneeded today as you can just do `obj.length = length`.
|
101
|
+
Stores the length `length` at pointer `pointer`, setting the length of an object. This is mostly unneeded today as you can just do `obj.length = length`. (The `0, 4` args are necessary for the Wasm instruction, but you don't need to worry about them (`0` alignment, `0` byte offset).
|
102
102
|
|
103
103
|
<br>
|
104
104
|
|
@@ -198,11 +198,15 @@ 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(...))`.
|
208
|
+
- For object (string/array/etc) literals, you must use a variable eg `const out: bytestring = 'foobar'; console.log(out);` instead of `console.log('foobar')` due to precompile's allocator constraints.
|
209
|
+
- Generally prefer/use non-strict equality ops (`==`/`!=`).
|
206
210
|
|
207
211
|
<br>
|
208
212
|
|
@@ -229,9 +233,13 @@ builtins/tostring_number: impl radix
|
|
229
233
|
|
230
234
|
## Test262
|
231
235
|
|
232
|
-
|
236
|
+
For the first time, ensure you run `./test262/setup.sh`.
|
233
237
|
|
234
|
-
Run `node test262` to run all the tests and get an output of total overall test results.
|
238
|
+
Run `node test262` to run all the tests and get an output of total overall test results.
|
239
|
+
|
240
|
+
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.
|
241
|
+
|
242
|
+
The main thing you want to pay attention to is the emoji summary (lol):
|
235
243
|
```
|
236
244
|
๐งช 50005 | ๐ค 7007 (-89) | โ 1914 (-32) | ๐ 13904 (-61) | ๐ 23477 (-120) | โฐ 2 | ๐ 2073 (+302) | ๐ฅ 1628
|
237
245
|
```
|
@@ -239,7 +247,7 @@ Run `node test262` to run all the tests and get an output of total overall test
|
|
239
247
|
To break this down:
|
240
248
|
๐งช total ๐ค pass โ fail ๐ runtime error ๐ todo (error) โฐ timeout ๐๏ธ wasm compile error ๐ฅ compile error
|
241
249
|
|
242
|
-
The diff compared to the last commit (with test262 data) is shown in brackets. Basically, you
|
250
|
+
The diff compared to the last commit (with test262 data) is shown in brackets. Basically, you want passes ๐ค up, and errors ๐๐๐๐ฅ down. It is fine if some errors change balance/etc, as long as they are not new failures.
|
243
251
|
|
244
252
|
It will also log new passes/fails. Be careful as sometimes the overall passes can increase, but other files have also regressed into failures which you might miss. Also keep in mind some tests may have been false positives before, but we can investigate the diff together :)
|
245
253
|
|
@@ -251,6 +259,4 @@ It will also log new passes/fails. Be careful as sometimes the overall passes ca
|
|
251
259
|
|
252
260
|
<br>
|
253
261
|
|
254
|
-
[^1]: The `0, 4` args are necessary for the Wasm instruction, but you don't need to worry about them (`0` alignment, `4` byte offset for length).
|
255
|
-
|
256
|
-
[^2]: The `0, 4` args are necessary for the Wasm instruction, but you don't need to worry about them (`0` alignment, `0` byte offset).
|
262
|
+
[^1]: The `0, 4` args are necessary for the Wasm instruction, but you don't need to worry about them (`0` alignment, `4` byte offset for length).
|
package/README.md
CHANGED
@@ -14,7 +14,7 @@ Porffor is primarily built from scratch, the only thing that is not is the parse
|
|
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
|
-
**`npm install -g porffor`**. It's that easy (hopefully) :)
|
17
|
+
**`npm install -g porffor@latest`**. It's that easy (hopefully) :)
|
18
18
|
|
19
19
|
### Trying a REPL
|
20
20
|
**`porf`**. Just run it with no script file argument.
|
@@ -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 (`+-/*%`)
|
@@ -266,8 +254,6 @@ Basically none right now (other than giving people headaches). Potential ideas:
|
|
266
254
|
No particular order and no guarentees, just what could happen soonโข
|
267
255
|
|
268
256
|
- Arrays
|
269
|
-
- More of `Array` prototype
|
270
|
-
- Arrays/strings inside arrays
|
271
257
|
- Destructuring
|
272
258
|
- Objects
|
273
259
|
- Basic object expressions (eg `{}`, `{ a: 0 }`)
|
@@ -315,16 +301,10 @@ Porffor intentionally does not use Wasm proposals which are not commonly impleme
|
|
315
301
|
|
316
302
|
- Multi-value **(required)**
|
317
303
|
- Non-trapping float-to-int conversions **(required)**
|
318
|
-
- Bulk memory operations (
|
319
|
-
- Exception handling (optional, for errors)
|
304
|
+
- Bulk memory operations (optional, can get away without sometimes)
|
305
|
+
- Exception handling (optional, only for errors)
|
320
306
|
- Tail calls (opt-in, off by default)
|
321
307
|
|
322
|
-
## Isn't this the same as AssemblyScript/other Wasm langs?
|
323
|
-
No. they are not alike at all internally and have very different goals/ideals:
|
324
|
-
- Porffor is made as a generic JS engine, not for Wasm stuff specifically
|
325
|
-
- Porffor primarily consumes JS
|
326
|
-
- Porffor is written in pure JS and compiles itself, not using Binaryen/etc
|
327
|
-
- (Also I didn't know it existed when I started this, lol)
|
328
308
|
|
329
309
|
## FAQ
|
330
310
|
|
@@ -338,5 +318,9 @@ No. they are not alike at all internally and have very different goals/ideals:
|
|
338
318
|
### 2. Why at all?
|
339
319
|
Yes!
|
340
320
|
|
341
|
-
|
342
|
-
|
321
|
+
## 3. Isn't this the same as AssemblyScript/other Wasm langs?
|
322
|
+
No. they are not alike at all internally and have very different goals/ideals:
|
323
|
+
- Porffor is made as a generic JS engine, not for Wasm stuff specifically
|
324
|
+
- Porffor primarily consumes JS
|
325
|
+
- Porffor is written in pure JS and compiles itself, not using Binaryen/etc
|
326
|
+
- (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/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})`;
|
@@ -119,6 +171,9 @@ const removeBrackets = str => {
|
|
119
171
|
};
|
120
172
|
|
121
173
|
export default ({ funcs, globals, tags, data, exceptions, pages }) => {
|
174
|
+
// fix declaring order for c
|
175
|
+
funcs.reverse();
|
176
|
+
|
122
177
|
const invOperatorOpcode = Object.values(operatorOpcode).reduce((acc, x) => {
|
123
178
|
for (const k in x) {
|
124
179
|
acc[x[k]] = k;
|
@@ -149,21 +204,31 @@ export default ({ funcs, globals, tags, data, exceptions, pages }) => {
|
|
149
204
|
|
150
205
|
if (pages.size > 0) {
|
151
206
|
prepend.set('_memory', `char _memory[${pages.size * pageSize}];\n`);
|
152
|
-
includes.set('string.h', true);
|
207
|
+
if (Prefs['2cMemcpy']) includes.set('string.h', true);
|
153
208
|
}
|
154
209
|
|
155
210
|
if (data.length > 0) {
|
156
|
-
|
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
|
+
}
|
217
|
+
}
|
218
|
+
|
219
|
+
if (importFuncs.find(x => x.name === '__Porffor_readArgv')) {
|
220
|
+
prepend.set('argv', `int _argc; char** _argv;`);
|
221
|
+
prependMain.set('argv', `_argc = argc; _argv = argv;`);
|
157
222
|
}
|
158
223
|
|
159
224
|
if (out) out += '\n';
|
160
225
|
|
161
226
|
let depth = 1;
|
162
227
|
let brDepth = 0;
|
163
|
-
const line = (str, semi = true) => out += `${' '.repeat(depth
|
228
|
+
const line = (str, semi = true) => out += `${' '.repeat((depth + brDepth) * 2)}${str}${semi ? ';' : ''}\n`;
|
164
229
|
const lines = lines => {
|
165
230
|
for (const x of lines) {
|
166
|
-
out += `${' '.repeat(depth * 2)}${x}\n`;
|
231
|
+
out += `${' '.repeat((depth + brDepth) * 2)}${x}\n`;
|
167
232
|
}
|
168
233
|
};
|
169
234
|
|
@@ -199,6 +264,8 @@ export default ({ funcs, globals, tags, data, exceptions, pages }) => {
|
|
199
264
|
depth = 1;
|
200
265
|
brDepth = 0;
|
201
266
|
|
267
|
+
let retTmpId = 0;
|
268
|
+
|
202
269
|
const invLocals = inv(f.locals, x => x.idx);
|
203
270
|
|
204
271
|
for (const x in invLocals) {
|
@@ -207,11 +274,12 @@ export default ({ funcs, globals, tags, data, exceptions, pages }) => {
|
|
207
274
|
|
208
275
|
const returns = f.returns.length > 0;
|
209
276
|
|
210
|
-
const shouldInline = f.internal;
|
211
|
-
|
277
|
+
const shouldInline = false; // f.internal;
|
278
|
+
if (f.name === 'main') out += `int main(${prependMain.has('argv') ? 'int argc, char* argv[]' : ''}) {\n`;
|
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`;
|
212
280
|
|
213
281
|
if (f.name === 'main') {
|
214
|
-
out += [...prependMain.values()].join('\n');
|
282
|
+
out += ' ' + [...prependMain.values()].join('\n ');
|
215
283
|
if (prependMain.size > 0) out += '\n\n';
|
216
284
|
}
|
217
285
|
|
@@ -272,12 +340,12 @@ export default ({ funcs, globals, tags, data, exceptions, pages }) => {
|
|
272
340
|
switch (i[1]) {
|
273
341
|
// i32_trunc_sat_f64_s
|
274
342
|
case 0x02:
|
275
|
-
vals.push(`(
|
343
|
+
vals.push(`(i32)(${vals.pop()})`);
|
276
344
|
break;
|
277
345
|
|
278
346
|
// i32_trunc_sat_f64_u
|
279
347
|
case 0x03:
|
280
|
-
vals.push(`(
|
348
|
+
vals.push(`(u32)(${vals.pop()})`);
|
281
349
|
break;
|
282
350
|
}
|
283
351
|
|
@@ -326,7 +394,7 @@ export default ({ funcs, globals, tags, data, exceptions, pages }) => {
|
|
326
394
|
|
327
395
|
case Opcodes.f64_trunc:
|
328
396
|
// vals.push(`trunc(${vals.pop()})`);
|
329
|
-
vals.push(`(
|
397
|
+
vals.push(`(i32)(${removeBrackets(vals.pop())})`); // this is ~10x faster with clang??
|
330
398
|
break;
|
331
399
|
|
332
400
|
case Opcodes.f64_convert_i32_u:
|
@@ -334,7 +402,7 @@ export default ({ funcs, globals, tags, data, exceptions, pages }) => {
|
|
334
402
|
case Opcodes.f64_convert_i64_u:
|
335
403
|
case Opcodes.f64_convert_i64_s:
|
336
404
|
// int to f64
|
337
|
-
vals.push(`(
|
405
|
+
vals.push(`(f64)(${removeBrackets(vals.pop())})`);
|
338
406
|
break;
|
339
407
|
|
340
408
|
case Opcodes.i32_eqz:
|
@@ -342,7 +410,7 @@ export default ({ funcs, globals, tags, data, exceptions, pages }) => {
|
|
342
410
|
vals.push(`!(${removeBrackets(vals.pop())})`);
|
343
411
|
} else {
|
344
412
|
let cond = '(' + removeBrackets(vals.pop());
|
345
|
-
if (cond.startsWith(`(
|
413
|
+
if (cond.startsWith(`(i32)`)) cond = `${cond.slice(5)} == 0e+0`;
|
346
414
|
else cond += ') == 0';
|
347
415
|
vals.push(cond);
|
348
416
|
}
|
@@ -351,13 +419,16 @@ export default ({ funcs, globals, tags, data, exceptions, pages }) => {
|
|
351
419
|
|
352
420
|
case Opcodes.return:
|
353
421
|
// line(`return${returns ? ` ${removeBrackets(vals.pop())}` : ''}`);
|
354
|
-
|
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())} }` : ''}`);
|
355
426
|
break;
|
356
427
|
|
357
428
|
case Opcodes.if: {
|
358
429
|
let cond = removeBrackets(vals.pop());
|
359
430
|
if (!lastCond) {
|
360
|
-
if (cond.startsWith(`(
|
431
|
+
if (cond.startsWith(`(i32)`)) cond = `${cond.slice(5)} != 0e+0`;
|
361
432
|
else cond = `(${cond}) != 0`;
|
362
433
|
}
|
363
434
|
|
@@ -423,28 +494,16 @@ export default ({ funcs, globals, tags, data, exceptions, pages }) => {
|
|
423
494
|
const importFunc = importFuncs[i[1]];
|
424
495
|
switch (importFunc.name) {
|
425
496
|
case 'print':
|
426
|
-
// line(`printf("%f\\n", ${vals.pop()})`);
|
427
497
|
line(`printf("${valtype === 'f64' ? '%g' : '%i'}\\n", ${vals.pop()})`);
|
428
498
|
includes.set('stdio.h', true);
|
429
499
|
break;
|
430
500
|
case 'printChar':
|
431
|
-
line(`
|
501
|
+
line(`putchar((int)(${vals.pop()}))`);
|
432
502
|
includes.set('stdio.h', true);
|
433
503
|
break;
|
434
504
|
|
435
505
|
case 'time':
|
436
506
|
line(`double _time_out`);
|
437
|
-
/* platformSpecific(
|
438
|
-
`FILETIME _time_filetime;
|
439
|
-
GetSystemTimeAsFileTime(&_time_filetime);
|
440
|
-
|
441
|
-
ULARGE_INTEGER _time_ularge;
|
442
|
-
_time_ularge.LowPart = _time_filetime.dwLowDateTime;
|
443
|
-
_time_ularge.HighPart = _time_filetime.dwHighDateTime;
|
444
|
-
_time_out = (_time_ularge.QuadPart - 116444736000000000i64) / 10000.;`,
|
445
|
-
`struct timespec _time;
|
446
|
-
clock_gettime(CLOCK_MONOTONIC, &_time);
|
447
|
-
_time_out = _time.tv_nsec / 1000000.;`); */
|
448
507
|
platformSpecific(
|
449
508
|
`LARGE_INTEGER _time_freq, _time_t;
|
450
509
|
QueryPerformanceFrequency(&_time_freq);
|
@@ -459,6 +518,61 @@ _time_out = _time.tv_nsec / 1000000. + _time.tv_sec * 1000.;`);
|
|
459
518
|
winIncludes.set('windows.h', true);
|
460
519
|
break;
|
461
520
|
|
521
|
+
case '__Porffor_readArgv': {
|
522
|
+
prepend.set('__Porffor_readArgv',
|
523
|
+
`i32 __Porffor_readArgv(u32 index, u32 outPtr) {
|
524
|
+
if (index >= _argc) {
|
525
|
+
return -1;
|
526
|
+
}
|
527
|
+
|
528
|
+
char* arg = _argv[index];
|
529
|
+
|
530
|
+
u32 read = 0;
|
531
|
+
char* out = _memory + outPtr + 4;
|
532
|
+
char ch;
|
533
|
+
while ((ch = *(arg++)) != 0) {
|
534
|
+
out[read++] = ch;
|
535
|
+
}
|
536
|
+
|
537
|
+
*((i32*)(_memory + outPtr)) = (i32)read;
|
538
|
+
return read;
|
539
|
+
}`);
|
540
|
+
|
541
|
+
const outPtr = vals.pop();
|
542
|
+
const index = vals.pop();
|
543
|
+
vals.push(`(f64)__Porffor_readArgv((u32)(${index}), (u32)(${outPtr}))`);
|
544
|
+
break;
|
545
|
+
}
|
546
|
+
|
547
|
+
case '__Porffor_readFile': {
|
548
|
+
includes.set('stdio.h', true);
|
549
|
+
|
550
|
+
prepend.set('__Porffor_readFile',
|
551
|
+
`i32 __Porffor_readFile(u32 pathPtr, u32 outPtr) {
|
552
|
+
char* path = _memory + pathPtr + 4;
|
553
|
+
FILE* fp = fopen(path, "r");
|
554
|
+
if (fp == NULL) {
|
555
|
+
return -1;
|
556
|
+
}
|
557
|
+
|
558
|
+
u32 read = 0;
|
559
|
+
char* out = _memory + outPtr + 4;
|
560
|
+
char ch;
|
561
|
+
while ((ch = fgetc(fp)) != EOF) {
|
562
|
+
out[read++] = ch;
|
563
|
+
}
|
564
|
+
|
565
|
+
fclose(fp);
|
566
|
+
|
567
|
+
*((i32*)(_memory + outPtr)) = (i32)read;
|
568
|
+
return read;
|
569
|
+
}`);
|
570
|
+
const outPtr = vals.pop();
|
571
|
+
const pathPtr = vals.pop();
|
572
|
+
vals.push(`(f64)__Porffor_readFile((u32)(${pathPtr}), (u32)(${outPtr}))`);
|
573
|
+
break;
|
574
|
+
}
|
575
|
+
|
462
576
|
default:
|
463
577
|
log.warning('2c', `unimplemented import: ${importFunc.name}`);
|
464
578
|
break;
|
@@ -474,9 +588,10 @@ _time_out = _time.tv_nsec / 1000000. + _time.tv_sec * 1000.;`);
|
|
474
588
|
if (func.internal) {
|
475
589
|
vals.push(`${sanitize(func.name)}(${args.join(', ')})`);
|
476
590
|
} else {
|
477
|
-
|
478
|
-
|
479
|
-
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`);
|
480
595
|
}
|
481
596
|
} else line(`${sanitize(func.name)}(${args.join(', ')})`);
|
482
597
|
|
@@ -506,7 +621,7 @@ _time_out = _time.tv_nsec / 1000000. + _time.tv_sec * 1000.;`);
|
|
506
621
|
|
507
622
|
let cond = removeBrackets(vals.pop());
|
508
623
|
if (!lastCond) {
|
509
|
-
if (cond.startsWith(`(
|
624
|
+
if (cond.startsWith(`(i32)`)) cond = `${cond.slice(5)} != 0e+0`;
|
510
625
|
else cond = `(${cond}) != 0`;
|
511
626
|
}
|
512
627
|
|
@@ -536,8 +651,7 @@ _time_out = _time.tv_nsec / 1000000. + _time.tv_sec * 1000.;`);
|
|
536
651
|
const name = invOpcodes[i[0]];
|
537
652
|
const func = CMemFuncs[i[0]];
|
538
653
|
if (!prepend.has(name)) {
|
539
|
-
prepend.set(name, `${func.returns || 'void'} ${name}(
|
540
|
-
// 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`);
|
541
655
|
}
|
542
656
|
|
543
657
|
const immediates = [ i[1], read_unsignedLEB128(i.slice(2)) ];
|
@@ -572,7 +686,6 @@ _time_out = _time.tv_nsec / 1000000. + _time.tv_sec * 1000.;`);
|
|
572
686
|
depth = 0;
|
573
687
|
|
574
688
|
const makeIncludes = includes => [...includes.keys()].map(x => `#include <${x}>\n`).join('');
|
575
|
-
|
576
689
|
out = platformSpecific(makeIncludes(winIncludes), makeIncludes(unixIncludes), false) + '\n' + makeIncludes(includes) + '\n' + alwaysPreface + [...prepend.values()].join('\n') + '\n\n' + out;
|
577
690
|
|
578
691
|
return out.trim();
|