watr 1.4.1 → 2.0.0
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/package.json +14 -3
- package/readme.md +34 -78
- package/src/compile.js +108 -141
- package/src/const.js +19 -18
package/package.json
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "watr",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "Ligth & fast WAT compiler",
|
|
5
5
|
"main": "watr.js",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": "./watr.js",
|
|
8
|
+
"./parse": "./src/parse.js",
|
|
9
|
+
"./print": "./src/print.js",
|
|
10
|
+
"./compile": "./src/compile.js"
|
|
11
|
+
},
|
|
6
12
|
"type": "module",
|
|
7
13
|
"scripts": {
|
|
8
14
|
"test": "node test"
|
|
@@ -16,11 +22,16 @@
|
|
|
16
22
|
],
|
|
17
23
|
"keywords": [
|
|
18
24
|
"wat",
|
|
25
|
+
"wasm",
|
|
26
|
+
"wat2wasm",
|
|
19
27
|
"compiler",
|
|
20
|
-
"wabt"
|
|
28
|
+
"wabt",
|
|
29
|
+
"pretty-print",
|
|
30
|
+
"webassembly",
|
|
31
|
+
"wasm-text"
|
|
21
32
|
],
|
|
22
33
|
"author": "Dmitry Iv",
|
|
23
|
-
"license": "
|
|
34
|
+
"license": "MIT",
|
|
24
35
|
"bugs": {
|
|
25
36
|
"url": "https://github.com/audio-lab/watr/issues"
|
|
26
37
|
},
|
package/readme.md
CHANGED
|
@@ -1,25 +1,16 @@
|
|
|
1
|
-
# watr [](https://github.com/audio-lab/watr/actions/workflows/test.js.yml) [](https://bundlephobia.com/package/watr)
|
|
2
2
|
|
|
3
|
-
>
|
|
3
|
+
> Bare minimum wasm text compiler & formatter.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
Useful as WASM API layer, eg. for hi-level languages or for dynamic (in-browser?) compilation.
|
|
7
|
-
<!--, eg. [sonl](https://github.com/audio-lab/sonl). -->
|
|
5
|
+
Light & fast alternative for [wat2wasm](https://github.com/AssemblyScript/wabt.js), useful for hi-level languages or dynamic (in-browser) compilation.<br>
|
|
8
6
|
|
|
9
7
|
<!-- See [REPL](https://audio-lab.github.io/watr/repl.html).-->
|
|
10
8
|
|
|
11
|
-
<!--
|
|
12
|
-
| watr | wat-compiler | wabt
|
|
13
|
-
---|---|---|---
|
|
14
|
-
Size (gzipped) | 2.8kb | 6kb | 300kb
|
|
15
|
-
Performance (op/s) | 45000 | 2500 | 3100
|
|
16
|
-
-->
|
|
17
|
-
|
|
18
9
|
| Size (gzipped) | Performance (op/s)
|
|
19
10
|
---|---|---
|
|
20
|
-
watr | 3.8 kb |
|
|
21
|
-
[wat-compiler](https://github.com/stagas/wat-compiler) | 6 kb |
|
|
22
|
-
[wabt](https://github.com/AssemblyScript/wabt.js) | 300 kb |
|
|
11
|
+
watr | 3.8 kb | 5950
|
|
12
|
+
[wat-compiler](https://github.com/stagas/wat-compiler) | 6 kb | 348
|
|
13
|
+
[wabt](https://github.com/AssemblyScript/wabt.js) | 300 kb | 574
|
|
23
14
|
|
|
24
15
|
## Usage
|
|
25
16
|
|
|
@@ -43,32 +34,17 @@ double(108) // 216
|
|
|
43
34
|
|
|
44
35
|
## API
|
|
45
36
|
|
|
46
|
-
### Parse
|
|
47
|
-
|
|
48
|
-
Parser converts input Wasm text string to syntax tree.
|
|
49
|
-
|
|
50
|
-
```js
|
|
51
|
-
import { parse } from 'watr
|
|
52
|
-
|
|
53
|
-
parse(`(func (export "double") (param f64) (result f64) (f64.mul (local.get 0) (f64.const 2)))`)
|
|
54
|
-
|
|
55
|
-
// [
|
|
56
|
-
// 'func', ['export', '"double"'], ['param', 'f64'], ['result', 'f64'],
|
|
57
|
-
// ['f64.mul', ['local.get', 0], ['f64.const', 2]]
|
|
58
|
-
// ]
|
|
59
|
-
```
|
|
60
|
-
|
|
61
37
|
### Compile
|
|
62
38
|
|
|
63
|
-
Compiles
|
|
39
|
+
Compiles wasm text or syntax tree into wasm binary.
|
|
64
40
|
|
|
65
41
|
```js
|
|
66
42
|
import { compile } from 'watr'
|
|
67
43
|
|
|
68
|
-
const buffer = compile(
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
44
|
+
const buffer = compile(`(func (export "double")
|
|
45
|
+
(param f64) (result f64)
|
|
46
|
+
(f64.mul (local.get 0) (f64.const 2))
|
|
47
|
+
)`)
|
|
72
48
|
const module = new WebAssembly.Module(buffer)
|
|
73
49
|
const instance = new WebAssembly.Instance(module)
|
|
74
50
|
const {double} = instance.exports
|
|
@@ -78,20 +54,20 @@ double(108) // 216
|
|
|
78
54
|
|
|
79
55
|
### Print
|
|
80
56
|
|
|
81
|
-
Format input
|
|
57
|
+
Format input wasm text or syntax tree into minified or pretty form.
|
|
82
58
|
|
|
83
59
|
```js
|
|
84
60
|
import { print } from 'watr'
|
|
85
61
|
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
62
|
+
const src = `(func (export "double")
|
|
63
|
+
(param f64) (result f64)
|
|
64
|
+
(f64.mul (local.get 0) (f64.const 2))
|
|
65
|
+
)`
|
|
90
66
|
|
|
91
67
|
// pretty-print (default)
|
|
92
|
-
|
|
93
|
-
indent: ' ',
|
|
94
|
-
newline: '\n',
|
|
68
|
+
print(src, {
|
|
69
|
+
indent: ' ',
|
|
70
|
+
newline: '\n',
|
|
95
71
|
})
|
|
96
72
|
// (func (export "double")
|
|
97
73
|
// (param f64) (result f64)
|
|
@@ -100,53 +76,34 @@ const str = print(tree, {
|
|
|
100
76
|
// (f64.const 2)))
|
|
101
77
|
|
|
102
78
|
// minify
|
|
103
|
-
|
|
79
|
+
print(src, {
|
|
104
80
|
indent: false,
|
|
105
81
|
newline: false
|
|
106
82
|
})
|
|
107
83
|
// (func (export "double")(param f64)(result f64)(f64.mul (local.get 0)(f64.const 2)))
|
|
108
84
|
```
|
|
109
85
|
|
|
110
|
-
|
|
111
|
-
## Limitations
|
|
112
|
-
|
|
113
|
-
It may miss some edge cases and nice error messages.
|
|
114
|
-
For better REPL/dev experience use [wabt](https://github.com/AssemblyScript/wabt.js).
|
|
115
|
-
|
|
86
|
+
### Parse
|
|
116
87
|
|
|
117
|
-
|
|
88
|
+
Parse input wasm text into syntax tree.
|
|
118
89
|
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
i32.const 1 ;; ✘ stacked arguments
|
|
122
|
-
drop
|
|
123
|
-
i32.const 0
|
|
124
|
-
i32.load offset=0 align=4 ;; ✘ ungrouped immediates
|
|
125
|
-
)
|
|
90
|
+
```js
|
|
91
|
+
import { parse } from 'watr'
|
|
126
92
|
|
|
127
|
-
(func (result
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
93
|
+
parse(`(func (export "double") (param f64) (result f64) (f64.mul (local.get 0) (f64.const 2)))`)
|
|
94
|
+
// [
|
|
95
|
+
// 'func', ['export', '"double"'], ['param', 'f64'], ['result', 'f64'],
|
|
96
|
+
// ['f64.mul', ['local.get', 0], ['f64.const', 2]]
|
|
97
|
+
// ]
|
|
131
98
|
```
|
|
132
99
|
|
|
133
|
-
|
|
134
|
-
(local.get 0) ;; ✘ stacked argument
|
|
135
|
-
if (result i32) ;; ✘ inline instruction
|
|
136
|
-
(i32.const 1)
|
|
137
|
-
end
|
|
138
|
-
|
|
139
|
-
(if (result i32) (local.get 0) ;; ✔ explicit signature
|
|
140
|
-
(i32.const 1)
|
|
141
|
-
)
|
|
142
|
-
```
|
|
100
|
+
## Limitations
|
|
143
101
|
|
|
144
|
-
|
|
145
|
-
(f32.const 0x1.fffffep+127) ;; ✘ floating HEX - not supported
|
|
146
|
-
```
|
|
147
|
-
-->
|
|
102
|
+
No floating HEX support, eg. `(f32.const 0x1.fffffep+127)`.
|
|
148
103
|
|
|
104
|
+
## Projects using watr
|
|
149
105
|
|
|
106
|
+
* [auro](https://github.com/audio-lab/auro) – audio processing language
|
|
150
107
|
|
|
151
108
|
## Useful links
|
|
152
109
|
|
|
@@ -162,20 +119,19 @@ end
|
|
|
162
119
|
* [wabt source search](https://github.com/WebAssembly/wabt/search?p=5&q=then)
|
|
163
120
|
* [wat control flow](https://developer.mozilla.org/en-US/docs/WebAssembly/Reference/Control_flow)
|
|
164
121
|
* [ontouchstart wasm book](https://ontouchstart.pages.dev/chapter_wasm_binary)
|
|
165
|
-
* [wat-compiler](https://github.com/stagas/wat-compiler/)
|
|
166
122
|
* [hackernoon](https://web.archive.org/web/20210215171830/https://hackernoon.com/webassembly-binary-format-explained-part-2-hj1t33yp?source=rss)
|
|
167
123
|
* [webassemblyjs](https://github.com/xtuc/webassemblyjs)
|
|
168
124
|
* [chasm](https://github.com/ColinEberhardt/chasm/blob/master/src/encoding.ts)
|
|
169
125
|
* [WebBS](https://github.com/j-s-n/WebBS)
|
|
170
126
|
* [leb128a](https://github.com/minhducsun2002/leb128/blob/master/src/index.ts)
|
|
171
127
|
* [leb128b](https://github.com/shmishtopher/wasm-LEB128/tree/master/esm)
|
|
172
|
-
|
|
173
128
|
-->
|
|
174
129
|
|
|
175
130
|
## Alternatives
|
|
176
131
|
|
|
177
132
|
* [wabt](https://www.npmjs.com/package/wabt) − port of WABT for the web, de-facto standard.
|
|
178
133
|
* [wat-compiler](https://www.npmjs.com/package/wat-compiler) − compact alternative for WABT, older brother of _watr_.
|
|
134
|
+
* [wassemble](https://github.com/wingo/wassemble)
|
|
179
135
|
* [web49](https://github.com/FastVM/Web49)
|
|
180
136
|
|
|
181
137
|
<p align=center><a href="https://github.com/krsnzd/license/">🕉</a></p>
|
package/src/compile.js
CHANGED
|
@@ -2,8 +2,7 @@ import { uleb, leb, bigleb, f64, f32 } from './util.js'
|
|
|
2
2
|
import { OP, SECTION, ALIGN, TYPE, KIND } from './const.js'
|
|
3
3
|
import parse from './parse.js'
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
const INLINE = { loop: 1, block: 1, if: 1, end: -1, return: -1 }
|
|
5
|
+
const OP_END = 0xb, OP_I32_CONST = 0x41, OP_I64_CONST = 0x42, OP_F32_CONST = 0x43, OP_F64_CONST = 0x44
|
|
7
6
|
|
|
8
7
|
// convert wat tree to wasm binary
|
|
9
8
|
export default (nodes) => {
|
|
@@ -43,14 +42,12 @@ export default (nodes) => {
|
|
|
43
42
|
for (let node of nodes) {
|
|
44
43
|
node[0] === name ? postcall.push(build[name](node, sections)) : remaining.push(node)
|
|
45
44
|
}
|
|
46
|
-
|
|
47
45
|
nodes = remaining
|
|
48
46
|
}
|
|
49
47
|
|
|
50
48
|
// code must be compiled after all definitions
|
|
51
49
|
for (let cb of postcall) cb && cb.call && cb()
|
|
52
50
|
|
|
53
|
-
|
|
54
51
|
// 3. build binary
|
|
55
52
|
for (let name in sections) {
|
|
56
53
|
let items = sections[name]
|
|
@@ -99,7 +96,7 @@ const build = {
|
|
|
99
96
|
// (func $name? ...params result ...body)
|
|
100
97
|
func([, ...body], ctx) {
|
|
101
98
|
let locals = [], // list of local variables
|
|
102
|
-
|
|
99
|
+
blocks = [] // control instructions / blocks stack
|
|
103
100
|
|
|
104
101
|
// fn name
|
|
105
102
|
if (body[0]?.[0] === '$') ctx.func[body.shift()] = ctx.func.length
|
|
@@ -125,164 +122,134 @@ const build = {
|
|
|
125
122
|
// squash local types
|
|
126
123
|
let locTypes = locals.reduce((a, type) => (type == a[a.length - 1] ? a[a.length - 2]++ : a.push(1, type), a), [])
|
|
127
124
|
|
|
128
|
-
//
|
|
129
|
-
const
|
|
130
|
-
|
|
131
|
-
let opCode = OP[op], argc = 0, before = [], after = [], id
|
|
132
|
-
|
|
133
|
-
// NOTE: we could reorganize ops by groups and detect signature as `op in STORE`
|
|
134
|
-
// but numeric comparison is faster than generic hash lookup
|
|
135
|
-
// FIXME: we often use OP.end or alike: what if we had list of global constants?
|
|
136
|
-
|
|
137
|
-
// binary/unary
|
|
138
|
-
if (opCode >= 69) {
|
|
139
|
-
argc = opCode >= 167 ||
|
|
140
|
-
(opCode <= 159 && opCode >= 153) ||
|
|
141
|
-
(opCode <= 145 && opCode >= 139) ||
|
|
142
|
-
(opCode <= 123 && opCode >= 121) ||
|
|
143
|
-
(opCode <= 105 && opCode >= 103) ||
|
|
144
|
-
opCode == 80 || opCode == 69 ? 1 : 2
|
|
145
|
-
}
|
|
146
|
-
// instruction
|
|
147
|
-
else {
|
|
148
|
-
// (i32.store align=n offset=m at value)
|
|
149
|
-
if (opCode >= 40 && opCode <= 62) {
|
|
150
|
-
// FIXME: figure out point in Math.log2 aligns
|
|
151
|
-
let o = { align: ALIGN[op], offset: 0 }, p
|
|
152
|
-
while (nodes[0]?.includes('=')) p = nodes.shift().split('='), o[p[0]] = Number(p[1])
|
|
153
|
-
after = [Math.log2(o.align), ...uleb(o.offset)]
|
|
154
|
-
argc = opCode >= 54 ? 2 : 1
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
// (i32.const 123)
|
|
158
|
-
else if (opCode >= 65 && opCode <= 68) {
|
|
159
|
-
after = (opCode == 65 ? leb : opCode == 66 ? bigleb : opCode == 67 ? f32 : f64)(nodes.shift())
|
|
160
|
-
}
|
|
125
|
+
// convert sequence of instructions from input nodes to out bytes
|
|
126
|
+
const consume = (nodes, out = []) => {
|
|
127
|
+
if (!nodes?.length) return out
|
|
161
128
|
|
|
162
|
-
|
|
163
|
-
else if (opCode >= 32 && opCode <= 34) {
|
|
164
|
-
after = uleb(nodes[0]?.[0] === '$' ? params[id = nodes.shift()] || locals[id] : nodes.shift())
|
|
165
|
-
if (opCode > 32) argc = 1
|
|
166
|
-
}
|
|
129
|
+
let op = nodes.shift(), opCode, args = nodes, immed, id, group
|
|
167
130
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
131
|
+
// groups are flattened, eg. (cmd z w) -> z w cmd
|
|
132
|
+
if (group = Array.isArray(op)) {
|
|
133
|
+
args = [...op] // op is immutable
|
|
134
|
+
opCode = OP.indexOf(op = args.shift())
|
|
135
|
+
}
|
|
136
|
+
else opCode = OP.indexOf(op)
|
|
173
137
|
|
|
174
|
-
|
|
175
|
-
else if (opCode == 16) {
|
|
176
|
-
let fnName = nodes.shift()
|
|
177
|
-
after = uleb(id = fnName[0] === '$' ? ctx.func[fnName] ?? err('Unknown function `' + fnName + '`') : fnName);
|
|
178
|
-
// FIXME: how to get signature of imported function
|
|
179
|
-
[, argc] = ctx.type[ctx.func[id][0]]
|
|
180
|
-
}
|
|
138
|
+
// NOTE: numeric comparison is faster than generic hash lookup
|
|
181
139
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
let typeId = nodes.shift()[1];
|
|
185
|
-
[, argc] = ctx.type[typeId = typeId[0] === '$' ? ctx.type[typeId] : typeId]
|
|
186
|
-
argc++
|
|
187
|
-
after = uleb(typeId), after.push(0) // extra afterediate indicates table idx (reserved)
|
|
188
|
-
}
|
|
140
|
+
// binary/unary - just consume immed
|
|
141
|
+
if (opCode >= 69) { }
|
|
189
142
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
143
|
+
// (i32.store align=n offset=m at value)
|
|
144
|
+
else if (opCode >= 40 && opCode <= 62) {
|
|
145
|
+
// FIXME: figure out point in Math.log2 aligns
|
|
146
|
+
let o = { align: ALIGN[op], offset: 0 }, param
|
|
147
|
+
while (args[0]?.includes('=')) param = args.shift().split('='), o[param[0]] = Number(param[1])
|
|
148
|
+
immed = [Math.log2(o.align), ...uleb(o.offset)]
|
|
149
|
+
}
|
|
195
150
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
after = [TYPE[type]]
|
|
201
|
-
|
|
202
|
-
argc = 0, before.push(...instr(nodes.shift()))
|
|
203
|
-
let body
|
|
204
|
-
if (nodes[0]?.[0] === 'then') [, ...body] = nodes.shift(); else body = nodes
|
|
205
|
-
after.push(...consume(body))
|
|
206
|
-
|
|
207
|
-
callstack.pop(), callstack.push(OP.else)
|
|
208
|
-
if (nodes[0]?.[0] === 'else') {
|
|
209
|
-
[, ...body] = nodes.shift()
|
|
210
|
-
if (body.length) after.push(OP.else, ...consume(body))
|
|
211
|
-
}
|
|
212
|
-
callstack.pop()
|
|
213
|
-
after.push(OP.end)
|
|
214
|
-
}
|
|
151
|
+
// (i32.const 123)
|
|
152
|
+
else if (opCode >= 65 && opCode <= 68) {
|
|
153
|
+
immed = (opCode == 65 ? leb : opCode == 66 ? bigleb : opCode == 67 ? f32 : f64)(args.shift())
|
|
154
|
+
}
|
|
215
155
|
|
|
216
|
-
|
|
217
|
-
|
|
156
|
+
// (local.get $id), (local.tee $id x)
|
|
157
|
+
else if (opCode >= 32 && opCode <= 34) {
|
|
158
|
+
immed = uleb(args[0]?.[0] === '$' ? params[id = args.shift()] || locals[id] : args.shift())
|
|
159
|
+
}
|
|
218
160
|
|
|
219
|
-
|
|
220
|
-
|
|
161
|
+
// (global.get id), (global.set id)
|
|
162
|
+
else if (opCode == 35 || opCode == 36) {
|
|
163
|
+
immed = uleb(args[0]?.[0] === '$' ? ctx.global[args.shift()] : args.shift())
|
|
164
|
+
}
|
|
221
165
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
166
|
+
// (call id ...nodes)
|
|
167
|
+
else if (opCode == 16) {
|
|
168
|
+
let fnName = args.shift()
|
|
169
|
+
immed = uleb(id = fnName[0] === '$' ? ctx.func[fnName] ?? err('Unknown function `' + fnName + '`') : fnName);
|
|
170
|
+
// FIXME: how to get signature of imported function
|
|
171
|
+
}
|
|
228
172
|
|
|
229
|
-
|
|
230
|
-
|
|
173
|
+
// (call_indirect (type $typeName) (idx) ...nodes)
|
|
174
|
+
else if (opCode == 17) {
|
|
175
|
+
let typeId = args.shift()[1];
|
|
176
|
+
typeId = typeId[0] === '$' ? ctx.type[typeId] : typeId
|
|
177
|
+
immed = uleb(typeId), immed.push(0) // extra immediate indicates table idx (reserved)
|
|
178
|
+
}
|
|
231
179
|
|
|
232
|
-
|
|
233
|
-
|
|
180
|
+
// FIXME (memory.grow $idx?)
|
|
181
|
+
else if (opCode == 63 || opCode == 64) {
|
|
182
|
+
immed = [0]
|
|
183
|
+
}
|
|
234
184
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
185
|
+
// (if (result i32)? (local.get 0) (then a b) (argse a b)?)
|
|
186
|
+
else if (opCode == 4) {
|
|
187
|
+
blocks.push(opCode)
|
|
188
|
+
let [, type] = args[0][0] === 'result' ? args.shift() : [, 'void']
|
|
189
|
+
immed = [TYPE[type]] // FIXME: what if that's multiple values return?
|
|
190
|
+
|
|
191
|
+
// (if ... (then) (else)) -> `... if ... else ... end`
|
|
192
|
+
if (group) {
|
|
193
|
+
nodes.unshift('end')
|
|
194
|
+
if (args[args.length - 1][0] === 'else') nodes.unshift(args.pop())
|
|
195
|
+
if (args[args.length - 1][0] === 'then') nodes.unshift(args.pop())
|
|
241
196
|
}
|
|
197
|
+
}
|
|
198
|
+
// (else)
|
|
199
|
+
else if (opCode === 5) {
|
|
200
|
+
// ignore empty else
|
|
201
|
+
if (!args.length && nodes[0] === 'end') return
|
|
202
|
+
// (else xxx) -> else xxx
|
|
203
|
+
if (group) while (args.length) nodes.unshift(args.pop())
|
|
204
|
+
}
|
|
242
205
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
206
|
+
// (block ...), (loop ...)
|
|
207
|
+
else if (opCode == 2 || opCode == 3) {
|
|
208
|
+
blocks.push(opCode)
|
|
209
|
+
if (args[0]?.[0] === '$') (blocks[args.shift()] = blocks.length)
|
|
210
|
+
let [, type] = args[0]?.[0] === 'result' ? args.shift() : [, 'void']
|
|
211
|
+
immed = [TYPE[type]]
|
|
212
|
+
// (block xxx) -> block xxx end
|
|
213
|
+
if (group) {
|
|
214
|
+
nodes.unshift('end')
|
|
215
|
+
while (args.length) nodes.unshift(args.pop())
|
|
249
216
|
}
|
|
250
|
-
|
|
251
|
-
else if (opCode == null) err(`Unknown instruction \`${op}\``)
|
|
252
217
|
}
|
|
253
218
|
|
|
254
|
-
//
|
|
255
|
-
if (
|
|
256
|
-
while (argc--) before.push(...instr(nodes.shift()))
|
|
257
|
-
if (nodes.length) err(`Too many arguments for \`${op}\`.`)
|
|
219
|
+
// (end)
|
|
220
|
+
else if (opCode == 0x0b) blocks.pop()
|
|
258
221
|
|
|
259
|
-
|
|
260
|
-
|
|
222
|
+
// (br $label result?)
|
|
223
|
+
// (br_if $label cond result?)
|
|
224
|
+
else if (opCode == 0x0c || opCode == 0x0d) {
|
|
225
|
+
// br index indicates how many block items to pop
|
|
226
|
+
immed = uleb(args[0]?.[0] === '$' ? blocks.length - blocks[args.shift()] : args.shift())
|
|
227
|
+
}
|
|
261
228
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
if (typeof node === 'string') {
|
|
269
|
-
// permit some inline instructions: loop $label ... end, br $label, arg return
|
|
270
|
-
if (c = INLINE[node]) {
|
|
271
|
-
node = [node], node.inline = true
|
|
272
|
-
if (c > 0) nodes[0]?.[0] === '$' && node.push(nodes.shift())
|
|
273
|
-
}
|
|
274
|
-
else err(`Inline instruction \`${node}\` is not supported`)
|
|
275
|
-
}
|
|
229
|
+
// (br_table 1 2 3 4 0 selector result?)
|
|
230
|
+
else if (opCode == 0x0e) {
|
|
231
|
+
immed = []
|
|
232
|
+
while (!Array.isArray(args[0])) id = args.shift(), immed.push(...uleb(id[0][0] === '$' ? blocks.length - blocks[id] : id))
|
|
233
|
+
immed.unshift(...uleb(immed.length - 1))
|
|
234
|
+
}
|
|
276
235
|
|
|
277
|
-
|
|
236
|
+
// if group (cmd im1 im2 arg1 arg2) - insert any remaining args first: arg1 arg2
|
|
237
|
+
// because inline case has them in stack already
|
|
238
|
+
if (group) {
|
|
239
|
+
while (args.length) consume(args, out)
|
|
278
240
|
}
|
|
279
|
-
|
|
241
|
+
|
|
242
|
+
// ignore (then) and other unknown instructions
|
|
243
|
+
if (opCode >= 0) out.push(opCode)
|
|
244
|
+
if (immed) out.push(...immed)
|
|
280
245
|
}
|
|
281
246
|
|
|
282
|
-
// evaluates after all definitions
|
|
247
|
+
// evaluates after all definitions (need globals, elements, data etc.)
|
|
248
|
+
// FIXME: get rid of this postcall
|
|
283
249
|
return () => {
|
|
284
|
-
|
|
285
|
-
|
|
250
|
+
const bytes = []
|
|
251
|
+
while (body.length) consume(body, bytes)
|
|
252
|
+
ctx.code.push([...uleb(bytes.length + 2 + locTypes.length), ...uleb(locTypes.length >> 1), ...locTypes, ...bytes, OP_END])
|
|
286
253
|
}
|
|
287
254
|
},
|
|
288
255
|
|
|
@@ -372,8 +339,8 @@ const build = {
|
|
|
372
339
|
|
|
373
340
|
// (i32.const 0) - instantiation time initializer
|
|
374
341
|
const iinit = ([op, literal], ctx) => op[0] === 'f' ?
|
|
375
|
-
[
|
|
376
|
-
[
|
|
342
|
+
[op[1] === '3' ? OP_F32_CONST : OP_F64_CONST, ...(op[1] === '3' ? f32 : f64)(literal), OP_END] :
|
|
343
|
+
[op[1] === '3' ? OP_I32_CONST : OP_I64_CONST, ...(op[1] === '3' ? leb : bigleb)(literal[0] === '$' ? ctx.global[literal] : literal), OP_END]
|
|
377
344
|
|
|
378
345
|
const escape = { n: 10, r: 13, t: 9, v: 1 }
|
|
379
346
|
|
package/src/const.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
// ref: https://github.com/stagas/wat-compiler/blob/main/lib/const.js
|
|
2
2
|
// NOTE: squashing into a string doesn't save up gzipped size
|
|
3
3
|
export const OP = [
|
|
4
|
-
'unreachable', 'nop', 'block', 'loop', 'if', 'else',
|
|
5
|
-
'end', 'br', 'br_if', 'br_table', 'return', 'call', 'call_indirect',
|
|
6
|
-
'drop', 'select',
|
|
7
|
-
'local.get', 'local.set', 'local.tee', 'global.get', 'global.set',
|
|
4
|
+
'unreachable', 'nop', 'block', 'loop', 'if', 'else', , , , , ,
|
|
5
|
+
'end', 'br', 'br_if', 'br_table', 'return', 'call', 'call_indirect', , , , , , , , ,
|
|
6
|
+
'drop', 'select', , , , ,
|
|
7
|
+
'local.get', 'local.set', 'local.tee', 'global.get', 'global.set', , , ,
|
|
8
8
|
'i32.load', 'i64.load', 'f32.load', 'f64.load',
|
|
9
9
|
'i32.load8_s', 'i32.load8_u', 'i32.load16_s', 'i32.load16_u',
|
|
10
10
|
'i64.load8_s', 'i64.load8_u', 'i64.load16_s', 'i64.load16_u', 'i64.load32_s', 'i64.load32_u',
|
|
@@ -14,8 +14,8 @@ export const OP = [
|
|
|
14
14
|
'i32.const', 'i64.const', 'f32.const', 'f64.const',
|
|
15
15
|
'i32.eqz', 'i32.eq', 'i32.ne', 'i32.lt_s', 'i32.lt_u', 'i32.gt_s', 'i32.gt_u', 'i32.le_s', 'i32.le_u', 'i32.ge_s', 'i32.ge_u',
|
|
16
16
|
'i64.eqz', 'i64.eq', 'i64.ne', 'i64.lt_s', 'i64.lt_u', 'i64.gt_s', 'i64.gt_u', 'i64.le_s', 'i64.le_u', 'i64.ge_s', 'i64.ge_u',
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
'f32.eq', 'f32.ne', 'f32.lt', 'f32.gt', 'f32.le', 'f32.ge',
|
|
18
|
+
'f64.eq', 'f64.ne', 'f64.lt', 'f64.gt', 'f64.le', 'f64.ge',
|
|
19
19
|
'i32.clz', 'i32.ctz', 'i32.popcnt', 'i32.add', 'i32.sub', 'i32.mul', 'i32.div_s', 'i32.div_u', 'i32.rem_s', 'i32.rem_u', 'i32.and', 'i32.or', 'i32.xor', 'i32.shl', 'i32.shr_s', 'i32.shr_u', 'i32.rotl', 'i32.rotr',
|
|
20
20
|
'i64.clz', 'i64.ctz', 'i64.popcnt', 'i64.add', 'i64.sub', 'i64.mul', 'i64.div_s', 'i64.div_u', 'i64.rem_s', 'i64.rem_u', 'i64.and', 'i64.or', 'i64.xor', 'i64.shl', 'i64.shr_s', 'i64.shr_u', 'i64.rotl', 'i64.rotr',
|
|
21
21
|
'f32.abs', 'f32.neg', 'f32.ceil', 'f32.floor', 'f32.trunc', 'f32.nearest', 'f32.sqrt', 'f32.add', 'f32.sub', 'f32.mul', 'f32.div', 'f32.min', 'f32.max', 'f32.copysign',
|
|
@@ -27,15 +27,16 @@ export const OP = [
|
|
|
27
27
|
'f64.convert_i32_s', 'f64.convert_i32_u', 'f64.convert_i64_s', 'f64.convert_i64_u', 'f64.promote_f32',
|
|
28
28
|
'i32.reinterpret_f32', 'i64.reinterpret_f64', 'f32.reinterpret_i32', 'f64.reinterpret_i64',
|
|
29
29
|
],
|
|
30
|
-
SECTION = { type:1, import:2, func:3, table:4, memory:5, global:6, export:7, start:8, elem:9, code:10, data:11 },
|
|
31
|
-
TYPE = { i32:0x7f, i64:0x7e, f32:0x7d, f64:0x7c, void:0x40, func:0x60, funcref:0x70 },
|
|
32
|
-
KIND = { func: 0, table: 1, memory: 2, global: 3 },
|
|
33
|
-
ALIGN = {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
|
|
30
|
+
SECTION = { type: 1, import: 2, func: 3, table: 4, memory: 5, global: 6, export: 7, start: 8, elem: 9, code: 10, data: 11 },
|
|
31
|
+
TYPE = { i32: 0x7f, i64: 0x7e, f32: 0x7d, f64: 0x7c, void: 0x40, func: 0x60, funcref: 0x70 },
|
|
32
|
+
KIND = { func: 0, table: 1, memory: 2, global: 3 },
|
|
33
|
+
ALIGN = {
|
|
34
|
+
'i32.load': 4, 'i64.load': 8, 'f32.load': 4, 'f64.load': 8,
|
|
35
|
+
'i32.load8_s': 1, 'i32.load8_u': 1, 'i32.load16_s': 2, 'i32.load16_u': 2,
|
|
36
|
+
'i64.load8_s': 1, 'i64.load8_u': 1, 'i64.load16_s': 2, 'i64.load16_u': 2, 'i64.load32_s': 4, 'i64.load32_u': 4, 'i32.store': 4,
|
|
37
|
+
'i64.store': 8, 'f32.store': 4, 'f64.store': 8,
|
|
38
|
+
'i32.store8': 1, 'i32.store16': 2, 'i64.store8': 1, 'i64.store16': 2, 'i64.store32': 4,
|
|
39
|
+
},
|
|
40
|
+
BLOCK = {
|
|
41
|
+
loop: 1, block: 1, if: 1, end: -1, return: -1
|
|
42
|
+
}
|