porffor 0.0.0-ba812f2 → 0.0.0-beff13f

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # porffor
2
2
  a basic experimental wip *aot* optimizing js -> wasm/c engine/compiler/runtime in js. not serious/intended for (real) use. (this is a straight forward, honest readme)<br>
3
- age: ~1 month
3
+ age: ~2 months
4
4
 
5
5
  ## design
6
6
  porffor is a very unique js engine, due a very different approach. it is seriously limited, but what it can do, it does pretty well. key differences:
@@ -83,6 +83,8 @@ these include some early (stage 1/0) and/or dead (last commit years ago) proposa
83
83
  - truthy/falsy (eg `!'' == true`)
84
84
  - string comparison (eg `'a' == 'a'`, `'a' != 'b'`)
85
85
  - nullish coalescing operator (`??`)
86
+ - `for...of` (arrays and strings)
87
+ - array member setting (`arr[0] = 2`, `arr[0] += 2`, etc)
86
88
 
87
89
  ### built-ins
88
90
 
@@ -110,11 +112,9 @@ these include some early (stage 1/0) and/or dead (last commit years ago) proposa
110
112
  no particular order and no guarentees, just what could happen soon™
111
113
 
112
114
  - arrays
113
- - member setting (`arr[0] = 2`)
114
115
  - more of `Array` prototype
115
116
  - arrays/strings inside arrays
116
117
  - destructuring
117
- - for .. of
118
118
  - strings
119
119
  - member setting
120
120
  - objects
@@ -132,10 +132,9 @@ no particular order and no guarentees, just what could happen soon™
132
132
  - rewrite local indexes per func for smallest local header and remove unused idxs
133
133
  - smarter inline selection (snapshots?)
134
134
  - remove const ifs (`if (true)`, etc)
135
- - use data segments for initing arrays
136
135
 
137
136
  ## porfformance
138
- *for the things it supports*, porffor is blazingly faster compared to most interpreters, and engines running without JIT. for those with JIT, it is not that much slower like a traditional interpreter would be.
137
+ *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 not that much slower like a traditional interpreter would be; mostly the same or a bit faster/slower depending on what.
139
138
 
140
139
  ![Screenshot of comparison chart](https://github.com/CanadaHonk/porffor/assets/19228318/76c75264-cc68-4be1-8891-c06dc389d97a)
141
140
 
@@ -158,6 +157,7 @@ mostly for reducing size. do not really care about compiler perf/time as long as
158
157
  - remove unneeded single just used vars
159
158
  - remove unneeded blocks (no `br`s inside)
160
159
  - remove unused imports
160
+ - use data segments for initing arrays/strings
161
161
 
162
162
  ### wasm module
163
163
  - type cache/index (no repeated types)
package/c.exe CHANGED
Binary file
package/compiler/2c.js CHANGED
@@ -2,8 +2,6 @@ import { read_ieee754_binary64, read_signedLEB128 } from './encoding.js';
2
2
  import { Blocktype, Opcodes, Valtype } from './wasmSpec.js';
3
3
  import { operatorOpcode } from './expression.js';
4
4
 
5
- import fs from 'fs';
6
-
7
5
  const CValtype = {
8
6
  i8: 'char',
9
7
  i16: 'unsigned short', // presume all i16 stuff is unsigned
@@ -36,18 +34,24 @@ const todo = msg => {
36
34
  throw new TodoError(`todo: ${msg}`);
37
35
  };
38
36
 
37
+ const removeBrackets = str => str.startsWith('(') && str.endsWith(')') ? str.slice(1, -1) : str;
38
+
39
39
  export default ({ funcs, globals, tags, exceptions, pages }) => {
40
- const invOperatorOpcode = inv(operatorOpcode[valtype]);
40
+ const invOperatorOpcode = Object.values(operatorOpcode).reduce((acc, x) => {
41
+ for (const k in x) {
42
+ acc[x[k]] = k;
43
+ }
44
+ return acc;
45
+ }, {});
41
46
  const invGlobals = inv(globals, x => x.idx);
42
47
 
43
- const includes = new Map();
48
+ const includes = new Map(), unixIncludes = new Map(), winIncludes = new Map();
44
49
  let out = '';
45
50
 
46
51
  for (const x in globals) {
47
52
  const g = globals[x];
48
53
 
49
- out += `${CValtype[g.type]} ${x}`;
50
- if (x.init) out += ` ${x.init}`;
54
+ out += `${CValtype[g.type]} ${x} = ${g.init ?? 0}`;
51
55
  out += ';\n';
52
56
  }
53
57
 
@@ -58,7 +62,43 @@ export default ({ funcs, globals, tags, exceptions, pages }) => {
58
62
 
59
63
  if (out) out += '\n';
60
64
 
65
+ let depth = 1;
66
+ const line = (str, semi = true) => out += `${' '.repeat(depth * 2)}${str}${semi ? ';' : ''}\n`;
67
+ const lines = lines => {
68
+ for (const x of lines) {
69
+ out += `${' '.repeat(depth * 2)}${x}\n`;
70
+ }
71
+ };
72
+
73
+ const platformSpecific = (win, unix, add = true) => {
74
+ let tmp = '';
75
+
76
+ if (win) {
77
+ if (add) out += '#ifdef _WIN32\n';
78
+ else tmp += '#ifdef _WIN32\n';
79
+
80
+ if (add) lines(win.split('\n'));
81
+ else tmp += win + (win.endsWith('\n') ? '' : '\n');
82
+ }
83
+
84
+ if (unix) {
85
+ if (add) out += (win ? '#else' : '#ifndef _WIN32') + '\n';
86
+ else tmp += (win ? '#else' : '#ifndef _WIN32') + '\n';
87
+
88
+ if (add) lines(unix.split('\n'));
89
+ else tmp += unix + (unix.endsWith('\n') ? '' : '\n');
90
+ }
91
+
92
+ if (win || unix)
93
+ if (add) out += '#endif\n';
94
+ else tmp += '#endif\n';
95
+
96
+ return tmp;
97
+ };
98
+
61
99
  for (const f of funcs) {
100
+ depth = 1;
101
+
62
102
  const invLocals = inv(f.locals, x => x.idx);
63
103
  if (f.returns.length > 1) todo('funcs returning >1 value unsupported');
64
104
 
@@ -66,16 +106,13 @@ export default ({ funcs, globals, tags, exceptions, pages }) => {
66
106
 
67
107
  const returns = f.returns.length === 1;
68
108
 
69
- const shouldInline = ['f64_%'].includes(f.name);
109
+ const shouldInline = f.internal;
70
110
  out += `${f.name === 'main' ? 'int' : CValtype[f.returns[0]]} ${shouldInline ? 'inline ' : ''}${sanitize(f.name)}(${f.params.map((x, i) => `${CValtype[x]} ${invLocals[i]}`).join(', ')}) {\n`;
71
111
 
72
- let depth = 1;
73
- const line = (str, semi = true) => out += `${' '.repeat(depth * 2)}${str}${semi ? ';' : ''}\n`;
74
-
75
112
  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);
76
113
  for (const x of localKeys) {
77
114
  const l = f.locals[x];
78
- line(`${CValtype[l.type]} ${x}`);
115
+ line(`${CValtype[l.type]} ${x} = 0`);
79
116
  }
80
117
 
81
118
  if (localKeys.length !== 0) out += '\n';
@@ -96,7 +133,8 @@ export default ({ funcs, globals, tags, exceptions, pages }) => {
96
133
  if (['==', '!=', '>', '>=', '<', '<='].includes(op)) lastCond = true;
97
134
  else lastCond = false;
98
135
 
99
- vals.push(`${a} ${op} ${b}`);
136
+ // vals.push(`${a} ${op} ${b}`);
137
+ vals.push(`(${removeBrackets(a)} ${op} ${b})`);
100
138
  continue;
101
139
  }
102
140
 
@@ -120,6 +158,7 @@ export default ({ funcs, globals, tags, exceptions, pages }) => {
120
158
 
121
159
  switch (i[0]) {
122
160
  case Opcodes.i32_const:
161
+ case Opcodes.i64_const:
123
162
  vals.push(read_signedLEB128(i.slice(1)).toString());
124
163
  break;
125
164
 
@@ -132,24 +171,42 @@ export default ({ funcs, globals, tags, exceptions, pages }) => {
132
171
  break;
133
172
 
134
173
  case Opcodes.local_set:
135
- line(`${invLocals[i[1]]} = ${vals.pop()}`);
174
+ line(`${invLocals[i[1]]} = ${removeBrackets(vals.pop())}`);
136
175
  break;
137
176
 
138
177
  case Opcodes.local_tee:
139
- vals.push(`${invLocals[i[1]]} = ${vals.pop()}`);
178
+ line(`${invLocals[i[1]]} = ${removeBrackets(vals.pop())}`);
179
+ vals.push(`${invLocals[i[1]]}`);
180
+ // vals.push(`${invLocals[i[1]]} = ${vals.pop()}`);
181
+ break;
182
+
183
+ case Opcodes.global_get:
184
+ vals.push(`${invGlobals[i[1]]}`);
185
+ break;
186
+
187
+ case Opcodes.global_set:
188
+ line(`${invGlobals[i[1]]} = ${removeBrackets(vals.pop())}`);
140
189
  break;
141
190
 
142
191
  case Opcodes.f64_trunc:
143
192
  // vals.push(`trunc(${vals.pop()})`);
144
- vals.push(`(int)(${vals.pop()})`); // this is ~10x faster with clang. what the fuck.
193
+ vals.push(`(int)(${removeBrackets(vals.pop())})`); // this is ~10x faster with clang??
194
+ break;
195
+
196
+ case Opcodes.f64_convert_i32_u:
197
+ case Opcodes.f64_convert_i32_s:
198
+ case Opcodes.f64_convert_i64_u:
199
+ case Opcodes.f64_convert_i64_s:
200
+ // int to double
201
+ vals.push(`(double)${vals.pop()}`);
145
202
  break;
146
203
 
147
204
  case Opcodes.return:
148
- line(`return${returns ? ` ${vals.pop()}` : ''}`);
205
+ line(`return${returns ? ` ${removeBrackets(vals.pop())}` : ''}`);
149
206
  break;
150
207
 
151
208
  case Opcodes.if:
152
- let cond = vals.pop();
209
+ let cond = removeBrackets(vals.pop());
153
210
  if (!lastCond) {
154
211
  if (cond.startsWith('(long)')) cond = `${cond.slice(6)} == 1e+0`;
155
212
  else cond += ' == 1';
@@ -214,12 +271,44 @@ export default ({ funcs, globals, tags, exceptions, pages }) => {
214
271
  line(`printf("%f\\n", ${vals.pop()})`);
215
272
  includes.set('stdio.h', true);
216
273
  break;
274
+
275
+ case 'time':
276
+ line(`double _time_out`);
277
+ /* platformSpecific(
278
+ `FILETIME _time_filetime;
279
+ GetSystemTimeAsFileTime(&_time_filetime);
280
+
281
+ ULARGE_INTEGER _time_ularge;
282
+ _time_ularge.LowPart = _time_filetime.dwLowDateTime;
283
+ _time_ularge.HighPart = _time_filetime.dwHighDateTime;
284
+ _time_out = (_time_ularge.QuadPart - 116444736000000000i64) / 10000.;`,
285
+ `struct timespec _time;
286
+ clock_gettime(CLOCK_MONOTONIC, &_time);
287
+ _time_out = _time.tv_nsec / 1000000.;`); */
288
+ platformSpecific(
289
+ `LARGE_INTEGER _time_freq, _time_t;
290
+ QueryPerformanceFrequency(&_time_freq);
291
+ QueryPerformanceCounter(&_time_t);
292
+ _time_out = ((double)_time_t.QuadPart / _time_freq.QuadPart) * 1000.;`,
293
+ `struct timespec _time;
294
+ clock_gettime(CLOCK_MONOTONIC, &_time);
295
+ _time_out = _time.tv_nsec / 1000000.;`);
296
+ vals.push(`_time_out`);
297
+
298
+ unixIncludes.set('time.h', true);
299
+ winIncludes.set('windows.h', true);
300
+ break;
301
+
302
+ default:
303
+ log('2c', `unimplemented import: ${importFunc.name}`);
304
+ break;
217
305
  }
306
+
218
307
  break;
219
308
  }
220
309
 
221
310
  let args = [];
222
- for (let j = 0; j < func.params.length; j++) args.unshift(vals.pop());
311
+ for (let j = 0; j < func.params.length; j++) args.unshift(removeBrackets(vals.pop()));
223
312
 
224
313
  if (func.returns.length === 1) vals.push(`${sanitize(func.name)}(${args.join(', ')})`)
225
314
  else line(`${sanitize(func.name)}(${args.join(', ')})`);
@@ -251,7 +340,11 @@ export default ({ funcs, globals, tags, exceptions, pages }) => {
251
340
  out += '}\n\n';
252
341
  }
253
342
 
254
- out = [...includes.keys()].map(x => `#include <${x}>`).join('\n') + '\n\n' + out;
343
+ depth = 0;
344
+
345
+ const makeIncludes = includes => [...includes.keys()].map(x => `#include <${x}>\n`).join('');
346
+
347
+ out = platformSpecific(makeIncludes(winIncludes), makeIncludes(unixIncludes), false) + '\n' + makeIncludes(includes) + '\n' + out;
255
348
 
256
349
  return out;
257
350
  };
@@ -26,6 +26,12 @@ export const importedFuncs = [
26
26
  import: 't',
27
27
  params: 0,
28
28
  returns: 1
29
+ },
30
+ {
31
+ name: 'printStr',
32
+ import: 's',
33
+ params: 1,
34
+ returns: 0
29
35
  }
30
36
  ];
31
37