Stream: cranelift

Topic: How to clear `FunctionBuilderContext`?


view this post on Zulip Ivan Chinenov (Mar 28 2024 at 09:16):

Comment on said type states:

In order to reduce memory reallocations when compiling multiple functions, FunctionBuilderContext holds various data structures which are cleared between functions

However, when trying to compile multiple functions using same context, I get a failed assert in FunctionBuilder::new, and searching the docs for appearances of FunctionBuilderContext in function yields nothing related to clearing it explicitly.

view this post on Zulip bjorn3 (Mar 28 2024 at 09:19):

You need to call finalize on the FunctionBuilder after you are done generating the clif ir and before you use the clif ir by eg passing it to define_function. Calling finalize will clear the FunctionBuilderContext.

view this post on Zulip Ivan Chinenov (Mar 28 2024 at 09:20):

So order of operations for each function is

view this post on Zulip bjorn3 (Mar 28 2024 at 09:21):

You need to finalize before defining.

view this post on Zulip Ivan Chinenov (Mar 28 2024 at 09:22):

Oh.
Perhaps I'll make some documentation pull requests so the future generations don't have to ask so many questions in this chatroom.

view this post on Zulip bjorn3 (Mar 28 2024 at 09:22):

builder.finalize() will make a couple of final fixups to the generated clif ir.

view this post on Zulip bjorn3 (Mar 28 2024 at 09:25):

By the way, something that may be useful to know in the future: You are allowed to declare functions multiple times for as long as you pass identical function signatures. So you can for example unconditionally declare every function you call as Linkage::Import, but only declare it as Linkage::Local/Linkage::Export when you define them. This way you don't have to distinguish between imported and locally defined functions when generating a call.

view this post on Zulip Ivan Chinenov (Mar 28 2024 at 09:25):

@bjorn3 while you're here, if i'm compiling an executable with multiple functions, am I supposed to pre-declare all functions, or use declare_func_in_funcfor each call?

view this post on Zulip bjorn3 (Mar 28 2024 at 09:31):

declare_func_in_func returns an id that is only valid within that specific function. As such you have to call it again on each function where you want to call the target function.

view this post on Zulip Ivan Chinenov (Mar 28 2024 at 09:46):

A bit inconvenient that I can't declare_func_in_func while a FunctionBuilder exist, as both require &mut to func. I guess I'll have to build some sort of dependency graph to handle pre-declaring functions before emitting clif.

view this post on Zulip bjorn3 (Mar 28 2024 at 09:56):

You can call declare_func_in_func on the func field of the FunctionBuilder. This is the reference to the Function that you are currently building.

view this post on Zulip Ivan Chinenov (Mar 28 2024 at 10:01):

bjorn3 said:

You can call declare_func_in_func on the func field of the FunctionBuilder. This is the reference to the Function that you are currently building.

I don't think there's such a function there. There's a declare_imported_user_function but that's all.

view this post on Zulip Ivan Chinenov (Mar 28 2024 at 10:09):

One more thing. How do I get Values from a ins().call()? I vaguely remember there was something specifically for this, but skimming the docs I can't find anything that has signature of Inst -> Value/Vec<Value>

view this post on Zulip Ivan Chinenov (Mar 28 2024 at 10:10):

Ivan Chinenov said:

One more thing. How do I get Values from a ins().call()? I vaguely remember there was something specifically for this, but skimming the docs I can't find anything that has signature of Inst -> Value/Vec<Value>

It's https://docs.wasmtime.dev/api/cranelift/prelude/struct.FunctionBuilder.html#method.inst_results

view this post on Zulip Ivan Chinenov (Mar 28 2024 at 10:22):

    builder.append_block_params_for_function_params(entry_b);

    let mut stack: Vec<Value> = Vec::new();
    stack.extend_from_slice(builder.block_params(entry_b));

Any ideas why this wouldn't append anything to the stack given I've pre-declared all functions?
My guess would be, I have to save signatures and manually set ctx.func.signature to a correct one for the function being built

view this post on Zulip Ivan Chinenov (Mar 28 2024 at 10:27):

Seems like it. Feels ugly though.

view this post on Zulip bjorn3 (Mar 28 2024 at 10:36):

I don't think there's such a function there.

I mean call module.declare_func_in_func except with builder.func as &mut Function argument instead of func.

view this post on Zulip bjorn3 (Mar 28 2024 at 10:38):

Ivan Chinenov said:

    builder.append_block_params_for_function_params(entry_b);

    let mut stack: Vec<Value> = Vec::new();
    stack.extend_from_slice(builder.block_params(entry_b));

Any ideas why this wouldn't append anything to the stack given I've pre-declared all functions?
My guess would be, I have to save signatures and manually set ctx.func.signature to a correct one for the function being built

append_block_params_for_function_params is for the function params of the current function, not of the called function. For your use case calling builder.append_block_param() for each function parameter yourself is likely nicer.

view this post on Zulip Ivan Chinenov (Mar 28 2024 at 10:49):

I ended up with structure like this for now

let mut path_to_fid = FnvHashMap::default();
    for (path, proc) in &procs {
        let mut sig = module.make_signature();

       // push paramenter types

        let fid = module.declare_function(&path.to_smolstr(), Linkage::Export, &sig)?;

        path_to_fid.insert(path.clone(), (fid, sig));
    }

    for (path, proc) in procs {
        compile_proc(
            path,
            proc,
            &path_to_fid,
            &mut module,
            &mut ctx,
            &mut builder_ctx,
        )?;
    }

...
fn compile_proc(
    path: ItemPathBuf,
    proc: CProc,
    path_to_fid: &FnvHashMap<ItemPathBuf, (FuncId, Signature)>,
    module: &mut ObjectModule,
    ctx: &mut codegen::Context,
    builder_ctx: &mut FunctionBuilderContext,
) -> ModuleResult<()> {
    module.clear_context(ctx);

    let (fid, sig) = path_to_fid[&path].clone();
    ctx.func.signature = sig;
    // build function

Last updated: Dec 23 2024 at 12:05 UTC