Hi, I am trying to understand why and where the compiler creates the specific patterns ahead of a call indirect, determining its index
I analyzed the WasmBench dataset and looked for pattern ahead of a call indirect and it turns out, that there are basically two patterns determining over 87% the index.
The first one is the Add Case
and the second one the Load Offset Case
:
ADD CASE
i32.add
i32.const
i32.and
i32.const
load offset
call_indirect (type x)
LOAD OFFSET CASE I
local.get
load offset
local.get
load offset
call_indirect (type x)
LOAD OFFSET CASE II
local.get
local.get
load offset
load offset
call_indirect (type x)
I mainly want to focus on C++ and Rust, as there these patterns occurred the most in the dataset, with the Add Case being dominant withing C++ and the Load Offset Case being dominant within Rust (but both cases occur in both languages).
I am trying to recreate those patterns by manually writing minimum viable programs. For example if you look at this small C Code. I do so, as i already know that a lot of call_indirects
come from libraries, for example in Rust a program with a std lib creates over 76 call_indirects
, whereas a #![no_std] program, doesn't.
int add(int a, int b) { return a+b; }
static int sub(int a, int b) { return a-b; }
int main(int argc) {
int (*functionPtr)(int, int);
if (argc)
functionPtr = add;
else
functionPtr = sub;
int a = functionPtr(2, 3);
return a;
}
This creates a small wat File, that is still readable and has a call_indirect
instruction in it, as i want. But it does not yet have one of the patterns above
It would be nice to know and thus i am asking if anyone knows the section, where the compiler creates these snippets?
Or could help me manually creating these patterns.
Thank you in advance, Jannik
Last updated: Dec 23 2024 at 12:05 UTC