Skip to content

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

Open
wants to merge 37 commits into
base: master
Choose a base branch
from
Open

Conversation

NWilson
Copy link
Owner

@NWilson NWilson commented Dec 7, 2017

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.

kaniini and others added 2 commits December 6, 2017 13:11
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.
* allocations. At 32KiB page size, we have <20% RAM
* waste which seems a bit more reasonable. All a bit arbitrary.
*/
#define PAGE_SIZE 32768

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.

TYPEDEF float float_t;
TYPEDEF double double_t;

TYPEDEF long time_t;

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.

Copy link
Owner Author

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.)

// 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, ...);
Copy link
Owner Author

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;

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.


static const char* const progname = "/a.out";

struct wasm_init_data_t wasm_init_data = {

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).

Copy link
Owner Author

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);
}

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.

Copy link
Owner Author

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.

Copy link
Owner Author

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.

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.

Copy link
Owner Author

Choose a reason for hiding this comment

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

I see, thanks!

#endif
AT_NULL, 0
}
};

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.).

@@ -0,0 +1,2 @@
#define _POSIX_V6_LP64_OFF64 1
#define _POSIX_V7_LP64_OFF64 1

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.

Copy link
Owner Author

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.
@NWilson
Copy link
Owner Author

NWilson commented Dec 12, 2017

On an aside, I'm currently having a go at a "JavaScript linker".

Emscripten currently does this using a very awkward <symbol>__deps annotation, which feels a bit odd to me. I'm trying to build something simple using nodejs, that does the following steps:

  1. Load WebAssembly file and list of Javascript files specified on the commandline
  2. For the Wasm module, grab its dependencies from its import list
  3. For the JS files, parse them using Shift AST, to get a list of symbol definitions, and extract all referenced variables using Shift's scope analyser, ie automatically build the deps list
  4. Output a Javascript module that pulls in the JS symbols that are used, and loads the Wasm module with the symbols it imports. JS symbols are all in a shared scope, so can reference each other regardless of whether they were pulled in by the Wasm module or other imports.

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.

richfelker and others added 7 commits December 12, 2017 13:12
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
Copy link
Owner Author

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).

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.

Copy link
Owner Author

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.

Copy link
Owner Author

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!

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.

7 participants