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.
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.
So order of operations for each function is
You need to finalize before defining.
Oh.
Perhaps I'll make some documentation pull requests so the future generations don't have to ask so many questions in this chatroom.
builder.finalize()
will make a couple of final fixups to the generated clif ir.
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.
@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_func
for each call?
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.
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.
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.
bjorn3 said:
You can call declare_func_in_func on the
func
field of theFunctionBuilder
. This is the reference to theFunction
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.
One more thing. How do I get Value
s 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>
Ivan Chinenov said:
One more thing. How do I get
Value
s from ains().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
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
Seems like it. Feels ugly though.
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
.
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 setctx.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.
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