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

Add recipe for oneTBB #427

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open

Add recipe for oneTBB #427

wants to merge 6 commits into from

Conversation

andrjohns
Copy link

Adds recipe for building oneTBB following their instructions for WASM.

Let me know if I've missed anything, thanks!

@andrjohns andrjohns changed the title One tbb dep Add recipe for oneTBB May 21, 2024
@andrjohns
Copy link
Author

@georgestagg apologies for the ping, but would it be possible to have a look at this PR? I'd like to fix building/linking for packages using RcppParallel and the TBB

Copy link
Member

@georgestagg georgestagg left a comment

Choose a reason for hiding this comment

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

Thank you for the PR and for working on this! My apologies for the delay in getting around to reviewing it.

The Make rules for oneTBB look good to me overall (don't mind the GitHub CI failure for the Docker build).

I can see how we'll need to compile our own oneTBB library for Wasm, rather than using the vendored tbb in RcppParallel, and so I'm okay with adding the library in principle to our list of optional wasm libs.

Feedback:

  • I have added a couple of minor nits as inline comments below.

  • I think there an issue with the current implementation in this PR. I used make all to include the libtbb.a and libtbbmalloc.a libraries in my development version of webR and then attempted to build RcppParallel pointing to the TBB Wasm library. On my machine (macOS), this failed due to duplicate symbols in the libraries. Emscripten's implementation of the Wasm equivalent of static libraries can be a little unforgiving when it comes to issues like that, it may be that the TMB library requires further patching.

Does building RcppParallel work as expected on your machine with this change? If so, what OS are you using?

If we can fix or work around the duplicated symbol problem when compiling RcppParallel while linking to this Wasm build of TBB, I'll be happy to merge this PR.


Build log for my machine:

$ TBB_INC="/Users/georgestagg/work/webr/wasm/include" TBB_LIB="/Users/georgestagg/work/webr/wasm/lib"  R 

R version 4.4.0 (2024-04-24) -- "Puppy Cup"
Copyright (C) 2024 The R Foundation for Statistical Computing
Platform: aarch64-apple-darwin20

R is free software and comes with ABSOLUTELY NO WARRANTY.
You are welcome to redistribute it under certain conditions.
Type 'license()' or 'licence()' for distribution details.

R is a collaborative project with many contributors.
Type 'contributors()' for more information and
'citation()' on how to cite R or R packages in publications.

Type 'demo()' for some demos, 'help()' for on-line help, or
'help.start()' for an HTML browser interface to help.
Type 'q()' to quit R.

> rwasm::add_pkg("RcppParallel")
✔ Loading metadata database ... done
Processing 1 package(s).
trying URL 'https://packagemanager.posit.co/cran/latest/src/contrib/RcppParallel_5.1.7.tar.gz'
Content type 'application/x-tar' length 1634152 bytes (1.6 MB)
==================================================
downloaded 1.6 MB

                                                                                               
→ The package (0 B) is cached.
ℹ No downloads are needed
✔ :  [4.2s]  
* installing *source* package ‘RcppParallel’ ...
file ‘configure’ has the wrong MD5 checksum
** using non-staged installation
configure: sh -c "./configure.orig --build=aarch64-apple-darwin20 --host=wasm32-unknown-emscripten ac_cv_func_getrandom=no"
** preparing to configure package 'RcppParallel' ...
*** configured file: 'R/tbb-autodetected.R.in' => 'R/tbb-autodetected.R'
*** configured file: 'src/Makevars.in' => 'src/Makevars'
*** configured file: 'src/install.libs.R.in' => 'src/install.libs.R'
** finished configure for package 'RcppParallel'
** libs
using C++ compiler: ‘emcc (Emscripten gcc/clang-like replacement + linker emulating GNU ld) 3.1.47 (431685f05c67f0424c11473cc16798b9587bb536)’
using SDK: ‘MacOSX14.4.sdk’
em++ -std=gnu++17 -DNDEBUG -I../inst/include -I/Users/georgestagg/work/webr/wasm/include   -I/Users/georgestagg/work/webr/wasm/include -I/Users/georgestagg/work/webr/R/build/R-4.4.0/build/include -I/Users/georgestagg/work/webr/R/build/R-4.4.0/src/include -s USE_BZIP2=1 -s USE_ZLIB=1 -std=gnu++11 -fPIC    -Oz -fPIC -fwasm-exceptions -s SUPPORT_LONGJMP=wasm -DRCPP_DEMANGLER_ENABLED=0 -D__STRICT_ANSI__  -c init.cpp -o init.o
em++ -std=gnu++17 -DNDEBUG -I../inst/include -I/Users/georgestagg/work/webr/wasm/include   -I/Users/georgestagg/work/webr/wasm/include -I/Users/georgestagg/work/webr/R/build/R-4.4.0/build/include -I/Users/georgestagg/work/webr/R/build/R-4.4.0/src/include -s USE_BZIP2=1 -s USE_ZLIB=1 -std=gnu++11 -fPIC    -Oz -fPIC -fwasm-exceptions -s SUPPORT_LONGJMP=wasm -DRCPP_DEMANGLER_ENABLED=0 -D__STRICT_ANSI__  -c options.cpp -o options.o
em++ -std=gnu++17 -s SIDE_MODULE=1 -s WASM_BIGINT -s ASSERTIONS=1 -L/Users/georgestagg/work/webr/wasm/lib -L/Users/georgestagg/work/webr/wasm/R-4.4.0/lib/R/lib -s USE_BZIP2=1 -s USE_ZLIB=1 -fwasm-exceptions -s SUPPORT_LONGJMP=wasm -Oz -o RcppParallel.so init.o options.o -Wl,-L,/Users/georgestagg/work/webr/wasm/lib -Wl,-rpath,/Users/georgestagg/work/webr/wasm/lib -ltbb -ltbbmalloc
em++: warning: ignoring unsupported linker flag: `-rpath` [-Wlinkflags]
wasm-ld: error: duplicate symbol: __itt__ittapi_global
>>> defined in /Users/georgestagg/work/webr/wasm/lib/libtbb.a(itt_notify.cpp.o)
>>> defined in /Users/georgestagg/work/webr/wasm/lib/libtbbmalloc.a(itt_notify.cpp.o)

wasm-ld: error: duplicate symbol: __itt_detach_ptr__3_0
>>> defined in /Users/georgestagg/work/webr/wasm/lib/libtbb.a(itt_notify.cpp.o)
>>> defined in /Users/georgestagg/work/webr/wasm/lib/libtbbmalloc.a(itt_notify.cpp.o)

wasm-ld: error: duplicate symbol: __itt_sync_create_ptr__3_0
>>> defined in /Users/georgestagg/work/webr/wasm/lib/libtbb.a(itt_notify.cpp.o)
>>> defined in /Users/georgestagg/work/webr/wasm/lib/libtbbmalloc.a(itt_notify.cpp.o)

wasm-ld: error: duplicate symbol: __itt_sync_rename_ptr__3_0
>>> defined in /Users/georgestagg/work/webr/wasm/lib/libtbb.a(itt_notify.cpp.o)
>>> defined in /Users/georgestagg/work/webr/wasm/lib/libtbbmalloc.a(itt_notify.cpp.o)

wasm-ld: error: duplicate symbol: __itt_sync_destroy_ptr__3_0
>>> defined in /Users/georgestagg/work/webr/wasm/lib/libtbb.a(itt_notify.cpp.o)
>>> defined in /Users/georgestagg/work/webr/wasm/lib/libtbbmalloc.a(itt_notify.cpp.o)

wasm-ld: error: duplicate symbol: __itt_sync_prepare_ptr__3_0
>>> defined in /Users/georgestagg/work/webr/wasm/lib/libtbb.a(itt_notify.cpp.o)
>>> defined in /Users/georgestagg/work/webr/wasm/lib/libtbbmalloc.a(itt_notify.cpp.o)

wasm-ld: error: duplicate symbol: __itt_sync_cancel_ptr__3_0
>>> defined in /Users/georgestagg/work/webr/wasm/lib/libtbb.a(itt_notify.cpp.o)
>>> defined in /Users/georgestagg/work/webr/wasm/lib/libtbbmalloc.a(itt_notify.cpp.o)

wasm-ld: error: duplicate symbol: __itt_sync_acquired_ptr__3_0
>>> defined in /Users/georgestagg/work/webr/wasm/lib/libtbb.a(itt_notify.cpp.o)
>>> defined in /Users/georgestagg/work/webr/wasm/lib/libtbbmalloc.a(itt_notify.cpp.o)

wasm-ld: error: duplicate symbol: __itt_sync_releasing_ptr__3_0
>>> defined in /Users/georgestagg/work/webr/wasm/lib/libtbb.a(itt_notify.cpp.o)
>>> defined in /Users/georgestagg/work/webr/wasm/lib/libtbbmalloc.a(itt_notify.cpp.o)

wasm-ld: error: duplicate symbol: __itt_suppress_push_ptr__3_0
>>> defined in /Users/georgestagg/work/webr/wasm/lib/libtbb.a(itt_notify.cpp.o)
>>> defined in /Users/georgestagg/work/webr/wasm/lib/libtbbmalloc.a(itt_notify.cpp.o)

wasm-ld: error: duplicate symbol: __itt_suppress_pop_ptr__3_0
>>> defined in /Users/georgestagg/work/webr/wasm/lib/libtbb.a(itt_notify.cpp.o)
>>> defined in /Users/georgestagg/work/webr/wasm/lib/libtbbmalloc.a(itt_notify.cpp.o)

wasm-ld: error: duplicate symbol: __itt_suppress_mark_range_ptr__3_0
>>> defined in /Users/georgestagg/work/webr/wasm/lib/libtbb.a(itt_notify.cpp.o)
>>> defined in /Users/georgestagg/work/webr/wasm/lib/libtbbmalloc.a(itt_notify.cpp.o)

wasm-ld: error: duplicate symbol: __itt_suppress_clear_range_ptr__3_0
>>> defined in /Users/georgestagg/work/webr/wasm/lib/libtbb.a(itt_notify.cpp.o)
>>> defined in /Users/georgestagg/work/webr/wasm/lib/libtbbmalloc.a(itt_notify.cpp.o)

wasm-ld: error: duplicate symbol: __itt_fsync_prepare_ptr__3_0
>>> defined in /Users/georgestagg/work/webr/wasm/lib/libtbb.a(itt_notify.cpp.o)
>>> defined in /Users/georgestagg/work/webr/wasm/lib/libtbbmalloc.a(itt_notify.cpp.o)

wasm-ld: error: duplicate symbol: __itt_fsync_cancel_ptr__3_0
>>> defined in /Users/georgestagg/work/webr/wasm/lib/libtbb.a(itt_notify.cpp.o)
>>> defined in /Users/georgestagg/work/webr/wasm/lib/libtbbmalloc.a(itt_notify.cpp.o)

wasm-ld: error: duplicate symbol: __itt_fsync_acquired_ptr__3_0
>>> defined in /Users/georgestagg/work/webr/wasm/lib/libtbb.a(itt_notify.cpp.o)
>>> defined in /Users/georgestagg/work/webr/wasm/lib/libtbbmalloc.a(itt_notify.cpp.o)

wasm-ld: error: duplicate symbol: __itt_fsync_releasing_ptr__3_0
>>> defined in /Users/georgestagg/work/webr/wasm/lib/libtbb.a(itt_notify.cpp.o)
>>> defined in /Users/georgestagg/work/webr/wasm/lib/libtbbmalloc.a(itt_notify.cpp.o)

wasm-ld: error: duplicate symbol: __itt_model_site_begin_ptr__3_0
>>> defined in /Users/georgestagg/work/webr/wasm/lib/libtbb.a(itt_notify.cpp.o)
>>> defined in /Users/georgestagg/work/webr/wasm/lib/libtbbmalloc.a(itt_notify.cpp.o)

wasm-ld: error: duplicate symbol: __itt_model_site_end_ptr__3_0
>>> defined in /Users/georgestagg/work/webr/wasm/lib/libtbb.a(itt_notify.cpp.o)
>>> defined in /Users/georgestagg/work/webr/wasm/lib/libtbbmalloc.a(itt_notify.cpp.o)

wasm-ld: error: duplicate symbol: __itt_model_task_begin_ptr__3_0
>>> defined in /Users/georgestagg/work/webr/wasm/lib/libtbb.a(itt_notify.cpp.o)
>>> defined in /Users/georgestagg/work/webr/wasm/lib/libtbbmalloc.a(itt_notify.cpp.o)

wasm-ld: error: too many errors emitted, stopping now (use -error-limit=0 to see all errors)
em++: error: '/Users/georgestagg/emsdk/upstream/bin/wasm-ld -o RcppParallel.so --whole-archive -L/Users/georgestagg/work/webr/wasm/lib -L/Users/georgestagg/work/webr/wasm/R-4.4.0/lib/R/lib init.o options.o -L /Users/georgestagg/work/webr/wasm/lib /Users/georgestagg/work/webr/wasm/lib/libtbb.a /Users/georgestagg/work/webr/wasm/lib/libtbbmalloc.a -L/Users/georgestagg/emsdk/upstream/emscripten/cache/sysroot/lib/wasm32-emscripten/pic --no-whole-archive -mllvm -combiner-global-alias-analysis=false -mllvm -wasm-enable-sjlj -mllvm -disable-lsr -mllvm -wasm-enable-eh -mllvm -exception-model=wasm --import-memory --strip-debug --export-dynamic --export-if-defined=main --export-if-defined=__get_exception_message --export-if-defined=free --export-if-defined=__cpp_exception --export-if-defined=__cxa_increment_exception_refcount --export-if-defined=__cxa_decrement_exception_refcount --export-if-defined=__thrown_object_from_unwind_exception --export-if-defined=__start_em_asm --export-if-defined=__stop_em_asm --export-if-defined=__start_em_lib_deps --export-if-defined=__stop_em_lib_deps --export-if-defined=__start_em_js --export-if-defined=__stop_em_js --export-if-defined=__main_argc_argv --export-if-defined=__wasm_apply_data_relocs --export-if-defined=fflush --export=__wasm_call_ctors --experimental-pic -shared' failed (returned 1)
make: *** [RcppParallel.so] Error 1
ERROR: compilation failed for package ‘RcppParallel’

libs/recipes/oneTBB/rules.mk Outdated Show resolved Hide resolved
libs/recipes/oneTBB/targets.mk Outdated Show resolved Hide resolved
libs/recipes/oneTBB/rules.mk Outdated Show resolved Hide resolved
@andrjohns
Copy link
Author

Does building RcppParallel work as expected on your machine with this change? If so, what OS are you using?

If we can fix or work around the duplicated symbol problem when compiling RcppParallel while linking to this Wasm build of TBB, I'll be happy to merge this PR.

Oh interesting, I've been using pkg-config for setting the linking/include flags in RcppParallel (this branch here) so I hadn't seen this.

It looks like the pkg-config entry only specifies -ltbb for --libs, which is why it wasn't an issue for me. I can open a PR to add the changes to RcppParallel once this is in, and also open an issue upstream with the TBB to see if there's a known fix or similar?

@georgestagg
Copy link
Member

Ah ha! That makes sense. So I suppose this is a RcppParallel problem in trying to link both versions of the library when cross-compiling for Emscripten, rather than a problem with TBB itself.

OK, I will try again with your patched version of RcppParallel when I am back in front of my computer tomorrow. If that all goes well, I'll merge this PR and then we can send a patch upstream to get the Emscripten compatibility into RcppParallel.

@andrjohns
Copy link
Author

Brilliant, thanks!

@andrjohns
Copy link
Author

Also opened an issue upstream with oneTBB for the linking issue: uxlfoundation/oneTBB#1392

@andrjohns
Copy link
Author

@georgestagg I found a discussion about a similar error where a flag for disabling the build of itt_notify was recommended. I've added it to the recipe here and it's fixed the linking error for RcppParallel (hooray!)

Note that the RcppParallel install will subsequently fail at the install.libs.R step since it only looks for dynamic libs, but you can try my branch where I've added this for WASM.

One question about the changes for RcppParallel - would it be possible to update the rwasm::build/rwasm::add_pkg shims/environment to automatically set TBB_INC and TBB_LIB? I'm setting the paths in RcppParallel at the moment as part of the configure step, but that feels pretty fragile to future changes

@georgestagg
Copy link
Member

georgestagg commented Jun 3, 2024

I've added it to the recipe here and it's fixed the linking error for RcppParallel (hooray!)

Great! That looks like a reasonable solution to me.

Note that the RcppParallel install will subsequently fail at the install.libs.R step since it only looks for dynamic libs [...] would it be possible to update the rwasm::build/rwasm::add_pkg shims/environment to automatically set TBB_INC and TBB_LIB?

Adding TBB_INC and TBB_LIB to the build environment provided by rwasm is possible, but I'd like to avoid it if possible since the oneTBB library is marked as optional. I think the right way to do this is using pkg-config to detect the library at configure time and handle both the cases with and without TBB.

I also think we can skip the copy in install.libs.R, since the library is linked statically at build time anyway?

I've made some further minor tweaks to your RcppParallel patches at r-wasm/RcppParallel. This version uses pkg-config to find TBB under Emscripten, setting TBB_LIB and TBB_INC if it exists. If this version looks good to you, we can submit a PR to RcppParallel.


I have now been able to successfully compile r-wasm/RcppParallel@webr, linking to -ltbb -ltbbmalloc. I gather that eventually you want to compile andrjohns/StanEstimators, is there a Wasm branch somewhere? Currently rwasm::add_pkg("andrjohns/StanEstimators") gives me:

error: Unsupported machine word size.

@georgestagg
Copy link
Member

Ah, I see. The error remains because my host version of RCppParallel has not changed. It does not link to oneTBB and includes headers such as tbb/include/tbb/tbb_machine.h without Emscripten support.

In other words, this change does not fix StanEstimators. At least, not by itself. Coordination is required on the cross-compiling machine. Perhaps this needs some more thought.

@andrjohns
Copy link
Author

Ah, I see. The error remains because my host version of RCppParallel has not changed. It does not link to oneTBB and includes headers such as tbb/include/tbb/tbb_machine.h without Emscripten support.

Hmm that is a tricky one. Could the recipe install both Emscripten static libs and regular shared libs, such that setting TBB_INC and TBB_LIB is valid for both the host and cross-compiled RcppParallel?

@georgestagg
Copy link
Member

The vendored tbb headers in RCppParallel will also need to be modified to recognise __EMSCRIPTEN__ as a valid Unix-style machine, and the modified headers updated on the host machine independently to the rwasm build environment.

I updated the r-wasm/RCppParallel@webr fork to do just that, then ran pak::pak(r-wasm/RCppParallel@webr) to update my host version, and now StanEstimators compiles OK.

However, at runtime I still see:

Could not load dynamic lib: /usr/lib/R/library/StanEstimators/libs/StanEstimators.so
Error: bad export type for '_ZTIN3tbb4taskE': undefined

I wonder if StanEstimators should be linking to oneTBB directly, rather than through RCppParallel, since we set -fvisibility=hidden by default under Wasm.


BTW: Somehow the BH R package (a prerequisite here) is 121MB!! That is not a reasonable download. I'm not sure what's happened there, but it's unrelated to this particular issue. Will investigate another day...

@andrjohns
Copy link
Author

The vendored tbb headers in RCppParallel will also need to be modified to recognise EMSCRIPTEN as a valid Unix-style machine, and the modified headers updated on the host machine independently to the rwasm build environment.

The vendored headers shouldn't need to be touched - since setting TBB_INC results in the system headers getting copied into RcppParallel's inst directory

I wonder if StanEstimators should be linking to oneTBB directly, rather than through RCppParallel, since we set -fvisibility=hidden by default under Wasm.

That could indicate an issue with the config, since all packages that depend on RcppParallel use a call to RcppParallelLibs() in their Makevars which should be returning the flags for linking against the system TBB.

I can have a look at the RcppParallel config tomorrow

@georgestagg
Copy link
Member

The vendored headers shouldn't need to be touched - since setting TBB_INC results in the system headers getting copied into RcppParallel's inst directory.

I'd rather not require users to reinstall their host RCppParallel package and enforce that they set TBB_INC on their own system. Ideally, everything should be independent and cross-compiling packages for Wasm should not depend on the host system version of RCppParallel at all, but oh well - that is where we are right now.

I can have a look at the RcppParallel config tomorrow

OK. I'll return here if anything else occurs to me.

@andrjohns
Copy link
Author

It's not the 'cleanest' solution - but could the build call check for a dependency on RcppParallel and if found, once the dependencies are installed then temporarily replace the include/tbb dir and libtbb & libtbbmalloc files with the system libs?

That way the host RcppParallel is only modified for the cross-compilation, and no re-installation or user-intervention needed

@andrjohns
Copy link
Author

In general, how does the cross-compilation handle situations where a package needs to link against another package and include headers from the other package? Can it be configured to link/include against a cross-compiled/wasm package, instead of a host x86_64 package?

@georgestagg
Copy link
Member

georgestagg commented Jun 3, 2024

Can it be configured to link/include against a cross-compiled/wasm package, instead of a host x86_64 package?

It cannot currently. The rwasm package uses the host R installation, overriding Makevars so as to cross-compile. Including C headers from the host-installed version of a package listed in LinkingTo is handled by R itself (source here).

Including the host version of include headers has not caused issues up until this point, probably because for most R packages we've encountered they do not differ over architectures in this way. However, I can see on my machine that when the system RcppParallel includes are added the undefined symbols appear.

$ nm call_stan_orig.o | grep tbb4taskE
         U _ZTIN3tbb4taskE

OTOH manually compiling the object without -I'/[... my host library...]/library/RcppParallel/include' does not include the incorrect symbol:

$ nm call_stan.o | grep tbb4taskE

I am thinking about how to possibly override this, but I cannot easily see a way to stop R adding in those include arguments. I'm beginning to think your prior intuition was the right way to go after all: having the host RcppParallel also link/include the exact same TBB implementation, complied for x86 rather than Wasm. That way the Wasm and host architechture headers would be more compatible.

I think the best way to do this would be to include oneTBB in the official webR development docker image, set the TBB_LIB and TBB_INC env vars, and build the RcppParallel and StanEstimators packages there.

Sorry that it seems we're going around in circles, but I really want to make sure we get this right. I have to work on some other things now, but I'll come back to this when I can.

@andrjohns
Copy link
Author

Yeah the different symbols are caused by different headers unfortunately - RcppParallel includes the older "TBB" library, and there were some significant API changes when Intel changed to "oneTBB" - so some symbols are included in the older headers and not in the newer ones (and vice-versa)

Sorry that it seems we're going around in circles, but I really want to make sure we get this right.

Not at all! Linking with the TBB is a universally painful experience

I have to work on some other things now, but I'll come back to this when I can.

No worries, no rush on my end!

@andrjohns
Copy link
Author

Another one of the Stan devs just let me know that the current oneTBB has a bug which affects static/WASM initialisation, so this will have to wait until the next release anyway

@StaffanBetner
Copy link

StaffanBetner commented Jun 28, 2024

There was a new release of oneTBB the day before yesterday.

@andrjohns
Copy link
Author

A possible solution for the linking/building issue would be to change rwasm::build to use a temporary site-library when building packages - such that package dependencies which can be used safely 'as-is' are simply copied across, and dependencies for which the cross-compiled version are needed (RcppParallel) are then installed to that library first

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