-
Notifications
You must be signed in to change notification settings - Fork 0
/
TODO
40 lines (33 loc) · 3.44 KB
/
TODO
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
TODO
For optimization purposes, a switch/branch-table construct would be helpful
Note that WASM's br_table requires keeping track of parent block labels, which requires context not currently available to the generator. Switches where every case ends in return/break are usually compiled to if/else chains anyway
As an ongoing task, add more semantic checks, warnings, and error messages (such as non-void functions missing a return statement, access operator used on primitively typed variables, recursive type warnings, etc.)
Project-based compilation
Should follow a similar design to TypeScript's tsconfig files
Default values for command-line arguments should be loaded from the config file if present
Exports and imports
Imports require definition of type signatures
Have two different import syntaxes; explicit (with type signatures spelled out) and implicit (resolved at compile time)
Explicit imports are assumed to be JavaScript imports and are not checked
Implicit imports are resolved by looking up a .schwa file at the relative path and compiling it to find the relevant data types
Note that only _immutable_ globals (constants) can be imported/exported
Also have both whole-module imports (import bar) and named imports (from bar import Foo)
Aliasing should also be possible: (import bar as bar), (from bar import void Foo() as Foobar), (export Foo as Bar)
The user could potentially define "mutually recursive" dependent files (both use types from each other). This is distinct from but related to recursive types (both types have properties of the other type). To support this use case, the analyzer will need to be modified to resolve imported types from other analyzer instances between the hoist and scope passes
Pointers/Memory access
There are load/store functions included in the type namespaces (i.e. int.load()) but the preferred form of memory access is mapping
Mapping a struct to an address creates a psuedo-global variable which instead of being backed by actual global variable(s) is instead converted into load/store operations relative to the specified mapping offset
In the future if we have struct initialization, those values should be filled at instantiation time via the WASM Data section
Arrays
Arrays are implemented but are currently restricted to mapped globals only; use as locals/globals/parameters will come later
They are similar to structs; always fixed-size, user code can keep a dynamic length separately if desired
To use arrays as locals/globals we could generate branch-tables that get/set the appropriate local/global using the computed index
Structs
For locals/globals, they are allocated as suffixed properties (e.g. v: { x, y } becomes "v.x", "v.y") and use scope trickery to control access
For memory-mapped use, they generate loads/stores which use static offsets from the struct's mapped address
We can potentially include byte and short types in the future, if we use i32 as backing and truncate when needed when performing operations
This would of course be more expensive than just using plain ints
For now, the load/store functions provide support for 8-bit and 16-bit numbers
Structs may include any primitives, structs, or fixed-length arrays of primitive or struct values
Warnings should be added if the struct properties are not access-aligned (shorts every 2 bytes, ints every 4 bytes, etc.)
Note that structs that are aligned on their own may become misaligned when used as part of an array or other struct