Skip to content

BasicCABI.md: mention how function pointers look like#200

Open
yamt wants to merge 2 commits intoWebAssembly:mainfrom
yamt:function-pointer
Open

BasicCABI.md: mention how function pointers look like#200
yamt wants to merge 2 commits intoWebAssembly:mainfrom
yamt:function-pointer

Conversation

@yamt
Copy link
Contributor

@yamt yamt commented Feb 20, 2023

No description provided.

BasicCABI.md Outdated

**Function pointers**

A pointer to a function is an index into the table 0 of the module.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
A pointer to a function is an index into the table 0 of the module.
A pointer to a function is an index into table 0 of the module.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think __indirect_function_table might not be table 0. For example, if the module happens to import a table, since import always come first in the index space there is no way for the linker to arrange for __indirect_function_table to be at index 0.

In other words, any documentation like this should probably refer to the table by name on only.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

on runtime (eg. when calling a host function which takes a function pointer), the name is not available, is it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i was confused with linker symbol and export name.
i dropped table 0 assumption.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There can be multiple tables, but will clang/LLVM ever emit more than one? To my eye, it looks like there's an undocumented assumption that we'll only ever handle a single table. For example, Linking.md discusses indices in tables but not indices of tables. For tools wishing to be compatible with clang/LLVM it seems to me that they must reserve table 0 for function pointers

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some links from trawling through LLVM itself:

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The things is that the Basic C API, up until this point has not needed to refer to symbols, or the linking metatdata, or the object file format. That has been specified in Linking.md.

Note that there is also no mention of the other magic symbols __stack_pointer here, although perhaps there should be?

I think perhaps its enough to to say something like:

Function pointers are represented as i32 (or i64 under wasm64) values which are indexes into a single Wasm table named `__indirect_function_table` (See Linking.md for more of how symbols work).    The are invoked using the `call_indirect` instruction.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about:

Function pointers are represented as i32 (or i64 under wasm64) values which are indexes into a single Wasm table,
which must be the table 0. If it is exported or imported then its name is __indirect_function_table
(See Linking.md for more of how symbols work). The are invoked using the call_indirect instruction.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But its not true anymore that it must be table zero. Its just the table with the name __indirect_function_table which gets identified via a relocation. Same as how stack pointer is identified via its symbols name __stack_pointer.

I don't think we need to mention the importing or exporting because in the final binary you can export as whatever name you like. Its only things like emscripten and WASI that care about the exported name, not the C API or the Linking specification.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For example I could create a runtime where the table has to be exported as __table, and that would not effect the C ABI using withing the module.


A pointer to a function is an index into the table 0 of the module.
The type of the table is funcref.
The table needs to be exported with the name `__indirect_function_table`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if this should be a requirement of the base C ABI. IIRC it is what emscripten does, but emscripten is also focused on the JS embedding (and has other conventions such as other exports that are also beyond the scope of this doc). There are also other ways to accomplish the same objective (for example, a module could export a function that takes the function pointer as an argument, and then calls it itself).
Probably it makes sense for conventions used by particular embeddings or particular SDKs/runtimes to be either left out of this doc or put in a separate section. Is this requirement particularly important to the environment that you are using?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are also other ways to accomplish the same objective (for example, a module could export a function that takes the function pointer as an argument, and then calls it itself).

yes. but sometimes it isn't desirable to tweak the api that way. eg. when it's from a standard. or when it's a port from non-wasm platform.

Probably it makes sense for conventions used by particular embeddings or particular SDKs/runtimes to be either left out of this doc or put in a separate section. Is this requirement particularly important to the environment that you are using?

i'm not familiar with emscripten.

my concern is more about wasi: https://github.com/WebAssembly/WASI/blob/main/legacy/application-abi.md#current-unstable-abi

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dschuff i'm not sure what exactly your concern is. after all, as the first section of this document is saying, this is just about "the ABI that the clang/LLVM WebAssembly backend is currently using".

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Everything in this document is about inter-linking between object files (and the interface between object files and the linker), and is therefore independent of the embedding. Its says nothing about what must be exported from the linked binary, or about what the linked binary may import from the environment (i.e. what APIs would be available to the program, or what API the program must implement). Those would depend on the embedding, whether WASI or emscripten is being used, and indeed even which WASI APIs were available vs. not.
I think those concerns should at the very least be in a separate section, and maybe a separate document entirely.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The import name env.__indirect_function_table is documented in DynamicLinking.md. It's not required that the table be exported from WASM at all; each module might import a table initialized by the host, for example.

@yamt yamt force-pushed the function-pointer branch from daf2f35 to bece839 Compare February 2, 2026 04:57
@yamt
Copy link
Contributor Author

yamt commented Feb 2, 2026

the last push was just a rebase

@dschuff
Copy link
Member

dschuff commented Mar 6, 2026

Collecting the discussion from the 2 different comment locations to here:
I think we are hitting a little bit of a seam between the things that concern a C ABI and the things that concern the linking ABI. A traditional C ABI describes compiling C in terms of the machine state (e.g. memory layout, the registers, including the stack pointer, FPU state, flags, etc.). These things apply regardless of whether code is part of an object file or linked. And a function can be called with a particular convention the same way regardless of whether the caller is local or cross-module. System ABIs also specify things about object files and linking, but those are completely separable (e.g. different chapters of the System V ABI).

But the indirect function table and stack pointer are a little bit in between. Modules have to agree on which global is the stack pointer. In hardware, the modules have to agree on which register to use but the register is always there. In Wasm we could have said "global 0 is always the stack pointer" but we made it a little more flexible than that and IIRC we have a linking scheme where stack pointer references are relocatable. And in fact it is not covered either here (there are TODOs) or in Linking.md.

Likewise an indirect function table required: for function pointers to even exist and be callable (even where no symbol resolution or linking are required) modules have to agree on which table to use. We could have specified which table it was (indeed in MVP there was only one) but we decided not to.
Both of these decisions allow flexibility (e.g. easier merging with modules using a different convention?) but it does mean we are mixing these concerns a bit.

Maybe in this document we could say something like what Sam is suggesting above,
Broadly, the stack pointer is a global used by all functions that need it. If this is a relocatable/linkable object, the symbol name is specified here, but the semantics of symbols and naming are not. Likewise modules use a single function table, with a name. Neither is required to be a particular numeric value, and if the file is not linkable, then it is also not required to be named.

Particular environments may want to impose additional constraints. Maybe Emscripten wants the memory exported, or some environment wants the stack pointer exported. But I think that goes beyond this convention.

WDYT?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants