Stream: git-wasmtime

Topic: wasmtime / PR #3275 Use relative `call` instructions betw...


view this post on Zulip Wasmtime GitHub notifications bot (Sep 01 2021 at 16:06):

alexcrichton opened PR #3275 from call-relative to main:

This commit is a relatively major change to the way that Wasmtime
generates code for Wasm modules and how functions call each other.
Prior to this commit all function calls between functions, even if they
were defined in the same module, were done indirectly through a
register. To implement this the backend would emit an absolute 8-byte
relocation near all function calls, load that address into a register,
and then call it. While this technique is simple to implement and easy
to get right, it has two primary downsides associated with it:

To fix these issues, while also somewhat compromising on the previously
simple implementation technique, this commit switches wasm calls within
a module to using the colocated flag enabled in Cranelift-speak, which
basically means that a relative call instruction is used with a
relocation that's resolved relative to the pc of the call instruction
itself.

When switching the colocated flag to true this commit is also then
able to move much of the relocation resolution from wasmtime_jit::link
into wasmtime_cranelift::obj during object-construction time. This
frontloads all relocation work which means that there's actually no
relocations related to function calls in the final image, solving both
of our points above.

The main gotcha in implementing this technique is that there are
hardware limitations to relative function calls which mean we can't
simply blindly use them. AArch64, for example, can only go +/- 64 MB
from the bl instruction to the target, which means that if the
function we're calling is a greater distance away then we would fail to
resolve that relocation. On x86_64 the limits are +/- 2GB which are much
larger, but theoretically still feasible to hit. Consequently the main
increase in implementation complexity is fixing this issue.

This issue is actually already present in Cranelift itself, and is
internally one of the invariants handled by the MachBuffer type. When
generating a function relative jumps between basic blocks have similar
restrictions. This commit adds new methods for the MachBackend trait
and updates the implementation of MachBuffer to account for all these
new branches. Specifically the changes to MachBuffer are:

I've added some simple testing of this for now. A synthetic compiler
option was create to simply add padded 0s between functions and test
cases implement various forms of calls that at least need veneers. A
test is also included for x86_64, but it is unfortunately pretty slow
because it requires generating 2GB of output. I'm hoping for now it's
not too bad, but we can disable the test if it's prohibitive and
otherwise just comment the necessary portions to be sure to run the
ignored test if these parts of the code have changed.

The final end-result of this commit is that for a large module I'm
working with the number of relocations dropped to zero, meaning that
nothing actually needs to be done to the text section when it's loaded
into memory (yay!). I haven't run final benchmarks yet but this is the
last remaining source of significant slowdown when loading modules,
after I land a number of other PRs both active and ones that I only have
locally for now.

<!--

Please ensure that the following steps are all taken care of before submitting
the PR.

Please ensure all communication adheres to the code of conduct.
-->

view this post on Zulip Wasmtime GitHub notifications bot (Sep 01 2021 at 16:06):

alexcrichton edited PR #3275 from call-relative to main:

This commit is a relatively major change to the way that Wasmtime
generates code for Wasm modules and how functions call each other.
Prior to this commit all function calls between functions, even if they
were defined in the same module, were done indirectly through a
register. To implement this the backend would emit an absolute 8-byte
relocation near all function calls, load that address into a register,
and then call it. While this technique is simple to implement and easy
to get right, it has two primary downsides associated with it:

To fix these issues, while also somewhat compromising on the previously
simple implementation technique, this commit switches wasm calls within
a module to using the colocated flag enabled in Cranelift-speak, which
basically means that a relative call instruction is used with a
relocation that's resolved relative to the pc of the call instruction
itself.

When switching the colocated flag to true this commit is also then
able to move much of the relocation resolution from wasmtime_jit::link
into wasmtime_cranelift::obj during object-construction time. This
frontloads all relocation work which means that there's actually no
relocations related to function calls in the final image, solving both
of our points above.

The main gotcha in implementing this technique is that there are
hardware limitations to relative function calls which mean we can't
simply blindly use them. AArch64, for example, can only go +/- 64 MB
from the bl instruction to the target, which means that if the
function we're calling is a greater distance away then we would fail to
resolve that relocation. On x86_64 the limits are +/- 2GB which are much
larger, but theoretically still feasible to hit. Consequently the main
increase in implementation complexity is fixing this issue.

This issue is actually already present in Cranelift itself, and is
internally one of the invariants handled by the MachBuffer type. When
generating a function relative jumps between basic blocks have similar
restrictions. This commit adds new methods for the MachBackend trait
and updates the implementation of MachBuffer to account for all these
new branches. Specifically the changes to MachBuffer are:

I've added some simple testing of this for now. A synthetic compiler
option was create to simply add padded 0s between functions and test
cases implement various forms of calls that at least need veneers. A
test is also included for x86_64, but it is unfortunately pretty slow
because it requires generating 2GB of output. I'm hoping for now it's
not too bad, but we can disable the test if it's prohibitive and
otherwise just comment the necessary portions to be sure to run the
ignored test if these parts of the code have changed.

The final end-result of this commit is that for a large module I'm
working with the number of relocations dropped to zero, meaning that
nothing actually needs to be done to the text section when it's loaded
into memory (yay!). I haven't run final benchmarks yet but this is the
last remaining source of significant slowdown when loading modules,
after I land a number of other PRs both active and ones that I only have
locally for now.

cc https://github.com/bytecodealliance/wasmtime/issues/3230

<!--

Please ensure that the following steps are all taken care of before submitting
the PR.

Please ensure all communication adheres to the code of conduct.
-->

view this post on Zulip Wasmtime GitHub notifications bot (Sep 01 2021 at 16:06):

alexcrichton requested cfallin for a review on PR #3275.

view this post on Zulip Wasmtime GitHub notifications bot (Sep 01 2021 at 16:43):

bjorn3 submitted PR review.

view this post on Zulip Wasmtime GitHub notifications bot (Sep 01 2021 at 16:43):

bjorn3 created PR review comment:

Crazy idea, but would it be possible for a single MachBuffer to somehow be used across all functions in a module? This would reduce the ability to compile functions in parallel though.

view this post on Zulip Wasmtime GitHub notifications bot (Sep 01 2021 at 16:59):

pchickey submitted PR review.

view this post on Zulip Wasmtime GitHub notifications bot (Sep 01 2021 at 16:59):

pchickey created PR review comment:

Compiling functions in parallel is an important optimization we rely on in production

view this post on Zulip Wasmtime GitHub notifications bot (Sep 01 2021 at 17:21):

alexcrichton updated PR #3275 from call-relative to main.

view this post on Zulip Wasmtime GitHub notifications bot (Sep 01 2021 at 18:15):

cfallin submitted PR review.

view this post on Zulip Wasmtime GitHub notifications bot (Sep 01 2021 at 18:15):

cfallin submitted PR review.

view this post on Zulip Wasmtime GitHub notifications bot (Sep 01 2021 at 18:15):

cfallin created PR review comment:

extreme grammar-nit mode (sorry!) but end the sentence with a period here (thanks!).

view this post on Zulip Wasmtime GitHub notifications bot (Sep 01 2021 at 18:15):

cfallin created PR review comment:

s/can be reach/can reach/

view this post on Zulip Wasmtime GitHub notifications bot (Sep 01 2021 at 18:15):

cfallin created PR review comment:

s/it's in in-bounds/it's an in-bounds/

view this post on Zulip Wasmtime GitHub notifications bot (Sep 01 2021 at 18:15):

cfallin created PR review comment:

Once this PR merges, would you mind creating an issue to track this?

view this post on Zulip Wasmtime GitHub notifications bot (Sep 01 2021 at 18:15):

cfallin created PR review comment:

Yeah, fundamentally it makes sense to have a serialized process only at the very end, which is what this separate "text builder" use of MachBuffer is. Conceptually it's like a linker; I think the stage separation is reasonable.

view this post on Zulip Wasmtime GitHub notifications bot (Sep 01 2021 at 18:15):

cfallin created PR review comment:

Would it make sense to have some notion of page alignment per platform, or maybe just take the least-common-multiple of those for platforms we support? I'm thinking specifically of macOS/aarch64, where mmap'ing part of a file with only 4K alignment might be problematic with the 16K (?) pages by default.

view this post on Zulip Wasmtime GitHub notifications bot (Sep 01 2021 at 18:24):

alexcrichton submitted PR review.

view this post on Zulip Wasmtime GitHub notifications bot (Sep 01 2021 at 18:24):

alexcrichton created PR review comment:

#3277, and indeed!

view this post on Zulip Wasmtime GitHub notifications bot (Sep 01 2021 at 18:25):

alexcrichton submitted PR review.

view this post on Zulip Wasmtime GitHub notifications bot (Sep 01 2021 at 18:25):

alexcrichton created PR review comment:

Indeed! This PR is just moving this logic around as opposed to adding it (it's actually been around for quite some time, although it's more important to get right now that we're directly mmap'ing cache artifacts), so I'll leave this as-is for now, but I'll open an issue. The issue with M1 macs was actually just fixed in https://github.com/bytecodealliance/wasmtime/pull/3274 as well, so it definitely needs fixing soon!

view this post on Zulip Wasmtime GitHub notifications bot (Sep 01 2021 at 18:26):

alexcrichton updated PR #3275 from call-relative to main.

view this post on Zulip Wasmtime GitHub notifications bot (Sep 01 2021 at 18:27):

alexcrichton merged PR #3275.


Last updated: Dec 23 2024 at 12:05 UTC