leaningtech / cheerp-meta

Cheerp - a C/C++ compiler for Web applications - compiles to WebAssembly and JavaScript
https://labs.leaningtech.com/cheerp
Other
1.03k stars 51 forks source link

_asm_ clobber list boo-boos ?! #83

Closed ghost closed 5 years ago

ghost commented 5 years ago

Hello,

I thought cheerp should be able to understand that temporary variables in clobber list are not valid CPU register names. However, I am not able to transpile code decorated with non-empty clobber list _asm_ .

CMakeLists.txt

# Skip _asm_ on native build
add_definitions(-D_NATIVE_BUILD_)

# Transpile to JavaScript post build
add_custom_command(TARGET mylib POST_BUILD
        COMMAND /opt/cheerp/bin/clang++ -target cheerp -cheerp-preexecute cheerp-mode=wasm -cheerp-wasm-loader=mylib.js -O2 -o mylib.wasm mylib.cpp
        WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
        COMMENT "Transpiling to JavaScript..."
        )

Code

#ifndef _NATIVE_BUILD_
    __asm__("var jsTemp1=%0; var jsTemp2=jsTemp1+1; console.log(jsTemp2);" : /*No output*/ : "r"(42) : /*Clobber list*/ "jsTemp1","jsTemp2"); // This will print out "43"
#endif

Error

Transpiling to JavaScript...
mylib.cpp:68:121: error: unknown register name 'jsTemp1' in asm
    __asm__("var jsTemp1=%0; var jsTemp2=jsTemp1+1; console.log(jsTemp2);" : /*No output*/ : "r"(42) : /*Clobber list*/ "jsTemp1","jsTemp2"); // This will print out "43"

Cheerp info

/opt/cheerp/bin/clang++ --version
Cheerp 2.0rc1-3~xenial clang version 3.7.0  (based on LLVM 3.7.0svn)
Target: x86_64-pc-linux-gnu
Thread model: posix

Quite confusing... :|

alexp-sssup commented 5 years ago

I see you are using Cheerp 2.0 RC1. Support for clobbering is only available in nightly builds.

Edit: Fixed for previous reference to RC2. In reality only nightly builds supports this feature.

ghost commented 5 years ago

After building cheerp from source, as detailed here, clang kept crashing every time a compilation was attempted.

I am currently rebuilding cheerp and will post logs in case I am able to replicate.

alexp-sssup commented 5 years ago

You can find ready made nightly Linux packages here: https://launchpad.net/~leaningtech-dev/+archive/ubuntu/cheerp-nightly-ppa

ghost commented 5 years ago

Same clobber list related error with nightly build.

/opt/cheerp/bin/clang++ --version
Cheerp 1516806243-1~xenial clang version 3.7.0  (based on LLVM 3.7.0svn)
Target: x86_64-pc-linux-gnu
Thread model: posix
Transpiling to JavaScript...
mylib.cpp:68:121: error: unknown register name 'jsTemp1' in asm
    __asm__("var jsTemp1=%0; var jsTemp2=jsTemp1+1; console.log(jsTemp2);" : /*No output*/ : "r"(42) : /*Clobber list*/ "jsTemp1","jsTemp2"); // This will print out "43"
alexp-sssup commented 5 years ago

Since you are using xenial you are getting an older version. You can force Ubuntu to use the newer repo by replacing xenial with bionic in /etc/apt/sources.list.d/leaningtech-dev*.list

ghost commented 5 years ago

Alright, with

/opt/cheerp/bin/clang++ --version
Cheerp 1545237965-1~bionic clang version 3.7.0  (based on LLVM 3.7.0svn)
Target: x86_64-pc-linux-gnu
Thread model: posix

... there is no more complaining about unknown register names, however things that used to work before aren't working anymore. Is this nightly build including breaking changes?

From example, the following code (copy paste from here):

void webMain() {
    __asm__("console.log('Inline JS example')");
}

Error:

Transpiling to JavaScript...
internal.cpp:59:5: error: Cheerp: Cannot use inline asm in a 'asmjs' function
    __asm__("console.log('Inline JS example')");
    ^

That's funny, because I am transpiling with-cheerp-mode=wasm.

Decorating webMain with [[cheerp::wasm]] merely changes the error message to error: Cheerp: Cannot use inline asm in a 'wasm' function

Then I have a class with no cheerp::genericjs attribute, which should be compiled to WebAssembly:

class Container {
        public:
            Container() = default;
            ~Container() = default;

            std::string fn001();

        private:
            std::vector<wchar_t> data;
            std::vector<std::function<client::String(unsigned int)>> functors;
            int (*non_member_func_array[4]) (int x, int y);
            int (Container::*member_func_array[]) (int x, int y);
        };

If I dump asm in fn001's implementation:

std::string Container::fn001() {

    __asm__("console.log('Inline JS example')");

    return std::string();
}
error: Cheerp: Cannot use inline asm in a 'asmjs' function
    __asm__("console.log('Inline JS example')");
    ^

It appears that _asm_ only works within member functions of classes decorated with [[cheerp::genericjs]] attribute and cannot be made use of elsewhere. Can you confirm this?

I'm beginning to wonder if it weren't simpler to revert back to RC1, renounce clobber lists, transpile with -pretty-code and then run the output through closure compiler for minification. Would that be a thing?

alexp-sssup commented 5 years ago

As the error message says, you cannot use __asm__ to put JavaScript in the middle of wasm or asmjs functions. That is by design. If you want to use inline JavaScript you need to tag a method as [[cheerp::genericjs]]

alexp-sssup commented 5 years ago

Keep in mind that [[cheerp::asmjs]] and [[cheerp::wasm]] have the same semantics and are interchangeable. The actual output format is decided by -cheerp-mode=

alexp-sssup commented 5 years ago

Also, to clarify, this was not supported in RC1 as well. The code would compile but be broken before, so the current version is actually more robust.

ghost commented 5 years ago

What about webMain ? _asm_ should be possible in webMain (the docs say as much), however the following example from the docs does no transpile:

void webMain() {
    __asm__("console.log('Inline JS example')");
}

Error:

Transpiling to JavaScript...
internal.cpp:59:5: error: Cheerp: Cannot use inline asm in a 'asmjs' function
    __asm__("console.log('Inline JS example')");
    ^

I'm confused as of why this fails for webMain . The documentation makes no reference of decorating webMain with [[cheerp::genericjs]] attribute. If I decorate webMain with [[cheerp::genericjs]] it transpiles just fine, however I'm not sure if this is the recommended approach.

alexp-sssup commented 5 years ago

webMain is the entry point of the application, independently of the compilation mode. If you compile with -cheerp-mode=asmjs or -cheerp-mode=wasm all the non-tagged function will default to asmjs/wasm mode.

The documentation for __asm__ uses webMain just as an example.

ghost commented 5 years ago

Makes sense now. Thank you for the input. :)