Stream: cranelift

Topic: Guidelines on marking stack map entries


view this post on Zulip Max T. Kristiansen (Sep 13 2025 at 19:57):

Hi again,

It's late and I've been fumbling with this for a couple of hours, so the following ramble may not make much sense.

I've been trying to put together a GC using the user stack maps in Cranelift, but I'm having throuble with the stack maps not containing the pointers I expected. For example, given a snippet like:

fn main() -> i32 {
    let i = 0;
    let x = Foo { a: 1 };

    function_call_to_trigger_gc();

    while i < 3 {
        let y = Foo { a: 5 };

        function_call_to_trigger_gc();

        i = i + 1;
    }

    // explicit use of `x`
    x.a
}

After each allocation (so, each call to Foo::new), the return value is declared as needing a stack map. When calling the first function_call_to_trigger_gc , the stack map contains the offset to x, which is great. Inside the while loop, function_call_to_trigger_gc is called again, but now the stack map only has a single entry, pointing to y. I would've expected x to also be included, but maybe I'm not declaring the correct values as needing a stack map.

My bottom line is; is there some guidelines for what values / variables need to be declared as needing stack maps? Would they ever need to be declared multiple times? Am I meant to see both x and y in the loops stack map, or am I missing something?

view this post on Zulip fitzgen (he/him) (Sep 15 2025 at 17:25):

Max T. Kristiansen said:

is there some guidelines for what values / variables need to be declared as needing stack maps?

if you want a value to appear in a stack map (i.e. it is a GC reference that is potentially live across some call that might trigger GC) then it needs to be declared as needing inclusion in stack maps.

if you are not using Variables, then the needs-stack-map bit will not be propagated from block argument values to block parameter values, and you are responsible for marking those block parameters as needing stack maps yourself.

If you are using Variables then declaring the variable as needing stack maps should automatically propagate that to all of that variable's def and use values. That includes any block arguments/parameters that the variable infrastructure inserts. (but it will not be propagated across block args/params that you insert yourself and manually thread values derived from the variable into, you are responsible for marking such block args/params as needing stack maps).

Max T. Kristiansen said:

Would they ever need to be declared multiple times?

If you look at the implementation of declare_{var,value}_needs_stack_map it is just inserting into a set, so it is an idempotent operation and calling the methods multiple times does nothing that the first call didn't already do:

https://github.com/bytecodealliance/wasmtime/blob/625a3e3fd9958f4bfb2445b4aea90588d08719b8/cranelift/frontend/src/frontend.rs#L557

Max T. Kristiansen said:

Am I meant to see both x and y in the loops stack map, or am I missing something?

Are you sure you aren't seeing only an entry for x and not for y? it doesn't look like y is live across any function call.


backing up a bit, it would be good to look at the logs you get for this test case with (assuming env_logger) RUST_LOG=cranelift_frontend::frontend::safepoints=trace

that will show the CLIF before and after spilling and reloading needs-stack-map values across safepoints, as well as the results of the liveness analysis and all that. in general, it is a good first place to look when confused about why a particular value is or is not present in a stack map

A lightweight WebAssembly runtime that is fast, secure, and standards-compliant - bytecodealliance/wasmtime

view this post on Zulip Max T. Kristiansen (Sep 16 2025 at 08:09):

Okay, I think I'm beginning to understand my issue now. I think the sentence that made it click was:

But [variables] will not be propagated across block args/params that you insert yourself.

While it makes total sense in hindsight, it wasn't intuitive to me that block parameters would need to be declared as well. I had just assumed that the liveness analysis would see a given Variable on the stack map as needing a stack map, even across block boundaries. When looking over the generated IR again, all the declarations were made using declare_value_needs_stack_map, ie without using Variables.

I'm unsure when, or even if, I'd ever get around, but would it be fine for me to add an example project to the cranelift package, outlining how stack maps might be used? Looking through the topics in the Zulip chat, it's something multiple people seem to struggle with.

view this post on Zulip fitzgen (he/him) (Sep 16 2025 at 16:48):

Max T. Kristiansen said:

I'm unsure when, or even if, I'd ever get around, but would it be fine for me to add an example project to the cranelift package, outlining how stack maps might be used? Looking through the topics in the Zulip chat, it's something multiple people seem to struggle with.

example projects would be great to have; none of the core maintainers have time for writing them at the moment. if you find time to contribute something, that would be neat. also, feel free to send doc comment update PRs; the docs can always improve and it is good to have the perspective of people who didn't write the implementation and are unknowingly assuming implementation knowledge while writing docs

in the meantime, when in doubt, the best bet is to look at what Wasmtime does

view this post on Zulip Max T. Kristiansen (Sep 17 2025 at 14:16):

the docs can always improve and it is good to have the perspective of people who didn't write the implementation and are unknowingly assuming implementation knowledge while writing docs

Is there a specific place these docs should go? Module documentation, Markdown files under cranelift/docs, etc?

view this post on Zulip fitzgen (he/him) (Sep 17 2025 at 15:47):

@Max T. Kristiansen I was imagining the /// ... doc comments above the declare_*_needs_stack_map methods that rustdoc turns into API docs, but if you have other ideas I'm all ears

cranelift/docs would be good for that kind of longer-form reference/tutorial/example project we talked about earlier

view this post on Zulip Max T. Kristiansen (Sep 18 2025 at 20:05):

I got around to making an example, along with a Markdown file for a higher-level view of the way it would work.

https://github.com/bytecodealliance/wasmtime/pull/11710

Adds a documentation entry for how stack maps might be used to implement a garbage collector. Adds an example project which shows off how a simple garbage collector might actually be implemented. C...

Last updated: Dec 06 2025 at 07:03 UTC