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.
Files changed (45) hide show
  1. package/CONTRIBUTING.md +16 -10
  2. package/README.md +14 -30
  3. package/asur/index.js +1 -1
  4. package/compiler/2c.js +172 -59
  5. package/compiler/allocators.js +128 -0
  6. package/compiler/assemble.js +37 -5
  7. package/compiler/builtins/annexb_string.ts +1 -0
  8. package/compiler/builtins/array.ts +154 -7
  9. package/compiler/builtins/base64.ts +1 -0
  10. package/compiler/builtins/boolean.ts +3 -1
  11. package/compiler/builtins/console.ts +6 -0
  12. package/compiler/builtins/crypto.ts +1 -0
  13. package/compiler/builtins/date.ts +5 -30
  14. package/compiler/builtins/error.js +22 -0
  15. package/compiler/builtins/escape.ts +1 -2
  16. package/compiler/builtins/function.ts +2 -0
  17. package/compiler/builtins/int.ts +2 -0
  18. package/compiler/builtins/math.ts +410 -0
  19. package/compiler/builtins/number.ts +12 -21
  20. package/compiler/builtins/object.ts +2 -0
  21. package/compiler/builtins/porffor.d.ts +18 -0
  22. package/compiler/builtins/set.ts +22 -12
  23. package/compiler/builtins/string.ts +1 -0
  24. package/compiler/builtins/string_f64.ts +10 -0
  25. package/compiler/builtins/symbol.ts +62 -0
  26. package/compiler/builtins/z_ecma262.ts +62 -0
  27. package/compiler/builtins.js +50 -12
  28. package/compiler/codegen.js +826 -552
  29. package/compiler/cyclone.js +535 -0
  30. package/compiler/decompile.js +7 -1
  31. package/compiler/generated_builtins.js +635 -84
  32. package/compiler/havoc.js +93 -0
  33. package/compiler/index.js +108 -15
  34. package/compiler/opt.js +10 -44
  35. package/compiler/parse.js +3 -9
  36. package/compiler/pgo.js +212 -0
  37. package/compiler/precompile.js +17 -11
  38. package/compiler/prefs.js +13 -5
  39. package/compiler/prototype.js +200 -186
  40. package/compiler/wasmSpec.js +2 -2
  41. package/compiler/wrap.js +128 -44
  42. package/package.json +3 -5
  43. package/runner/index.js +31 -15
  44. package/runner/repl.js +18 -2
  45. /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 `node compiler/precompile.js` 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).
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`. [^2]
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/literals.**
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
- 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.
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. The main thing you want to pay attention to is the emoji summary (lol):
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 can passes ๐Ÿค  up, and errors ๐Ÿ’€๐Ÿ“๐Ÿ—๐Ÿ’ฅ down. It is fine if some errors change balance/etc, as long as they are not new failures.
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/zig/gcc`, and which opt level to use with `--cO=O3` (`Ofast` by default). Output binaries are also stripped by default.
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
- ### Profiling the generated Wasm of a JS file
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 *literal callees only*
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 (required, but uncommonly used)
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
- ### 3. But what about spec compliance?
342
- Lol, no. (sorry.)
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: 'i8',
8
- i16: '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 i8;
21
- typedef uint16_t i16;
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
- ${CValtype.f64} value;
33
- ${CValtype.i32} type;
33
+ f64 value;
34
+ i32 type;
34
35
  };\n\n`;
35
36
 
36
- // todo: is memcpy/etc safe with host endianness?
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: [CValtype.i32, CValtype.i32],
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: [CValtype.i32, CValtype.i16],
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: [CValtype.i32, CValtype.i8],
57
+ argTypes: ['i32', 'u8'],
57
58
  returns: false
58
59
  },
59
60
 
60
61
  [Opcodes.i32_load]: {
61
- c: `${CValtype.i32} out;
62
+ c: `i32 out;
62
63
  memcpy(&out, _memory + offset + pointer, sizeof(out));
63
64
  return out;`,
64
65
  args: ['pointer'],
65
- argTypes: [CValtype.i32],
66
- returns: CValtype.i32
66
+ argTypes: ['i32'],
67
+ returns: 'i32'
67
68
  },
68
69
  [Opcodes.i32_load16_u]: {
69
- c: `${CValtype.i16} out;
70
+ c: `u16 out;
70
71
  memcpy(&out, _memory + offset + pointer, sizeof(out));
71
72
  return out;`,
72
73
  args: ['pointer'],
73
- argTypes: [CValtype.i32],
74
- returns: CValtype.i32
74
+ argTypes: ['i32'],
75
+ returns: 'i32'
75
76
  },
76
77
  [Opcodes.i32_load8_u]: {
77
- c: `${CValtype.i8} out;
78
+ c: `u8 out;
78
79
  memcpy(&out, _memory + offset + pointer, sizeof(out));
79
80
  return out;`,
80
81
  args: ['pointer'],
81
- argTypes: [CValtype.i32],
82
- returns: CValtype.i32
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: [CValtype.i32, CValtype.f64],
89
+ argTypes: ['i32', 'f64'],
89
90
  returns: false
90
91
  },
91
92
  [Opcodes.f64_load]: {
92
- c: `${CValtype.f64} out;
93
+ c: `f64 out;
93
94
  memcpy(&out, _memory + offset + pointer, sizeof(out));
94
95
  return out;`,
95
96
  args: ['pointer'],
96
- argTypes: [CValtype.i32],
97
- returns: CValtype.f64
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(`(${CValtype.i32})(${CValtype.u32})`)) return `(${CValtype.i32})(${CValtype.u32})(` + removeBrackets(str.slice(22, -1)) + ')';
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
- prependMain.set('_data', data.map(x => `memcpy(_memory + ${x.offset}, (unsigned char[]){${x.bytes.join(',')}}, ${x.bytes.length});`).join('\n'));
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 * 2 + brDepth * 2)}${str}${semi ? ';' : ''}\n`;
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
- 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`;
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(`(${CValtype.i32})(${vals.pop()})`);
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(`(${CValtype.u32})(${vals.pop()})`);
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(`(${CValtype.i32})(${removeBrackets(vals.pop())})`); // this is ~10x faster with clang??
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(`(${CValtype.f64})(${removeBrackets(vals.pop())})`);
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(`(${CValtype.i32})`)) cond = `${cond.slice(`(${CValtype.i32})`.length)}) == 0e+0`;
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
- line(`return${returns ? ` (struct ReturnValue){ ${removeBrackets(vals.pop())}, ${removeBrackets(vals.pop())} }` : ''}`);
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(`(${CValtype.i32})`)) cond = `(${cond.slice(`(${CValtype.i32})`.length)}) != 0e+0`;
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(`printf("%c", (int)(${vals.pop()}))`);
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
- line(`const struct ReturnValue _ = ${sanitize(func.name)}(${args.join(', ')})`);
478
- vals.push(`_.value`);
479
- vals.push(`_.type`);
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(`(${CValtype.i32})`)) cond = `(${cond.slice(`(${CValtype.i32})`.length)}) != 0e+0`;
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}(${CValtype.i32} align, ${CValtype.i32} offset, ${func.args.map((x, i) => `${func.argTypes[i]} ${x}`).join(', ')}) {\n ${func.c.replaceAll('\n', '\n ')}\n}\n`);
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();