-
Notifications
You must be signed in to change notification settings - Fork 1
PR demonstrating WebAssembly support #1
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
base: master
Are you sure you want to change the base?
Conversation
notes added by maintainer: this function is a GNU extension. it was chosen over the similar BSD function funopen because the latter depends on fpos_t being an arithmetic type as part of its public API, conflicting with our definition of fpos_t and with the intent that it be an opaque type. it was accepted for inclusion because, despite not being widely used, it is usually very difficult to extricate software using it from the dependency on it. calling pattern for the read and write callbacks is not likely to match glibc or other implementations, but should work with any reasonable callbacks. in particular the read function is never called without at least one byte being needed to satisfy its caller, so that spurious blocking is not introduced. contracts for what callbacks called from inside libc/stdio can do are always complicated, and at some point still need to be specified explicitly. at the very least, the callbacks must return or block indefinitely (they cannot perform nonlocal exits) and they should not make calls to stdio using their own FILE as an argument.
stdio types use the struct tag names from glibc libio to match C++ ABI.
6bf1680
to
b638f75
Compare
* allocations. At 32KiB page size, we have <20% RAM | ||
* waste which seems a bit more reasonable. All a bit arbitrary. | ||
*/ | ||
#define PAGE_SIZE 32768 |
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.
(Random drive-by review comment...)
This property of page sizes may not always hold; it is possible that wasm could add mmap-like features in the future, in which case PAGE_SIZE should reflect the granularity of those operations.
I realize that you don't want to mix in bigger changes in this PR, but if malloc's heuristics are suboptimal for 64KiB page sizes, it would affect other architectures as well, including some arm64 platforms, so ideally the solution should be to fix malloc's heuristics.
arch/wasm/bits/alltypes.h.in
Outdated
TYPEDEF float float_t; | ||
TYPEDEF double double_t; | ||
|
||
TYPEDEF long time_t; |
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.
long
is 32-bit on wasm32, so using it for time_t
is susceptible to the 2038 bug. Since wasm has a full set of 64-bit operations, I suggest making time_t
64-bit.
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.
Yes, what was I thinking!?! The ABI is supposed to match the Linux x32 ABI, this must have snuck in from the x86 one by mistake.
I'll do a diff between arch/x32 and arch/wasm and make sure everything matches where possible.
(By comparing the syscall numbers from all the different architectures, I've deduced that Emscripten is currently trying to emulate the x86 ABI in its syscall interface, or at least seems to have copied the definitions from x86 most extensively. I think moving to the x32 definitions is a good move at this point.)
arch/wasm/bits/syscall.h.in
Outdated
// The list of syscalls is taken from x32, which contains a sensible list of | ||
// "modern" syscalls. | ||
|
||
#define SYSCALL_ACTION(x) long __syscall_##x(long arg1, ...); |
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.
Some syscalls take a variable number of arguments (like SYS_open, and SYS_mremap).
I thought it would be cunning to therefore give syscalls a varargs signature. (NB. The way static linking for syscalls has to work is that all syscalls must have the same signature. See how support for syscall()
and pthread_cancel()
was implemented by cunningly using each syscall's function pointer as its syscall number! Hat-tip to John Starks at Microsoft.)
Making them varargs is a silly idea, I should just make all take six normal arguments, and pass zeros for the ones that don't matter.
@@ -0,0 +1,31 @@ | |||
typedef int32_t int_fast16_t; |
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 definition of int_fast16_t differs from clang's, but I think it's better. I filed this bug to track the difference.
src/internal/wasm/wasm_init_data.c
Outdated
|
||
static const char* const progname = "/a.out"; | ||
|
||
struct wasm_init_data_t wasm_init_data = { |
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.
Should this be named __wasm_init_data
to avoid poluting the user namespace? Also, I think this can be const
(though wasm doesn't have read-only memory yet).
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.
Oops, hiding with underscores done.
Making const
unfortunately not possible; Musl functions like _start_c
from crt1.c expect a non-const pointer. Making const only to cast it away seems pointless.
I think for time being this sort of ELF emulation is OK. It's not too burdensome, and it's emulating a well-defined API for interaction with Musl.
If _start_wasm
does become a standard thing, we should maybe add argv/envp to it as arguments.
Finally, a getrandom opcode in Wasm (__builtin_wasm_getrandom
) would be nice :)
__attribute__((const)) float truncf(float x) | ||
{ | ||
return __builtin_truncf(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.
You can also optimize nearbyint
/nearbyintf
and rint
/rintf
using __builtin_nearbyint
/__builtin_nearbyintf
and __builtin_rint
/__builtin_rintf
, respectively, which codegen to f64.nearest
/f32.nearest
. (This is possible for rint because wasm doesn't have user-accessible floating-point exceptions, and for nearbyint and rint because wasm doesn't have user-modifiable rounding modes.
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.
Thanks, added those too.
I did a bit of trial-and-error to work out which math builtins could be optimised this way. Adding __attribute__((const))
was a bit of trial-and-error. Musl doesn't set errno for any maths functions, so it should be safe/consistent.
I was hoping to turn fmin/fmax into f64.min
, but couldn't seem to get it to be emitted by __builtin_fmax
. Those builtins are tricky, because if they don't lower to the expected native opcode, then they generate a recursive call to the libc function!
In any case, these routines in libc are pretty unimportant. LLVM replaces calls to these libc functions with the native opcodes anyway, so they won't/shouldn't actually be called here unless you're calling them by pointer.
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.
Can you think of a reason why this body doesn't emit a nice f64.min
instruction:
__attribute__((const)) double fmin(double x, double y) { return __builtin_fmin(x,y); }
I'm not confident in saying it's a bug in the WebAssembly frontend, or whether my expectation is wrong, but it is surprising that so many of the other maths builtins do 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.
When one operand is NaN, wasm's min
and max
return NaN, while C's fmin
and fmax
return the other operand. Both forms are useful, so I expect that in the future wasm will have opcodes for both forms. As a historical note, the reason why wasm initially left out the latter form because IEEE 754-2008's definition of this form had surprising behavior on signaling NaN (see here for a description). The IEEE 754 committee has recently recognized this as an error, and drafts of the upcoming 754-2018 have removed that definition and provided a new corrected one.
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 see, thanks!
src/internal/wasm/wasm_init_data.c
Outdated
#endif | ||
AT_NULL, 0 | ||
} | ||
}; |
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.
Random musing: I realize that this serves a purpose right now, though at some point we may want to explore other options. Portable code won't likely use interfaces like getauxv
in the first place, for wasm-specific code there are better ways to obtain most of this information, and for musl initialization, there isn't all that much code that needs these, and some of it needs auxv fields that aren't supported (AT_PHDR etc.).
arch/wasm/bits/posix.h
Outdated
@@ -0,0 +1,2 @@ | |||
#define _POSIX_V6_LP64_OFF64 1 | |||
#define _POSIX_V7_LP64_OFF64 1 |
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.
wasm32 is ILP32, so it looks like these should be ILP32_OFFBIG rather than LP64_OFF64, for wasm32.
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.
Hm, I just copied that blindly from the x32 port - which is also ILP32, surely? So it looks like a bug in arch/x32/bits/posix.h
that I didn't notice or think about. off_t
is complicated, I was just hoping I wouldn't have to check out the specs for it!
I'll raise it tomorrow on the Musl mailing list for the x32 port.
notes added by maintainer: the '-' specifier allows default padding to be suppressed, and '_' allows padding with spaces instead of the default (zeros). these extensions seem to be included in several other implementations including FreeBSD and derivatives, and Solaris. while portable software should not depend on them, time format strings are often exposed to the user for configurable time display. reportedly some python programs also use and depend on them.
On an aside, I'm currently having a go at a "JavaScript linker". Emscripten currently does this using a very awkward
I don't think there's anything quite like that at the moment? Emscripten's Python module loader has quite a lot of asm.js going on, and it would be nice to have a short & clean linker/JS-module-builder that just knows how to assemble the Javascript side of a Wasm module. Then there'd need to be a libc.js (derived from Emscripten's existing library.js) that implements some sane subset of the Musl syscalls. |
aside from theoretical arbitrary results due to UB, this could practically cause unbounded overflow of static array if hit, but hitting it depends on having more than 32 calls to at_quick_exit and having them sufficiently often.
sysconf should return -1 for infinity, not LONG_MAX.
notes by maintainer: both C and POSIX use the term UTC to specify related functionality, despite POSIX defining it as something more like UT1 or historical (pre-UTC) GMT without leap seconds. neither specifies the associated string for %Z. old choice of "GMT" violated principle of least surprise for users and some applications/tests. use "UTC" instead.
notes by maintainer: commit 2f853dd added these rules because the new system for handling arch-provided replacement files introduced for out-of-tree builds did not apply to the crt tree. commit 63bcda4 later adapted the makefile logic so that the crt and ldso trees go through the same replacement logic as everything else, but failed to remove the explicit rules that assumed the arch would always provide asm replacements. in addition to cleaning things up, removing these spurious rules allows crti/crtn asm to be omitted by an arch (thereby using the empty C files instead) if they are not needed.
#define LDBL_MAX 1.18973149535723176508575932662800702e+4932L | ||
#define LDBL_EPSILON 1.92592994438723585305597794258492732e-34L | ||
|
||
#define LDBL_MANT_DIG 113 |
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.
@sunfishcode, do you know how Wasm has 80-bit long-double support? I would have expected long-double to be the same as double, and was surprised to find that it's not. Does clang emulate the higher-precision, or is it fictitious?
I got these constants here by dumping Clang's predefined macros (as requested by Rich Felker in his review on the Musl mailing list).
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.
Clang is currently configured to make long-double be a 128-bit IEEE-754 quad-precision type. It is supported through software emulation in compiler-rt (it calls __addtf3 for addition, and so on).
The decision to do this is debatable. On one hand, making long double the same as double makes long double effectively useless, and making it longer makes it at least useful for some purposes (even if they are fairly narrow). On the other, musl's printf promotes all floating-point values to long double
to avoid duplicating code, and this means that all floating-point formating goes through slow software-emulated paths, and it increases code size for any program that calls printf.
I expect we'll eventually have custom versions of printf that will disable disable various bits of floating-point support, because many applications won't need everything (and the nature of printf format strings makes it difficult to dce unused formatting code), and if we do, this would significantly mitigate the problems. However, the utility of 128-bit long double is admittedly obscure, so it's not completely certain that the benefits outweigh the costs.
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.
When you say "Clang is currently configured", I assume that's specific to the WebAssembly backend? Given that GCC on x86 doesn't give you 128-bit quad-precision floats, the need for that level of precision seems remote.
I don't know - maybe people really would welcome having that high precision available in Wasm via Clang. But, to me it just seems like a pitfall.
Something to think about, thanks for explaining. I'd vote to have double match long-double on Wasm, at least until the demand for it is strong enough. Since we don't yet have dynamic linkage, changing the ABI isn't too big a deal.
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've found emscripten-core/emscripten#4340 now. I see Derek was thinking of going back to 64-bit long-double, but that doesn't seem to have happened. Oh well - up to you guys!
WebAssembly doesn't have user-accessible floating point exceptions, so there's no advantage to forcing floating-point expression evaluations.
a9ecdbe
to
14a308a
Compare
042895a
to
7cd91fc
Compare
I know that Musl doesn't accept changes via pull-requests!
This is just a way of showing the current status of my work.
Go to the diff to see what changes I made to support Wasm.