├── .gitignore
├── LICENSE
├── README.md
├── etc
└── memory-and-addressing-notes.md
├── package.json
├── src
├── ast.ts
├── basic-types.ts
├── emit.ts
├── eval.ts
├── info.ts
├── lbtext.ts
├── repr.ts
└── utf8.ts
├── test
├── .gitignore
├── build_factorial_test.ts
├── build_test.js
├── mem_align_checks_test.js
└── test.js
├── tsconfig.json
└── yarn.lock
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | *.min.js
3 | *.g.js
4 | *.map
5 | *.lock
6 | *.tmp
7 | build
8 | wasm-spec
9 | node_modules
10 | _local
11 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2017 Rasmus Andersson
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in
11 | all copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
20 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # wasm-util
2 |
3 | Utilities for working with WebAssembly (aka WASM), to be used with TypeScript and JavaScript.
4 | This code currently supports version MVP-13 (candidate for v1) of WebAssembly.
5 |
6 | - Want to learn more about WebAssembly? Check out ["Introduction to WebAssembly"](https://rsms.me/wasm-intro)
7 | - You can also skip the reading and [jump to "Building and testing"](#building-and-testing)
8 |
9 | **Overview:**
10 |
11 | - [`ast`](src/ast.ts) provides a full TypeScript type system for [the complete WebAssembly specification](https://github.com/WebAssembly/design).
12 | - `ast.c` provides constructors for all parts of a WebAssembly module.
13 | - `ast.t` is a table of AST node types and their respective internal symbols.
14 | - `ast.sect_id` is a table of section names and their respective identifiers as `varunit7` objects.
15 | - `ast.get` provides helper functions for convenient access and traversal of an AST.
16 | - [`emit`](src/emit.ts) provides helpers for emitting WASM byte code from an AST.
17 | - [`repr`](src/repr.ts) generates human-readable text representations of an AST or ArrayBuffer.
18 | - [`lbtext`](src/lbtext.ts) generates [Linear Bytecode text](https://github.com/WebAssembly/design/blob/master/TextFormat.md) from AST instructions
19 |
20 | I found myself relying on a very complicated tool chain (source build of llvm, binaryen, etc) while all I was looking for was to get close to WebAssembly. The prime component of wasm-util is `ast` which provides a convenient way of building complete WASM modules, with full static type-checking if you're using TypeScript.
21 |
22 | Following is an example of building a module that provides the `factorial` function.
23 | Let's start by describing the function we're making in a C-like syntax:
24 |
25 | ```cc
26 | int64 factorial(int64 n) {
27 | return (n == 0) ?
28 | 1
29 | :
30 | n * factorial(n-1);
31 | }
32 | ```
33 |
34 | The equivalent WebAssembly code looks like this:
35 |
36 | ```wasm
37 | get_local 0 // push parameter #0 on stack.
38 | i64.const 0 // push constant int64 "0" on stack.
39 | i64.eq // execute "eq" which pops two operands from stack
40 | // and pushes int32 "1" or "0" on stack.
41 | if i64 // pops one int32 from stack; if its not "0":
42 | i64.const 1 // push constant int64 "0" on stack.
43 | else // else (if operand was "0"):
44 | get_local 0 // push parameter #0 on stack. $1
45 | get_local 0 // push parameter #0 on stack.
46 | i64.const 1 // push constant int64 "0" on stack.
47 | i64.sub // execute "sub[tract]" which pops two operands
48 | // from stack (parameter #0 and constant int64 "1")
49 | // and finally pushes the result int64 on stack.
50 | call 0 // call function #0 ("factorial") which pops one
51 | // int64 from the stack and when it returns an
52 | // int64 has been pushed on stack
53 | i64.mul // execute "sub[tract]" which pops two operands
54 | // from stack ($1 and result from function call)
55 | // and finally pushes the resulting int64 on stack
56 | end // ends function, returning one int64 result (on stack.)
57 | // Stack now contains one int64 value that's the result from one of
58 | // the two branches above.
59 | ```
60 |
61 | The above code was printed by [lbtext](src/lbtext.ts), for which we provided an AST built with the [ast](src/ast.ts) module:
62 |
63 | ```js
64 | import ... 'wasm-util/ast'
65 | const mod = c.module([
66 |
67 | type_section([
68 | func_type([i64], i64), // type index = 0
69 | ]),
70 |
71 | function_section([
72 | varuint32(0), // function index = 0, using type index 0
73 | ]),
74 |
75 | export_section([
76 | // exports "factorial" as function at index 0
77 | export_entry(str_ascii("factorial"), external_kind.function, varuint32(0)),
78 | ]),
79 |
80 | code_section([
81 | // body of function at index 0:
82 | function_body([ /* additional local variables here */ ], [
83 | if_(i64, // i64 = result type of `if` expression
84 | i64.eq(get_local(i64, 0), i64.const(0)), // condition
85 | [ // then
86 | i64.const(1)
87 | ], [ // else
88 | i64.mul(
89 | get_local(i64, 0),
90 | call(i64, varuint32(0), [ // 0 = function index
91 | i64.sub(get_local(i64, 0), i64.const(1))
92 | ]))])])]
93 | )]
94 | )
95 | ```
96 |
97 | We can now generate WASM bytecode through the `Emittable` interface:
98 |
99 | ```ts
100 | const emitbuf = new BufferedEmitter(new ArrayBuffer(mod.z))
101 | mod.emit(emitbuf)
102 | // the array buffer (emitbuf.buffer) now contains the complete module code
103 | ```
104 |
105 | Or print a human-readable representation of the AST:
106 |
107 | ```ts
108 | import { strRepr } from 'wasm-util/repr'
109 | console.log(strRepr(mod))
110 | ```
111 |
112 | Which yields the following in the console:
113 |
114 | ```lisp
115 | (module 13
116 | (section type 6 1
117 | (func_type (i64) i64))
118 | (section function 2 1 0)
119 | (section export 13 1
120 | (export_entry "factorial" external_kind.function 0))
121 | (section code 25 1
122 | (function_body 23 0
123 | (if [i64]
124 | (i64.eq
125 | (get_local [0])
126 | (i64.const [0])
127 | )
128 | (then
129 | (i64.const [1]))
130 | (else
131 | (i64.mul
132 | (get_local [0])
133 | (call [0]
134 | (i64.sub
135 | (get_local [0])
136 | (i64.const [1])
137 | )))) end) end)))
138 | ```
139 |
140 | A complete version of this "factorial" demo can be found at [test/build_factorial_test.ts](test/build_factorial_test.ts).
141 |
142 | ## ast
143 |
144 | `ast` provides a full TypeScript type system for [the complete WebAssembly specification](https://github.com/WebAssembly/design) including AST constructors and access functions.
145 |
146 | Noteworthy properties of the AST:
147 |
148 | - Nodes are immutable and contains no parents, meaning that any subtree can be used in multiple locations without the need to copy any data (e.g. macros can be trivially "implemented" by building a structure once and using it multiple times.)
149 | - Nodes' underlying type structures are uniform to allow efficient JavaScript VM optimizations.
150 | - Nodes' TypeScript types are rich in expression but with almost zero effect on runtime code — i.e. the `type_section` constructor returns the same kind of underlying structure as `import_section`, but the two functions when operated in TypeScript returns two exclusive, incompatible types (`TypeSection` and `ImportSection`, respectively.)
151 | - Each node has the ability to emit WASM bytecode that represents itself in a very efficient and side-effect-free manner.
152 |
153 | The AST is built in a way that makes it as portable and light-weight as possible, with two basic types: atoms and cells. An atom is a single value and represents some amount of bytes corresponding to actual WASM bytecode. A cell is a compount structure that contains other atoms and cells, possibly also represents WASM bytecode.
154 |
155 | ```ts
156 | // N is the common type of all AST nodes
157 | interface N extends Emittable {
158 | readonly t :TypeTag // type
159 | readonly z :uint32 // size in bytes (includes size of any children)
160 | readonly v :any // value
161 | }
162 | interface Atom extends N {
163 | readonly v :T
164 | }
165 | interface Cell extends N {
166 | readonly v :T[]
167 | }
168 | interface Module ...
169 | ```
170 |
171 | For the full type system, see [`ast.ts`](src/ast.ts)
172 |
173 |
174 | ### Static type checking with TypeScript
175 |
176 | When used in TypeScript, the operand types, immediate types and result types of opcode and compound instructions are checked at compile time. For instance, say that we're converting a certain code path from i32 to i64 and forget something:
177 |
178 | ```ts
179 | i64.eq(i64.const(1), i32.const(3))
180 | ```
181 |
182 | Then the TypeScript compiler will complain:
183 |
184 | > error TS2345: Argument of type 'Op' is not assignable to parameter of type 'Op'.
185 | > Type 'I64' is not assignable to type 'I32'.
186 | > Property '_I32' is missing in type 'I64'.
187 |
188 | We can correct the error by replacing `i32.const(3)` with `i64.const(3)` in this case since the `i64.eq` function has the type signature `(i64, i64) i32`.
189 |
190 |
191 | ## emit
192 |
193 | AST nodes has the ability to efficiently produce its corresponding
194 | WASM bytecode through the `Emittable` interface:
195 |
196 | ```ts
197 | interface Emittable {
198 | emit(e :Emitter) :Emitter
199 | }
200 | ```
201 |
202 | Which takes an Emitter as its parameter and returns a potentially different Emitter which reflects the state after emitting code for the callee node. The Emitter interface looks like this:
203 |
204 | ```ts
205 | interface Emitter {
206 | writeU8(v :uint8) :Emitter
207 | writeU16(v :uint16) :Emitter
208 | writeU32(v :uint32) :Emitter
209 | writeF32(v :float32) :Emitter
210 | writeF64(v :float64) :Emitter
211 | writeBytes(v :ArrayLike) :Emitter
212 | }
213 | ```
214 |
215 | Each modifying operation returns a potentially different Emitter which is the result of
216 | the receiver + modifications, thus modifying operations should be called like this:
217 |
218 | ```js
219 | e = e.writeU32(1)
220 | e = e.writeU32(2)
221 | ```
222 |
223 | However **NOT** like this:
224 |
225 | ```js
226 | e.writeU32(1)
227 | e.writeU32(2)
228 | // e represents same state as before `e.writeU32(1)`
229 | ```
230 |
231 | This interface makes it possible to implement emitters with immutable persistent data structures.
232 |
233 | A concrete implementation of an Emitter is provided by `emit` which writes to an `ArrayBuffer`:
234 |
235 | ```ts
236 | class BufferedEmitter implements Emitter {
237 | readonly buffer :ArrayBuffer
238 | readonly view :DataView
239 | length :uint32
240 | constructor(buffer :ArrayBuffer)
241 | }
242 | ```
243 |
244 | ## repr
245 |
246 | [repr](src/repr.ts) has the ability to generate human-readable text representations of AST nodes.
247 |
248 | There's an example of using `repr` to visualize an AST earlier in this document.
249 |
250 | The `repr` function generates a S-expression representation of the AST in a form that is similar to how the AST would have been built using `ast`.
251 |
252 | The `reprBuffer` function generates rows and columns of bytes values representing
253 | an `ArrayBuffer` with optional terminal-color higlighting of a range of bytes.
254 | Useful for visualizing what an `Emitter` produces, or for pointing out bytes in a module
255 | that causes an error with the spec interpreter.
256 |
257 | ```ts
258 | function repr(n :N, w :Writer, options? :Options)
259 |
260 | function reprBuffer(
261 | buffer :ArrayBuffer,
262 | w :Writer,
263 | limit? :number,
264 | highlightRange? :number[],
265 | options? :Options)
266 |
267 | type Writer = (s :string)=>void
268 | interface Options {
269 | readonly colors :boolean // explicitly enable or disable terminal colors
270 | readonly immSeparator :string // defaults to `:`
271 | readonly detailedTypes? :boolean, // `vi32(9)` or just `9`
272 | }
273 |
274 | // Convenience function that returns a string
275 | function strRepr(n :N, options? :Options) :string
276 |
277 | // Convenience function that returns a string
278 | function strReprBuffer(
279 | buffer :ArrayBuffer,
280 | limit? :number,
281 | highlightRange? :number[],
282 | options? :Options) :string
283 | ```
284 |
285 |
286 | ## eval
287 |
288 | `eval` is rather specialized module for executing the WebAssembly spec interpreter. It only works with Nodejs as it needs access to both the file system and process spawning.
289 |
290 | The `specEval` function evaluates a WASM module and resolves a promise with any stdout output from the spec interpreter.
291 |
292 | ```ts
293 | function specEval(buf :ArrayBuffer, options? :SpecOptions) :Promise
294 |
295 | interface SpecOptions {
296 | eval? :string // S-expression to evaluate after loading the module
297 | timeout? :number // 0 = no timeout. Defaults to 30000ms.
298 | logErrors? :boolean // when true, logs errors to stderr
299 | trace? :boolean // trace execution, printing to stdout
300 | }
301 | ```
302 |
303 | Have a look at [test/build_test.js](test/build_test.js) for an example where `specEval` is used to test the functionality of a module built with `ast`.
304 |
305 |
306 | ## lbtext
307 |
308 | `lbtext` can be used to generate [Linear Bytecode text](https://github.com/WebAssembly/design/blob/master/TextFormat.md) from AST code. E.g.
309 |
310 | ```wasm
311 | get_local 0
312 | i64.const 2
313 | i64.div_s
314 | end
315 | ```
316 |
317 | The `printCode` function takes a list of operations to print.
318 |
319 | ```ts
320 | function printCode(instructions :N[], writer :Writer)
321 | type Writer = (s :string)=>void
322 | ```
323 |
324 | ## Building and testing
325 |
326 | First-time setup:
327 |
328 | ```bash
329 | git clone https://github.com/WebAssembly/spec.git wasm-spec
330 | cd wasm-spec/interpreter
331 | # install ocaml in some way, perhaps with homebrew or aptitude, then
332 | make test && make opt
333 | cd ../..
334 | yarn || npm
335 | ```
336 |
337 | Building JavaScript from TypeScript source:
338 |
339 | ```
340 | $ node_modules/.bin/tsc // puts things in "build" directory
341 | ```
342 |
343 | Running tests:
344 |
345 | ```
346 | $ test/test.js
347 | ```
348 |
349 | Upgrading the spec interpreter:
350 |
351 | ```bash
352 | git -C wasm-spec pull origin
353 | cd wasm-spec/interpreter
354 | make clean && make test && make opt
355 | ```
356 |
--------------------------------------------------------------------------------
/etc/memory-and-addressing-notes.md:
--------------------------------------------------------------------------------
1 | irc.w3.org#webassembly Jan 11, 2017
2 |
3 | TL;DR: mem imm's offset is simply a way for the assembler/compiler to add a constant offset in addition to the dynamic address provided by the addr operand.
4 |
5 | ```
6 | 11:58 rsms: Question: memory address semantics (https://github.com/WebAssembly/design/blob/master/Semantics.md#addressing) doesn't make it clear if the `address` operand signifies memory index, memory-page index or virtual address. I'm _guessing_ it signified memory-page index, but I'm _hoping_ it's virtual address (byte offset into total default memory segment.)
7 | 13:04 jfb: rsms it's the address within the WebAssembly.Memory's buffer. Address 0 is the start of that buffer. It's *not* virtual address from the process' POV, but you can think of it as the virtual address from the WebAssembly VM's perspective (certainly, from C++ code's POV when executing in a wasm VM).
8 | 13:09 rsms: jfb: Thanks for your reply. So `(i32.const 8) (i32.const 132) (i32.store (alignment 2) (offset 0))` would cause the value "123" to be stored at bytes [8..11] in the first memory page?
9 | 13:10 rsms: And `(i32.const 72) (i32.const 132) (i32.store (alignment 2) (offset 0))` would cause the value "123" to be stored at bytes [72..75] in the second memory page, e.g. at effective address 72?
10 | 13:42 dschuff: rsms: not in the second memory page
11 | 13:42 dschuff: but at effective address 72 yes
12 | 13:43 dschuff: page size in wasm is defined as 64k but it's only meaningful for specifying the size of the linear memory
13 | 13:50 rsms: dschuff: Sorry, I meant 72000 th byte in the second example, i.e. `(i32.const 72000) (i32.const 132) (i32.store (alignment 2) (offset 0))` writes i32 "123" at bytes [72000..75000] within the second memory page, assuming the second page starts at byte 64000
14 | 13:50 rsms: 72000..72003 *
15 | 13:51 rsms: If this is correct, then when would I ever use the "offset" property of the memory immediate?
16 | 14:03 dschuff: rsms: that's correct. and yes, `(i32.const 8)(i32.const 132)(i32.store (offset 0))` is equivalent to `(i32.const 0)(i32.const 132)(i32.store (offset 8))`
17 | 14:04 dschuff: but if you imagine that instead of (i32.const 8) you have some non-constant expression, then it might still be convenient for your compiler to additionaly have the constant offset
18 | ```
19 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "devDependencies": {
3 | "source-map-support": "^0.4.8",
4 | "typescript": "^2.1.5"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/src/ast.ts:
--------------------------------------------------------------------------------
1 | import {utf8} from './utf8'
2 | import {Emittable, Emitter} from './emit'
3 |
4 | // For some reason we can't import basic-types into this module.
5 | // import {uint1,uint7,uint8,uint16,uint32,int7,int32,int64,float32,float64} from './basic-types'
6 | export type uint1 = number
7 | export type uint7 = number
8 | export type uint8 = number
9 | export type uint16 = number
10 | export type uint32 = number
11 | export type int7 = number
12 | export type int32 = number
13 | export type int64 = number
14 | export type float32 = number
15 | export type float64 = number
16 |
17 | const DEBUG = false
18 | const assert = DEBUG ? function(cond :any, msg? :any) {
19 | if (!cond) { throw new Error('assertion failure'); }
20 | } : function(){}
21 |
22 | //——————————————————————————————————————————————————————————————————————————————
23 | // Basic node types
24 |
25 | export type TypeTag = symbol
26 |
27 | export interface N extends Emittable {
28 | readonly t :TypeTag // type
29 | readonly z :uint32 // size in bytes (includes size of any children)
30 | readonly v :any // value
31 | }
32 |
33 | export interface Atom extends N {
34 | readonly v :T
35 | }
36 |
37 | export interface Cell extends N {
38 | readonly v :T[]
39 | }
40 |
41 | //—————————————————————————————————————
42 | // Formal types
43 | // We use a trick here to get the most out of TypeScripts type checker,
44 | // namely we specify interfaces that have "type tag" properties.
45 | // However, concrete types doesn't actually have these properties, so any
46 | // attempts to access these properties will always yield `undefined`.
47 |
48 | export interface Module extends Cell { readonly _Module: undefined
49 | readonly version :uint32
50 | }
51 |
52 | export type Section = CustomSection
53 | | TypeSection // Function signature declarations
54 | | ImportSection // Import declarations
55 | | FunctionSection // Function declarations
56 | | TableSection // Indirect function table and other tables
57 | | MemorySection // Memory attributes
58 | | GlobalSection // Global declarations
59 | | ExportSection // Exports
60 | | StartSection // Start function declaration
61 | | ElementSection // Elements section
62 | | CodeSection // Function bodies (code)
63 | | DataSection // Data segments
64 |
65 | export interface CustomSection extends Cell { readonly _CustomSection: undefined }
66 | export interface TypeSection extends Cell { readonly _TypeSection: undefined }
67 | export interface ImportSection extends Cell { readonly _ImportSection: undefined }
68 | export interface FunctionSection extends Cell { readonly _FunctionSection: undefined }
69 | export interface TableSection extends Cell { readonly _TableSection: undefined }
70 | export interface MemorySection extends Cell{readonly _MemorySection:undefined}
71 | export interface GlobalSection extends Cell {readonly _GlobalSection:undefined}
72 | export interface ExportSection extends Cell { readonly _ExportSection: undefined }
73 | export interface StartSection extends Cell { readonly _StartSection: undefined }
74 | export interface ElementSection extends Cell { readonly _ElementSection: undefined}
75 | export interface CodeSection extends Cell { readonly _CodeSection: undefined }
76 | export interface DataSection extends Cell { readonly _DataSection: undefined }
77 |
78 | export interface ImportEntry extends Cell { readonly _ImportEntry: undefined }
79 | export interface ExportEntry extends Cell { readonly _ExportEntry: undefined }
80 |
81 | export interface FuncType extends Cell { readonly _FuncType: undefined }
82 | export interface TableType extends Cell { readonly _TableType: undefined }
83 | export interface GlobalType extends Cell { readonly _GlobalType: undefined }
84 |
85 | export interface ResizableLimits extends Cell { readonly _ResizableLimits: undefined }
86 | export interface GlobalVariable extends Cell { readonly _GlobalVariable: undefined }
87 | export interface ElemSegment extends Cell { readonly _ElemSegment: undefined }
88 | export interface DataSegment extends Cell { readonly _DataSegment: undefined }
89 |
90 | export interface InitExpr extends Cell { readonly _InitExpr: undefined }
91 | export interface FunctionBody extends Cell { readonly _FunctionBody: undefined }
92 | export interface LocalEntry extends Cell { readonly _LocalEntry: undefined }
93 |
94 | export interface Str extends Atom> { readonly _Str: undefined
95 | readonly len :VarUint32
96 | }
97 |
98 | export interface Data extends Atom> { readonly _Data: undefined }
99 |
100 | export interface Uint8 extends Atom {}
101 | export interface Uint16 extends Atom {}
102 | export interface Uint32 extends Atom {}
103 | export interface VarUint32 extends Atom {}
104 | export interface VarUint7 extends Atom {}
105 | export interface VarUint1 extends Atom {}
106 | export interface VarInt7 extends Atom {}
107 | export interface VarInt32 extends Atom {}
108 | export interface VarInt64 extends Atom {}
109 | export interface Float32 extends Atom {}
110 | export interface Float64 extends Atom {}
111 |
112 | export interface I32 extends VarInt32 { readonly _I32: undefined }
113 | export interface I64 extends VarInt64 { readonly _I64: undefined }
114 | export interface F32 extends Float32 { readonly _F32: undefined }
115 | export interface F64 extends Float64 { readonly _F64: undefined }
116 | export interface Void extends VarInt7 { readonly _Void :undefined }
117 |
118 | export type Int = I32 | I64 // wasm32 | wasm64
119 | export type Result = I32 | I64 | F32 | F64
120 | export type AnyResult = Result | Void
121 | export type AnyOp = Op
122 |
123 | export interface ValueType extends Atom {}
124 | type AnyFunc = VarInt7
125 | type Func = VarInt7
126 | type EmptyBlock = VarInt7
127 | type ElemType = AnyFunc
128 | type ExternalKind = Uint8
129 | type BlockType = ValueType | EmptyBlock
130 |
131 | // Memory immediate.
132 | // In wasm32, address operands and offset attributes have type i32
133 | export type MemImm = [
134 | // flags - a bitfield which currently contains the alignment in the least
135 | // significant bits, encoded as log2(alignment)
136 | VarUint32,
137 |
138 | // offset - added to the address to form effective address.
139 | // Useful when the address is dynamic and the compiler wants to add some
140 | // constant amount of offset to the dynamically-produced address.
141 | // I.e. effective_address = address + offset
142 | Int
143 | ]
144 |
145 | // Instruction opcodes
146 | export type OpCode = uint8
147 |
148 | export interface Op extends N {
149 | readonly _Op :R
150 | readonly r :AnyResult
151 | readonly v :OpCode
152 | readonly pre? :N[] | N // instrs. pushing values onto the stack, used by "pre" types
153 | readonly imm? :N[] | N // immediates, used by "imm" types
154 | readonly post? :N[] // used by "post" types
155 | }
156 |
157 | // Operations on all number types
158 | export interface NumOps {
159 | const(v :number) :Op
160 | load(mi :MemImm, addr :Op) :Op
161 | store(mi :MemImm, addr :Op, v :Op) :Op
162 | addrIsAligned(mi :MemImm, addr :number) :boolean
163 | eq(a :Op, b :Op) :Op
164 | ne(a :Op, b :Op) :Op
165 | add(a :Op, b :Op) :Op
166 | sub(a :Op, b :Op) :Op
167 | mul(a :Op, b :Op) :Op
168 | }
169 |
170 | // Operations on all integer number types
171 | export interface IntOps extends NumOps {
172 | // Memory
173 | load8_s(mi :MemImm, addr :Op) :Op
174 | load8_u(mi :MemImm, addr :Op) :Op
175 | load16_s(mi :MemImm, addr :Op) :Op
176 | load16_u(mi :MemImm, addr :Op) :Op
177 | store8(mi :MemImm, addr :Op, v :Op) :Op
178 | store16(mi :MemImm, addr :Op, v :Op) :Op
179 |
180 | // Comparison
181 | eqz (a :Op) :Op
182 | lt_s(a :Op, b :Op) :Op
183 | lt_u(a :Op, b :Op) :Op
184 | gt_s(a :Op, b :Op) :Op
185 | gt_u(a :Op, b :Op) :Op
186 | le_s(a :Op, b :Op) :Op
187 | le_u(a :Op, b :Op) :Op
188 | ge_s(a :Op, b :Op) :Op
189 | ge_u(a :Op, b :Op) :Op
190 |
191 | // Numeric
192 | clz (a :Op) :Op
193 | ctz (a :Op) :Op
194 | popcnt(a :Op) :Op
195 | add (a :Op, b :Op) :Op
196 | sub (a :Op, b :Op) :Op
197 | mul (a :Op, b :Op) :Op
198 | div_s (a :Op, b :Op) :Op
199 | div_u (a :Op, b :Op) :Op
200 | rem_s (a :Op, b :Op) :Op
201 | rem_u (a :Op, b :Op) :Op
202 | and (a :Op, b :Op) :Op
203 | or (a :Op, b :Op) :Op
204 | xor (a :Op, b :Op) :Op
205 | shl (a :Op, b :Op) :Op
206 | shr_s (a :Op, b :Op) :Op
207 | shr_u (a :Op, b :Op) :Op
208 | rotl (a :Op, b :Op) :Op
209 | rotr (a :Op, b :Op) :Op
210 |
211 | // Conversion
212 | trunc_s_f32(a :Op) :Op
213 | trunc_u_f32(a :Op) :Op
214 | trunc_s_f64(a :Op) :Op
215 | trunc_u_f64(a :Op) :Op
216 | }
217 |
218 | export interface FloatOps extends NumOps {
219 | // Comparison
220 | eq(a :Op, b :Op) :Op
221 | ne(a :Op, b :Op) :Op
222 | lt(a :Op, b :Op) :Op
223 | gt(a :Op, b :Op) :Op
224 | le(a :Op, b :Op) :Op
225 | ge(a :Op, b :Op) :Op
226 |
227 | // Numeric
228 | add (a :Op, b :Op) :Op
229 | sub (a :Op, b :Op) :Op
230 | mul (a :Op, b :Op) :Op
231 | abs (a :Op) :Op
232 | neg (a :Op) :Op
233 | ceil (a :Op) :Op
234 | floor (a :Op) :Op
235 | trunc (a :Op) :Op
236 | nearest (a :Op) :Op
237 | sqrt (a :Op) :Op
238 | div (a :Op, b :Op) :Op
239 | min (a :Op, b :Op) :Op
240 | max (a :Op, b :Op) :Op
241 | copysign(a :Op, b :Op) :Op
242 |
243 | // Conversion
244 | convert_s_i32(a :Op) :Op
245 | convert_u_i32(a :Op) :Op
246 | convert_s_i64(a :Op) :Op
247 | convert_u_i64(a :Op) :Op
248 | }
249 |
250 | export interface I32ops extends I32, IntOps {
251 | constv(v :VarInt32) :Op
252 | const(v :int32) :Op
253 | wrap_i64(a :Op) :Op
254 | reinterpret_f32(a :Op) :Op
255 | }
256 |
257 | export interface I64ops extends I64, IntOps {
258 | constv(v :VarInt64) :Op
259 | const(v :int64) :Op
260 | load32_s(mi :MemImm, addr :Op) :Op
261 | load32_u(mi :MemImm, addr :Op) :Op
262 | store32(mi :MemImm, addr :Op, v :Op) :Op
263 | extend_s_i32(a :Op) :Op
264 | extend_u_i32(a :Op