Skip to content
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

Followup to #8438 - let NO_FILESYSTEM still work even if code uses files #8464

Merged
merged 19 commits into from
Apr 25, 2019

Conversation

kripken
Copy link
Member

@kripken kripken commented Apr 19, 2019

That flag means we manually want to not include filesystem support, even if the code seems to use it. That PR changed things to a compile-time error instead, which this fixes.

We should eventually avoid including the FS object entirely, see comment in the code.

…e filesystem. that flag means we manually want to not include filesystem support, even if the code seems to use it.
@sbc100
Copy link
Collaborator

sbc100 commented Apr 19, 2019

Why not make this a compiler time failure? Shouldn't we always prefer compiler time errors.

Imagine you are working on a bug project that sets NO_FILESYSTEM. Someone adds some new code that using "fopen", but in some rare case. As a developer I'd like to know right away that my new code is not compatible with the project and needs modification.

@kripken
Copy link
Member Author

kripken commented Apr 19, 2019

Yeah, that makes sense in it's own way - we'd need a different flag, though. The current flag just means to include the minimal FS support possible, assuming it won't actually be called. I broke that in the other PR mentioned, so this just restores the previous behavior and unbreaks people.

In the longer term, with ASMFS we can maybe avoid this complexity entirely (there would be no JS dependencies for the filesystem).

@floooh
Copy link
Collaborator

floooh commented Apr 20, 2019

@sbc100 I'm having a "use-case" where it's convenient that NO_FILESYSTEM doesn't fail even when the code contains calls to fopen() etc, but never calls it:

Dear ImGui contains code to serialize the current UI state (windows positions, sizes, etc) to disc, so on next start, the UI can be restored, but this code isn't useful on emscripten anyway, since it's all synchronous. This code can be disabled at runtime, but cannot be removed at compile time with a define.

Compiling with NO_FILESYSTEM on emscripten still makes sense, since it reduces the download size.

Similar situations may also exist in other libraries (image loaders etc...) Those often have code to either load image data from memory (useful for emscripten), or directly through the CRT file functions (not useful for emscripten, since this is synchronous code). But often the file IO code cannot be removed from compilation because the library authors don't specifically had the emscripten situation in mind.

So if emscripten would make such situations a compile error, I would either have to remove NO_FILESYSTEM, increasing download size just for code that isn't called anyway... or pester a number of library authors with emscripten-specific PRs, which includes arguing about whether adding such fixes just for a single platform makes sense ;)

@kripken
Copy link
Member Author

kripken commented Apr 21, 2019

Oh, actually I got this wrong - my original testcase here didn't pass even before that PR. The issue is much narrower - if the filesystem is disabled, then syscalls should not depend on it. That fixes a refined testcase that I pushed with that proper fix.

@floooh , I think this should fix the regressions from that PR, does it fix your case?

@floooh
Copy link
Collaborator

floooh commented Apr 21, 2019

@kripken Yes, the PR fixes my samples.

I'm having some weird closure errors, but maybe I did something wrong when test-merging the PR locally.

But switching back and forth between incoming and this PR, and closure enabled disabled I can confirm that the PR fixes the FS linking issue.

@kripken
Copy link
Member Author

kripken commented Apr 22, 2019

Building with -g1, the closure error should be more readable. We should verify this PR didn't break anything there (in theory any JS change could).

@floooh
Copy link
Collaborator

floooh commented Apr 22, 2019

This is the closure error output I get when linking with -g1 (including the linker command line), further down there are indeed FS-related undeclared variables:

FAILED: /Users/floh/projects/fips-deploy/sokol-samples/sapp-webgl2-wasm-ninja-release/clear-sapp-ui.html
: && /Users/floh/projects/fips-sdks/osx/emsdk-portable/emscripten/incoming/em++  --em-config /Users/floh/projects/fips-sdks/osx/emsdk-portable/.emscripten --cache /Users/floh/projects/fips-sdks/osx/emsdk-portable/.emscripten_cache  -fno-exceptions -fno-rtti -std=c++11 -stdlib=libc++ -fstrict-aliasing -Wall -Wno-multichar -Wextra -Wno-unused-parameter -Wno-unknown-pragmas -Wno-ignored-qualifiers -Wno-long-long -Wno-overloaded-virtual -Wno-deprecated-writable-strings -Wno-unused-volatile-lvalue -Wno-inconsistent-missing-override -Wno-warn-absolute-paths -Wno-expansion-to-defined -O3 -DNDEBUG  --em-config /Users/floh/projects/fips-sdks/osx/emsdk-portable/.emscripten --cache /Users/floh/projects/fips-sdks/osx/emsdk-portable/.emscripten_cache  --memory-init-file 0 -s TOTAL_MEMORY=134217728 -s ERROR_ON_UNDEFINED_SYMBOLS=1 -s NO_EXIT_RUNTIME=1 -s "BINARYEN_TRAP_MODE='clamp'" -s USE_WEBGL2=1 -s "MALLOC='emmalloc'" -s NO_FILESYSTEM=1 -s DISABLE_EXCEPTION_CATCHING=1 -s WASM=1  --shell-file /Users/floh/projects/sokol-samples/webpage/shell.html -g1 -O3  --llvm-lto 1 --closure 1 -s ASSERTIONS=0  -rdynamic sapp/CMakeFiles/clear-sapp-ui.dir/clear-sapp.c.o  -o /Users/floh/projects/fips-deploy/sokol-samples/sapp-webgl2-wasm-ninja-release/clear-sapp-ui.html  sapp/sokol/libsokol.a sapp/ui/libdbgui.a fips-imgui_imgui/libimgui.a && :
/Users/floh/projects/fips-sdks/osx/emsdk-portable/emscripten/incoming/third_party/closure-compiler/node-externs/domain.js:67: WARNING - Bad type annotation. type not recognized due to syntax error. See https://github.com/google/closure-compiler/wiki/Annotating-JavaScript-for-the-Closure-Compiler for more information.
 * @param {function(...[*])} callback
                       ^

/Users/floh/projects/fips-sdks/osx/emsdk-portable/emscripten/incoming/third_party/closure-compiler/node-externs/domain.js:68: WARNING - Bad type annotation. type not recognized due to syntax error. See https://github.com/google/closure-compiler/wiki/Annotating-JavaScript-for-the-Closure-Compiler for more information.
 * @return {function(...[*])}
                        ^

/Users/floh/projects/fips-sdks/osx/emsdk-portable/emscripten/incoming/third_party/closure-compiler/node-externs/domain.js:73: WARNING - Bad type annotation. type not recognized due to syntax error. See https://github.com/google/closure-compiler/wiki/Annotating-JavaScript-for-the-Closure-Compiler for more information.
 * @param {function(...[*])} callback
                       ^

/Users/floh/projects/fips-sdks/osx/emsdk-portable/emscripten/incoming/third_party/closure-compiler/node-externs/domain.js:74: WARNING - Bad type annotation. type not recognized due to syntax error. See https://github.com/google/closure-compiler/wiki/Annotating-JavaScript-for-the-Closure-Compiler for more information.
 * @return {function(...[*])}
                        ^

/tmp/emscripten_temp_94QTrH/clear-sapp-ui.bc.o.js.mem.js.jso.js.jso.js.jso.js.jso.js:2405: WARNING - Duplicate case in a switch statement.
 case 34042:
 ^^^^^^^^^^^

/tmp/emscripten_temp_94QTrH/clear-sapp-ui.bc.o.js.mem.js.jso.js.jso.js.jso.js.jso.js:2448: WARNING - Duplicate case in a switch statement.
 case 34042:
 ^^^^^^^^^^^

/tmp/emscripten_temp_94QTrH/clear-sapp-ui.bc.o.js.mem.js.jso.js.jso.js.jso.js.jso.js:2831: WARNING - dangerous use of the global this object
 this.name = "ExitStatus";
 ^^^^

/tmp/emscripten_temp_94QTrH/clear-sapp-ui.bc.o.js.mem.js.jso.js.jso.js.jso.js.jso.js:2832: WARNING - dangerous use of the global this object
 this.message = "Program terminated with exit(" + status + ")";
 ^^^^

/tmp/emscripten_temp_94QTrH/clear-sapp-ui.bc.o.js.mem.js.jso.js.jso.js.jso.js.jso.js:2833: WARNING - dangerous use of the global this object
 this.status = status;
 ^^^^

/tmp/emscripten_temp_94QTrH/clear-sapp-ui.bc.o.js.mem.js.jso.js.jso.js.jso.js.jso.js:601: ERROR - variable ERRNO_CODES is undeclared
    if (!dirstream) throw new FS.ErrnoError(ERRNO_CODES.EBADF);
                                            ^^^^^^^^^^^

/tmp/emscripten_temp_94QTrH/clear-sapp-ui.bc.o.js.mem.js.jso.js.jso.js.jso.js.jso.js:604: ERROR - variable PATH is undeclared
   path = PATH.join2(dir, path);
          ^^^^

/tmp/emscripten_temp_94QTrH/clear-sapp-ui.bc.o.js.mem.js.jso.js.jso.js.jso.js.jso.js:734: ERROR - variable SOCKFS is undeclared
  var socket = SOCKFS.getSocket(SYSCALLS.get());
               ^^^^^^

/tmp/emscripten_temp_94QTrH/clear-sapp-ui.bc.o.js.mem.js.jso.js.jso.js.jso.js.jso.js:741: ERROR - variable __read_sockaddr is undeclared
  var info = __read_sockaddr(addrp, addrlen);
             ^^^^^^^^^^^^^^^

/tmp/emscripten_temp_94QTrH/clear-sapp-ui.bc.o.js.mem.js.jso.js.jso.js.jso.js.jso.js:743: ERROR - variable DNS is undeclared
  info.addr = DNS.lookup_addr(info.addr) || info.addr;
              ^^^

5 error(s), 9 warning(s)
shared:ERROR: closure compiler failed (rc: 5.)

@kripken
Copy link
Member Author

kripken commented Apr 22, 2019

The ERRNO_CODES issue is definitely a bug, thanks. I'm less sure about those other errors, but sometimes the later errors in closure are false positives - can you please check with the current PR (which fixes the ERRNO_CODES stuff, I think :)

@floooh
Copy link
Collaborator

floooh commented Apr 23, 2019

The ERRNO_CODE error is gone, but the others are still there:

[12/12] Linking CXX executable /Users/floh/projects/fips-d...-samples/sapp-webgl2-wasm-ninja-release/clear-sapp-ui.html
FAILED: /Users/floh/projects/fips-deploy/sokol-samples/sapp-webgl2-wasm-ninja-release/clear-sapp-ui.html
: && /Users/floh/projects/fips-sdks/osx/emsdk-portable/emscripten/incoming/em++  --em-config /Users/floh/projects/fips-sdks/osx/emsdk-portable/.emscripten --cache /Users/floh/projects/fips-sdks/osx/emsdk-portable/.emscripten_cache  -fno-exceptions -fno-rtti -std=c++11 -stdlib=libc++ -fstrict-aliasing -Wall -Wno-multichar -Wextra -Wno-unused-parameter -Wno-unknown-pragmas -Wno-ignored-qualifiers -Wno-long-long -Wno-overloaded-virtual -Wno-deprecated-writable-strings -Wno-unused-volatile-lvalue -Wno-inconsistent-missing-override -Wno-warn-absolute-paths -Wno-expansion-to-defined -O3 -DNDEBUG  --em-config /Users/floh/projects/fips-sdks/osx/emsdk-portable/.emscripten --cache /Users/floh/projects/fips-sdks/osx/emsdk-portable/.emscripten_cache  --memory-init-file 0 -s TOTAL_MEMORY=134217728 -s ERROR_ON_UNDEFINED_SYMBOLS=1 -s NO_EXIT_RUNTIME=1 -s "BINARYEN_TRAP_MODE='clamp'" -s USE_WEBGL2=1 -s "MALLOC='emmalloc'" -s NO_FILESYSTEM=1 -s DISABLE_EXCEPTION_CATCHING=1 -s WASM=1  --shell-file /Users/floh/projects/sokol-samples/webpage/shell.html -O3  --llvm-lto 1 --closure 1 -s ASSERTIONS=0 -g1  -rdynamic sapp/CMakeFiles/clear-sapp-ui.dir/clear-sapp.c.o  -o /Users/floh/projects/fips-deploy/sokol-samples/sapp-webgl2-wasm-ninja-release/clear-sapp-ui.html  sapp/sokol/libsokol.a sapp/ui/libdbgui.a fips-imgui_imgui/libimgui.a && :
/Users/floh/projects/fips-sdks/osx/emsdk-portable/emscripten/incoming/third_party/closure-compiler/node-externs/domain.js:67: WARNING - Bad type annotation. type not recognized due to syntax error. See https://github.com/google/closure-compiler/wiki/Annotating-JavaScript-for-the-Closure-Compiler for more information.
 * @param {function(...[*])} callback
                       ^

/Users/floh/projects/fips-sdks/osx/emsdk-portable/emscripten/incoming/third_party/closure-compiler/node-externs/domain.js:68: WARNING - Bad type annotation. type not recognized due to syntax error. See https://github.com/google/closure-compiler/wiki/Annotating-JavaScript-for-the-Closure-Compiler for more information.
 * @return {function(...[*])}
                        ^

/Users/floh/projects/fips-sdks/osx/emsdk-portable/emscripten/incoming/third_party/closure-compiler/node-externs/domain.js:73: WARNING - Bad type annotation. type not recognized due to syntax error. See https://github.com/google/closure-compiler/wiki/Annotating-JavaScript-for-the-Closure-Compiler for more information.
 * @param {function(...[*])} callback
                       ^

/Users/floh/projects/fips-sdks/osx/emsdk-portable/emscripten/incoming/third_party/closure-compiler/node-externs/domain.js:74: WARNING - Bad type annotation. type not recognized due to syntax error. See https://github.com/google/closure-compiler/wiki/Annotating-JavaScript-for-the-Closure-Compiler for more information.
 * @return {function(...[*])}
                        ^

/tmp/emscripten_temp_xitXTJ/clear-sapp-ui.bc.o.js.mem.js.jso.js.jso.js.jso.js.jso.js:2405: WARNING - Duplicate case in a switch statement.
 case 34042:
 ^^^^^^^^^^^

/tmp/emscripten_temp_xitXTJ/clear-sapp-ui.bc.o.js.mem.js.jso.js.jso.js.jso.js.jso.js:2448: WARNING - Duplicate case in a switch statement.
 case 34042:
 ^^^^^^^^^^^

/tmp/emscripten_temp_xitXTJ/clear-sapp-ui.bc.o.js.mem.js.jso.js.jso.js.jso.js.jso.js:2831: WARNING - dangerous use of the global this object
 this.name = "ExitStatus";
 ^^^^

/tmp/emscripten_temp_xitXTJ/clear-sapp-ui.bc.o.js.mem.js.jso.js.jso.js.jso.js.jso.js:2832: WARNING - dangerous use of the global this object
 this.message = "Program terminated with exit(" + status + ")";
 ^^^^

/tmp/emscripten_temp_xitXTJ/clear-sapp-ui.bc.o.js.mem.js.jso.js.jso.js.jso.js.jso.js:2833: WARNING - dangerous use of the global this object
 this.status = status;
 ^^^^

/tmp/emscripten_temp_xitXTJ/clear-sapp-ui.bc.o.js.mem.js.jso.js.jso.js.jso.js.jso.js:604: ERROR - variable PATH is undeclared
   path = PATH.join2(dir, path);
          ^^^^

/tmp/emscripten_temp_xitXTJ/clear-sapp-ui.bc.o.js.mem.js.jso.js.jso.js.jso.js.jso.js:734: ERROR - variable SOCKFS is undeclared
  var socket = SOCKFS.getSocket(SYSCALLS.get());
               ^^^^^^

/tmp/emscripten_temp_xitXTJ/clear-sapp-ui.bc.o.js.mem.js.jso.js.jso.js.jso.js.jso.js:741: ERROR - variable __read_sockaddr is undeclared
  var info = __read_sockaddr(addrp, addrlen);
             ^^^^^^^^^^^^^^^

/tmp/emscripten_temp_xitXTJ/clear-sapp-ui.bc.o.js.mem.js.jso.js.jso.js.jso.js.jso.js:743: ERROR - variable DNS is undeclared
  info.addr = DNS.lookup_addr(info.addr) || info.addr;
              ^^^

4 error(s), 9 warning(s)
shared:ERROR: closure compiler failed (rc: 4.)
ninja: build stopped: subcommand failed.

@floooh
Copy link
Collaborator

floooh commented Apr 23, 2019

PS: just to make sure I didn't mess up anything in my previous tests, I tested the PR on a fresh emscripten install, and the errors are the same.

@kripken
Copy link
Member Author

kripken commented Apr 23, 2019

Interesting. I think this has uncovered a bunch of missing dependencies in our JS libraries. I'll keep working on it in this PR.

@kripken
Copy link
Member Author

kripken commented Apr 24, 2019

Ok, tests are passing - @sbc100 , PTAL. Detailed description of where this ended up:

  • Followup to Fix filesystem/syscall optimization #8438 - fix dependencies problems that PR created (they just surfaced because of it - we were lucky before that they were not noticeable, since we included too much code, which hid things).
  • Split up PATH into PATH and PATH_FS, where the latter depends on FS while the former does not. This allows code to use basic path functionality without getting the whole FS.
  • Fix missing PATH dependencies through the codebase - turns out we missed them because FS depended on it, and we assumed that covered things (which it mostly does).
  • Refactor syscall internals to move sockets code into the sockets syscall, so non-socket-using code doesn't get sockets + FS + all the support for that.
  • Optimize syscall internals to not depend on ERRNO_CODES.

I also verified the sockets tests pass, which we don't run on CI here.

@kripken
Copy link
Member Author

kripken commented Apr 24, 2019

@floooh, please check if this works on your codebases too.

@floooh
Copy link
Collaborator

floooh commented Apr 24, 2019

Everything working now, tested on my sokol-samples and the tiny-emulators, both with NO_FILESYSTEM=1 and closure enabled.

Thanks!

Copy link
Collaborator

@sbc100 sbc100 left a comment

Choose a reason for hiding this comment

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

Mostly lgtm. Can you explain any using cDefine('') is better than ERRNO_CODES? Is ERRNO_CODES legacy?

@@ -4468,6 +4468,8 @@ def clean(out, err):

def test_mount(self):
self.set_setting('FORCE_FILESYSTEM', 1)
self.set_setting('INCLUDE_FULL_LIBRARY', 1) # uses constants from ERRNO_CODES
self.set_setting('ERROR_ON_UNDEFINED_SYMBOLS', 0) # avoid errors when linking in full library
Copy link
Collaborator

Choose a reason for hiding this comment

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

This seems like a regression, no?

Copy link
Member Author

Choose a reason for hiding this comment

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

The issue here is that the test depends on ERRNO_CODES - which is an internal API. So in a sense, FS not automatically including it is a regression and users might notice it. But I think the risk is worth it for emitting smaller code.

Copy link
Collaborator

Choose a reason for hiding this comment

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

This seems a little gross.. but I'll take your word for it that there are no obvious better ways to achieve this or to write this test.

Copy link
Member Author

Choose a reason for hiding this comment

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

Another option is to hardcode the constants in the test. I pushed a commit with that now.

#include <sys/time.h>
#include <stddef.h>
int main() {
extern int __syscall295(int);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Function declaration normally go at global scope.

Copy link
Member Author

Choose a reason for hiding this comment

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

Fixed.

@kripken
Copy link
Member Author

kripken commented Apr 24, 2019

cDefine computes it at compile time, so it just embeds the constant there. That saves code size, since we don't need to include all the ERRNO_CODES array.

@@ -4468,6 +4468,8 @@ def clean(out, err):

def test_mount(self):
self.set_setting('FORCE_FILESYSTEM', 1)
self.set_setting('INCLUDE_FULL_LIBRARY', 1) # uses constants from ERRNO_CODES
self.set_setting('ERROR_ON_UNDEFINED_SYMBOLS', 0) # avoid errors when linking in full library
Copy link
Collaborator

Choose a reason for hiding this comment

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

This seems a little gross.. but I'll take your word for it that there are no obvious better ways to achieve this or to write this test.

@kripken
Copy link
Member Author

kripken commented Apr 24, 2019

Also pushed a commit with a fix after merging latest incoming.

@kripken
Copy link
Member Author

kripken commented Apr 25, 2019

Merging to resolve the existing breakage (this also affected binaryen CI), but if you don't like the change I made to that one test in the last commit, I can revert that in a followup.

@kripken kripken merged commit 55ad1c4 into incoming Apr 25, 2019
@kripken kripken deleted the no_fs branch April 25, 2019 02:07
@floooh
Copy link
Collaborator

floooh commented Apr 25, 2019

Thanks for merging, I have recompiled my sokol-samples and emulators with the current incoming and it's all looking good:

https://floooh.github.io/sokol-html5/
https://floooh.github.io/tiny8bit/

The most simple clear-sample (https://floooh.github.io/sokol-html5/wasm/clear-sapp.html) is now 8.0 KB for the WASM and 8.4 KB for the JS (both compressed download size in Chrome). I haven't checked for a while, but I think that's a new record.

Also the compressed download size for the C64 emulator (https://floooh.github.io/tiny8bit/c64.html) now fits comfortably into under 64KBs (with closure enabled and all the latest size improvements). And this even includes the embedded C64 Kernal/BASIC ROMs, and 8-bit CPU machine code compresses a whole lot worse than even WASM ;)

kripken added a commit that referenced this pull request Apr 30, 2019
…_FILESYSTEM` as 0 too (#8524)

Otherwise, we incorrectly try to use the filesystem in some cases, like the testcase added here with libc++ code. This also affected binaryen which is how I noticed.

Followup to #8438 and #8464.
VirtualTim pushed a commit to VirtualTim/emscripten that referenced this pull request May 21, 2019
…created (emscripten-core#8464)

They just surfaced because of it - we were lucky before that they were not noticeable, since we included too much code, which hid things.

*    Split up PATH into PATH and PATH_FS, where the latter depends on FS while the former does not. This allows code to use basic path functionality without getting the whole FS.
*    Fix missing PATH dependencies through the codebase - turns out we missed them because FS depended on it, and we assumed that covered things (which it mostly does).
*    Refactor syscall internals to move sockets code into the sockets syscall, so non-socket-using code doesn't get sockets + FS + all the support for that.
*    Optimize syscall internals to not depend on ERRNO_CODES.
VirtualTim pushed a commit to VirtualTim/emscripten that referenced this pull request May 21, 2019
…_FILESYSTEM` as 0 too (emscripten-core#8524)

Otherwise, we incorrectly try to use the filesystem in some cases, like the testcase added here with libc++ code. This also affected binaryen which is how I noticed.

Followup to emscripten-core#8438 and emscripten-core#8464.
VirtualTim added a commit to VirtualTim/emscripten that referenced this pull request May 23, 2019
VirtualTim added a commit to VirtualTim/emscripten that referenced this pull request May 23, 2019
belraquib pushed a commit to belraquib/emscripten that referenced this pull request Dec 23, 2020
…created (emscripten-core#8464)

They just surfaced because of it - we were lucky before that they were not noticeable, since we included too much code, which hid things.

*    Split up PATH into PATH and PATH_FS, where the latter depends on FS while the former does not. This allows code to use basic path functionality without getting the whole FS.
*    Fix missing PATH dependencies through the codebase - turns out we missed them because FS depended on it, and we assumed that covered things (which it mostly does).
*    Refactor syscall internals to move sockets code into the sockets syscall, so non-socket-using code doesn't get sockets + FS + all the support for that.
*    Optimize syscall internals to not depend on ERRNO_CODES.
belraquib pushed a commit to belraquib/emscripten that referenced this pull request Dec 23, 2020
…_FILESYSTEM` as 0 too (emscripten-core#8524)

Otherwise, we incorrectly try to use the filesystem in some cases, like the testcase added here with libc++ code. This also affected binaryen which is how I noticed.

Followup to emscripten-core#8438 and emscripten-core#8464.
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.

3 participants