Skip to content

Commit

Permalink
Add tail calls
Browse files Browse the repository at this point in the history
  • Loading branch information
dy committed Dec 30, 2024
1 parent f752df1 commit edd8e12
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 17 deletions.
4 changes: 2 additions & 2 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,16 +80,16 @@ print(src, {
* [x] [sign_extension](https://github.com/WebAssembly/sign-extension-ops)
* [x] [multi-value](https://github.com/WebAssembly/spec/blob/master/proposals/multi-value/Overview.md)
* [x] [bulk memory ops](https://github.com/WebAssembly/bulk-memory-operations/blob/master/proposals/bulk-memory-operations/Overview.md)
* [ ] [memory64](https://github.com/WebAssembly/memory64)
* [x] [multiple memories](https://github.com/WebAssembly/multi-memory/blob/master/proposals/multi-memory/Overview.md)
* [x] [simd](https://github.com/WebAssembly/simd/blob/master/proposals/simd/SIMD.md)
* [x] [relaxed simd](https://github.com/WebAssembly/relaxed-simd)
* [x] [fixed-width simd](https://github.com/WebAssembly/simd/blob/master/proposals/simd/SIMD.md)
* [x] [ref types](https://github.com/WebAssembly/reference-types/blob/master/proposals/reference-types/Overview.md)
* [x] [func refs](https://github.com/WebAssembly/function-references/blob/main/proposals/function-references/Overview.md)
* [x] [tail_call](https://github.com/WebAssembly/tail-call)
* [ ] [gc](https://github.com/WebAssembly/gc)
* [ ] [exceptions](https://github.com/WebAssembly/exception-handling)
* [ ] [tail_call](https://github.com/WebAssembly/tail-call)
* [ ] [memory64](https://github.com/WebAssembly/memory64)
* [ ] [annotations](https://github.com/WebAssembly/annotations)
* [ ] [code_metadata](https://github.com/WebAssembly/tool-conventions/blob/main/CodeMetadata.md)
* [ ] [js strings](https://github.com/WebAssembly/js-string-builtins/blob/main/proposals/js-string-builtins/Overview.md)
Expand Down
20 changes: 15 additions & 5 deletions src/compile.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import parse from './parse.js'
// build instructions index
INSTR.forEach((op, i) => INSTR[op] = i >= 0x11a ? [0xfd, i - 0x11a] : i >= 0xfc ? [0xfc, i - 0xfc] : [i])

// console.log(INSTR)
/**
* Converts a WebAssembly Text Format (WAT) tree to a WebAssembly binary format (WASM).
*
Expand Down Expand Up @@ -164,7 +165,8 @@ const plain = (nodes, ctx) => {
}

// call_indirect $typeidx
else if (node === 'call_indirect') {
// return_call_indirect $typeidx
else if (node.endsWith('call_indirect')) {
out.push(nodes[0]?.[0] === '$' || !isNaN(nodes[0]) ? nodes.shift() : 0)
let [idx, param, result] = typeuse(nodes, ctx, 0)
out.push(['type', idx ?? (ctx._[idx = '$' + param + '>' + result] = [param, result], idx)])
Expand Down Expand Up @@ -595,25 +597,33 @@ const instr = (nodes, ctx) => {
immed.push(...uleb(id(nodes.shift(), ctx.global)))
}

// call id ...nodes
else if (code == 0x10) {
// call $func ...nodes
// return_call $func
else if (code == 0x10 || code == 0x12) {
immed.push(...uleb(id(nodes.shift(), ctx.func)))
}

// call_indirect tableIdx (type $typeName) ...nodes
else if (code == 0x11) {
// return_call_indirect $table (type $typeName) ... nodes
else if (code == 0x11 || code == 0x13) {
immed.push(
...uleb(id(nodes[1][1], ctx.type)),
...uleb(id(nodes.shift(), ctx.table))
), nodes.shift()
}

// call_ref $type
else if (code == 0x14) {
immed.push(...uleb(id(nodes.shift(), ctx.type)))
}

// end
else if (code == 0x0b) ctx.block.pop()

// br $label result?
// br_if $label cond result?
else if (code == 0x0c || code == 0x0d) {
// br_on_null $l, br_on_non_null $l
else if (code == 0x0c || code == 0x0d || code == 0xd5 || code == 0xd6) {
// br index indicates how many block items to pop
let l = nodes.shift(), i = l?.[0] === '$' ? ctx.block.length - ctx.block[l] : +l
i <= ctx.block.length || err(`Bad label ${l}`)
Expand Down
4 changes: 2 additions & 2 deletions src/const.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

34 changes: 26 additions & 8 deletions test/testsuite.js
Original file line number Diff line number Diff line change
Expand Up @@ -174,13 +174,31 @@ t('/test/official/proposals/extended-const/data.wast', async function () { await
t('/test/official/proposals/extended-const/elem.wast', async function () { await file(this.name, { spectest }) })
t('/test/official/proposals/extended-const/global.wast', async function () { await file(this.name, { spectest }) })

t.todo('/test/official/proposals/function-references/exports.wast', async function () { await file(this.name, { spectest }) })
t.todo('/test/official/proposals/function-references/imports.wast', async function () { await file(this.name, { spectest }) })
t('/test/official/proposals/function-references/binary.wast', async function () { await file(this.name, { spectest }) })
t.todo('/test/official/proposals/function-references/br_on_non_null.wast', async function () { await file(this.name, { spectest }) })
t.todo('/test/official/proposals/function-references/br_table.wast', async function () { await file(this.name, { spectest }) })
t.todo('/test/official/proposals/function-references/call_ref.wast', async function () { await file(this.name, { spectest }) })
t.todo('/test/official/proposals/function-references/data.wast', async function () { await file(this.name, { spectest }) })
t.todo('/test/official/proposals/function-references/elem.wast', async function () { await file(this.name, { spectest }) })
t.todo('/test/official/proposals/function-references/func.wast', async function () { await file(this.name, { spectest }) })
t.todo('/test/official/proposals/function-references/global.wast', async function () { await file(this.name, { spectest }) })
t.todo('/test/official/proposals/function-references/if.wast', async function () { await file(this.name, { spectest }) })
t.todo('/test/official/proposals/function-references/linking.wast', async function () { await file(this.name, { spectest }) })
t.todo('/test/official/proposals/function-references/local_get.wast', async function () { await file(this.name, { spectest }) })
t.todo('/test/official/proposals/function-references/local_init.wast', async function () { await file(this.name, { spectest }) })
t.todo('/test/official/proposals/function-references/ref_as_non_null.wast', async function () { await file(this.name, { spectest }) })
t.todo('/test/official/proposals/function-references/ref_is_null.wast', async function () { await file(this.name, { spectest }) })
t.todo('/test/official/proposals/function-references/ref_null.wast', async function () { await file(this.name, { spectest }) })
t.todo('/test/official/proposals/function-references/tag.wast', async function () { await file(this.name, { spectest }) })
t.todo('/test/official/proposals/function-references/throw_ref.wast', async function () { await file(this.name, { spectest }) })
t.todo('/test/official/proposals/function-references/throw.wast', async function () { await file(this.name, { spectest }) })
t.todo('/test/official/proposals/function-references/try_table.wast', async function () { await file(this.name, { spectest }) })
t.todo('/test/official/proposals/function-references/ref.wast', async function () { await file(this.name, { spectest }) })
t.todo('/test/official/proposals/function-references/return_call_indirect.wast', async function () { await file(this.name, { spectest }) })
t.todo('/test/official/proposals/function-references/return_call_ref.wast', async function () { await file(this.name, { spectest }) })
t.todo('/test/official/proposals/function-references/return_call.wast', async function () { await file(this.name, { spectest }) })
t.todo('/test/official/proposals/function-references/select.wast', async function () { await file(this.name, { spectest }) })
t.todo('/test/official/proposals/function-references/table-sub.wast', async function () { await file(this.name, { spectest }) })
t.todo('/test/official/proposals/function-references/table.wast', async function () { await file(this.name, { spectest }) })
t.todo('/test/official/proposals/function-references/type-equivalence.wast', async function () { await file(this.name, { spectest }) })
t.todo('/test/official/proposals/function-references/unreached-invalid.wast', async function () { await file(this.name, { spectest }) })
t.todo('/test/official/proposals/function-references/unreached-valid.wast', async function () { await file(this.name, { spectest }) })

t.todo('/test/official/proposals/gc/try_table.wast', async function () { await file(this.name, { spectest }) })

Expand All @@ -194,7 +212,7 @@ t('/test/official/proposals/relaxed-simd/relaxed_laneselect.wast', async functio
t('/test/official/proposals/relaxed-simd/relaxed_madd_nmadd.wast', async function () { await file(this.name, { spectest }) })
t('/test/official/proposals/relaxed-simd/relaxed_min_max.wast', async function () { await file(this.name, { spectest }) })

t.todo('/test/official/proposals/tail-call/return_call_indirect.wast', async function () { await file(this.name, { spectest }) })
t.todo('/test/official/proposals/tail-call/return_call.wast', async function () { await file(this.name, { spectest }) })
t('/test/official/proposals/tail-call/return_call_indirect.wast', async function () { await file(this.name, { spectest }) })
t('/test/official/proposals/tail-call/return_call.wast', async function () { await file(this.name, { spectest }) })

t.todo('/test/official/proposals/wide-arithmetic/wide-arithmetic.wast', async function () { await file(this.name, { spectest }) })

0 comments on commit edd8e12

Please sign in to comment.