-
Notifications
You must be signed in to change notification settings - Fork 699
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
wasm2c: implement the tail-call proposal #2272
Conversation
24c854b
to
88d5298
Compare
49bfc70
to
005af87
Compare
005af87
to
70b888f
Compare
70b888f
to
99caef2
Compare
99caef2
to
91548be
Compare
d446ede
to
e677efa
Compare
2b3e918
to
65f434e
Compare
0fab24a
to
94bcf25
Compare
5084240
to
7ca62d2
Compare
Interesting! Thanks for sharing this. @keithw would probably know more, but I'll also try to have a look to see if we can use to ensure that the C code being generated would be tail-called when compiled with gcc/clang |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm % comments
src/template/wasm2c.declarations.c
Outdated
#if (__STDC_VERSION__ >= 201112L) || defined(_Static_assert) | ||
#define wasm_static_assert(X) _Static_assert(X, "assertion failure") | ||
#else | ||
#define wasm_static_assert(X) \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the wasm_
prefix the correct one to use here? Why not wasm_rt_
?
} | ||
|
||
/* The C symbol for a tail-callee export from an arbitrary module. */ | ||
// static |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks strange mixing C and C++ comments directly next each other like this.. maybe its fine since the // static
is more like the type hint?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed that it's a little strange, but it matches the existing style (e.g. on CWriter::ExportName and CWriter::ModuleInstanceTypeName). We could maybe change all these together in a future PR?
src/c-writer.cc
Outdated
WriteTailCallFuncDeclaration( | ||
TailCallExportName(import->module_name, import->field_name)); | ||
Write(Newline(), "#endif", Newline(), OpenBrace()); | ||
Write("next->fn = NULL;", Newline()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it be readable/maintainable to define a macro in the internal header rather than try to stamp out the if/else/endif
here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done (or at least attempted -- doing the #pragma inside a #define was a little tricky but hopefully the CI passes on Windows).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Still need to look at a part of c-writer)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, one more batch of comments. I finally managed to go through the whole PR this time. Biggest concern is one new source of UB here that we need to avoid.
I think I finally understand the strategy for tailcalls you have chosen and the codegen for this in the example files now makes sense to me. This is nice in that we won't need any C compiler attributes to force a tail-call, since we are doing it by hand in the tail_call_stack.
I can't say I understand 100% of the c-writer code, but it mostly made sense to me. With a little code-dedup and refactoring, I think this is nearly there.
#else | ||
#define WEAK_FUNC_DECL(func, fallback) \ | ||
__attribute__((weak)) void func(void** instance_ptr, void* tail_call_stack, \ | ||
wasm_rt_tailcallee_t* next) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This version seems to completely ignore fallback
.. how does that work?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i think the fallback is just an alternate way to implement weak symbols in MSVC. IIUC, they aren't used/needed in the non MSVC environments as weak symbols are available.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess I don't understand how that fallback function is used on MSVC but a weak symbol is used otherwise. Do we also declare the fallback symbol only under MSVC?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, the "fallback" parameter is only used on MSVC.
On GCC/clang, the "weak import" is implemented by defining a stub version of the import under its real name, and marking it as a weak symbol so it will be overridden by the real import if it exists.
On MSVC, it's implemented by defining the stub version of the import under the "fallback" name, and then telling the linker to use the fallback as a substitute for the real import if the real import doesn't exist.
@sbc100 Did you want to weigh in again on this one? My thinking is that after we get this in, we can finally update the testsuite and possibly cut a new release. |
Ok, merging now, and then let's try to get the testsuite updated, a release cut, and #2308 merged (not necessarily in that order). |
This PR implements the tail-call proposal in wasm2c. (Will be needed to update the testsuite -- the exception-handling tests now use tail calls.)
The basic strategy is:
void fn(void **instance_ptr, void *tail_call_stack, wasm_rt_tailcallee_t *next)
tail_call_stack
argument, and when it returns, it packs its results back into thetail_call_stack
return_call
orreturn_call_indirect
, it setsnext
to point to the new function andinstance_ptr
to point to its originating module instance. If it returns normally, it setsnext
to NULL.return_call
orreturn_call_indirect
, it implements a trampoline: it allocates space for the tail_call_stack, calls the tailcallee version of function, and then loops forever untilnext
is NULL.To simplify the generated code: