porffor 0.2.0-c7b7423 → 0.2.0-eaee2da

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,5 +1,5 @@
1
1
  # Porffor &nbsp;<sup><sub>/ˈpɔrfɔr/ &nbsp;*(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,7 +118,7 @@ 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 js
121
+ - *Basic* Wasm engine (interpreter) in JS
122
122
  - More math operators (`**`, etc)
123
123
  - `do { ... } while (...)`
124
124
  - Rewrite `console.log` to work with strings/arrays
@@ -130,7 +130,21 @@ 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
- - Use type(script) information to remove unneeded typechecker code
133
+ - Experiment with byte strings?
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
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 not that much slower like a traditional interpreter would be; mostly the same or a bit faster/slower depending on what.
@@ -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 js with the compiler
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 to come?
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 js syntax highlighting to be nicer with porffor features (eg highlighting wasm inside of inline asm).
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 takes in JS, not a different language or typescript
239
- - Porffor is made in pure JS and compiles itself, not using Binaryen/etc
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 js engine is purple colored
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
@@ -124,7 +124,7 @@ export default ({ funcs, globals, tags, exceptions, pages }) => {
124
124
  const returns = f.returns.length > 0;
125
125
 
126
126
  const shouldInline = f.internal;
127
- out += `${f.name === 'main' ? 'int' : (f.internal ? 'double' : 'struct ReturnValue')} ${shouldInline ? 'inline ' : ''}${sanitize(f.name)}(${f.params.map((x, i) => `${CValtype[x]} ${invLocals[i]}`).join(', ')}) {\n`;
127
+ out += `${f.name === 'main' ? 'int' : (f.internal ? (returns ? 'double' : 'void') : 'struct ReturnValue')} ${shouldInline ? 'inline ' : ''}${sanitize(f.name)}(${f.params.map((x, i) => `${CValtype[x]} ${invLocals[i]}`).join(', ')}) {\n`;
128
128
 
129
129
  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
130
  for (const x of localKeys) {
@@ -367,6 +367,11 @@ _time_out = _time.tv_nsec / 1000000. + _time.tv_sec * 1000.;`);
367
367
  line(`return ${vals.pop()}`);
368
368
  }
369
369
 
370
+ if (f.name === 'main') {
371
+ out += '\n';
372
+ line(`return 0`);
373
+ }
374
+
370
375
  out += '}\n\n';
371
376
  }
372
377
 
@@ -1,4 +1,4 @@
1
- import { Blocktype, Opcodes, Valtype } from "./wasmSpec.js";
1
+ import { Blocktype, Opcodes, Valtype, ValtypeSize } from "./wasmSpec.js";
2
2
  import { number, i32x4 } from "./embedding.js";
3
3
 
4
4
  export const importedFuncs = [
@@ -29,6 +29,21 @@ for (let i = 0; i < importedFuncs.length; i++) {
29
29
 
30
30
  const char = c => number(c.charCodeAt(0));
31
31
 
32
+ const printStaticStr = str => {
33
+ const out = [];
34
+
35
+ for (let i = 0; i < str.length; i++) {
36
+ out.push(
37
+ // ...number(str.charCodeAt(i)),
38
+ ...number(str.charCodeAt(i), Valtype.i32),
39
+ Opcodes.i32_from_u,
40
+ [ Opcodes.call, importedFuncs.printChar ]
41
+ );
42
+ }
43
+
44
+ return out;
45
+ };
46
+
32
47
  // todo: somehow diff between these (undefined != null) while remaining falsey in wasm as a number value
33
48
  export const UNDEFINED = 0;
34
49
  export const NULL = 0;
@@ -187,12 +202,125 @@ export const BuiltinFuncs = function() {
187
202
 
188
203
 
189
204
  this.__console_log = {
190
- params: [ valtypeBinary ],
191
- locals: [],
205
+ params: [ valtypeBinary, Valtype.i32 ],
206
+ typedParams: true,
207
+ locals: [ Valtype.i32, Valtype.i32 ],
192
208
  returns: [],
193
- wasm: [
194
- [ Opcodes.local_get, 0 ],
195
- [ Opcodes.call, importedFuncs.print ],
209
+ wasm: (scope, { TYPES, typeSwitch }) => [
210
+ ...typeSwitch(scope, [ [ Opcodes.local_get, 1 ] ], {
211
+ [TYPES.number]: [
212
+ [ Opcodes.local_get, 0 ],
213
+ [ Opcodes.call, importedFuncs.print ],
214
+ ],
215
+ [TYPES.boolean]: [
216
+ [ Opcodes.local_get, 0 ],
217
+ Opcodes.i32_to_u,
218
+ [ Opcodes.if, Blocktype.void ],
219
+ ...printStaticStr('true'),
220
+ [ Opcodes.else ],
221
+ ...printStaticStr('false'),
222
+ [ Opcodes.end ]
223
+ ],
224
+ [TYPES.string]: [
225
+ // simply print a string :))
226
+ // cache input pointer as i32
227
+ [ Opcodes.local_get, 0 ],
228
+ Opcodes.i32_to_u,
229
+ [ Opcodes.local_tee, 2 ],
230
+
231
+ // make end pointer
232
+ [ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, 0 ],
233
+ ...number(ValtypeSize.i16, Valtype.i32),
234
+ [ Opcodes.i32_mul ],
235
+
236
+ [ Opcodes.local_get, 2 ],
237
+ [ Opcodes.i32_add ],
238
+ [ Opcodes.local_set, 3 ],
239
+
240
+ [ Opcodes.loop, Blocktype.void ],
241
+
242
+ // print current char
243
+ [ Opcodes.local_get, 2 ],
244
+ [ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1, ValtypeSize.i32 ],
245
+ Opcodes.i32_from_u,
246
+ [ Opcodes.call, importedFuncs.printChar ],
247
+
248
+ // increment pointer by sizeof i16
249
+ [ Opcodes.local_get, 2 ],
250
+ ...number(ValtypeSize.i16, Valtype.i32),
251
+ [ Opcodes.i32_add ],
252
+ [ Opcodes.local_tee, 2 ],
253
+
254
+ // if pointer != end pointer, loop
255
+ [ Opcodes.local_get, 3 ],
256
+ [ Opcodes.i32_ne ],
257
+ [ Opcodes.br_if, 0 ],
258
+
259
+ [ Opcodes.end ]
260
+ ],
261
+ [TYPES._array]: [
262
+ ...printStaticStr('[ '),
263
+
264
+ // cache input pointer as i32
265
+ [ Opcodes.local_get, 0 ],
266
+ Opcodes.i32_to_u,
267
+ [ Opcodes.local_tee, 2 ],
268
+
269
+ // make end pointer
270
+ [ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, 0 ],
271
+ ...number(ValtypeSize[valtype], Valtype.i32),
272
+ [ Opcodes.i32_mul ],
273
+
274
+ [ Opcodes.local_get, 2 ],
275
+ [ Opcodes.i32_add ],
276
+ [ Opcodes.local_set, 3 ],
277
+
278
+ [ Opcodes.loop, Blocktype.void ],
279
+
280
+ // print current char
281
+ [ Opcodes.local_get, 2 ],
282
+ [ Opcodes.load, Math.log2(ValtypeSize.i16) - 1, ValtypeSize.i32 ],
283
+ [ Opcodes.call, importedFuncs.print ],
284
+
285
+ // increment pointer by sizeof valtype
286
+ [ Opcodes.local_get, 2 ],
287
+ ...number(ValtypeSize[valtype], Valtype.i32),
288
+ [ Opcodes.i32_add ],
289
+ [ Opcodes.local_tee, 2 ],
290
+
291
+ // if pointer != end pointer, print separator and loop
292
+ [ Opcodes.local_get, 3 ],
293
+ [ Opcodes.i32_ne ],
294
+ [ Opcodes.if, Blocktype.void ],
295
+ ...printStaticStr(', '),
296
+ [ Opcodes.br, 1 ],
297
+ [ Opcodes.end ],
298
+
299
+ [ Opcodes.end ],
300
+
301
+ ...printStaticStr(' ]'),
302
+ ],
303
+ [TYPES.undefined]: [
304
+ ...printStaticStr('undefined')
305
+ ],
306
+ [TYPES.function]: [
307
+ ...printStaticStr('function () {}')
308
+ ],
309
+ [TYPES.object]: [
310
+ [ Opcodes.local_get, 0 ],
311
+ Opcodes.i32_to_u,
312
+ [ Opcodes.if, Blocktype.void ],
313
+ ...printStaticStr('{}'),
314
+ [ Opcodes.else ],
315
+ ...printStaticStr('null'),
316
+ [ Opcodes.end ]
317
+ ],
318
+ default: [
319
+ [ Opcodes.local_get, 0 ],
320
+ [ Opcodes.call, importedFuncs.print ],
321
+ ]
322
+ }, Blocktype.void),
323
+
196
324
  ...char('\n'),
197
325
  [ Opcodes.call, importedFuncs.printChar ]
198
326
  ]