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

Reproducible builds #2173

Closed
xofyarg opened this issue Mar 6, 2021 · 8 comments · Fixed by #2692
Closed

Reproducible builds #2173

xofyarg opened this issue Mar 6, 2021 · 8 comments · Fixed by #2692
Assignees
Labels
Milestone

Comments

@xofyarg
Copy link
Contributor

xofyarg commented Mar 6, 2021

Summary

At version 0.17, the compiler is able to generate the same output with the same input. But after upgrading to 1.0.2, the compilation results differ each time even the wasm file is the same. I tried LLVM and cranelift. I'm using the default options, not sure if I missed anything.

@xofyarg xofyarg added the ❓ question I've a question! label Mar 6, 2021
@syrusakbary
Copy link
Member

I believe this is due to the introduction of parallel compilation, at least on the LLVM side.
I've also read that latest version of cranelift may have issues with determinism (trying to search the source where I read it).

How important is deterministic compilation for your use case @xofyarg? Also, have you tried singlepass?

@xofyarg
Copy link
Contributor Author

xofyarg commented Mar 6, 2021

I just tried singlepass, same issue.

My use case is to pre-compile the binary in one location, and distribute it worldwide. The build service is stateless, so it doesn't know if the source is changed or not. The new version introduced some unnecessary pressure on the pipeline, but it acceptable at the moment.

@syrusakbary
Copy link
Member

Thanks for the info!
Could it be possible to have a PR opened in this repo testing the deterministic behavior so we can work in a fix?

xofyarg pushed a commit to xofyarg/wasmer that referenced this issue Mar 12, 2021
This is to test wasmerio#2173, the empty test always pass while the table one
fails sometimes.
@xofyarg
Copy link
Contributor Author

xofyarg commented Mar 12, 2021

I add some tests to show the issue in PR #2184.

@nlewycky
Copy link
Contributor

nlewycky commented Mar 17, 2021

I've identified two problems. The easier one is that ModuleInfo::function_names is a HashMap. Maybe this is because it tends to be sparse, as not all functions have names? The iteration order of a hashmap is indeterminate so bincode's output for serializing one is indeterminate. We could either serialize this one differently (for example, in sorted order) or use a different type for it (for example, a BTreeMap).

The other issue is inside compiler-cranelift/src/compiler.rs, in a parallel loop over all function_body_inputs we have dwarf_frametable.lock().expect("").add_fde() which essentially appends bytes to the end of dwarf_frametable. This causes the FDEs in the frametable to be in an order that depends on which parallel compilation happens to finish first. We should defer this until after the parallel part and do them in function order (or, use a semaphore to block until earlier functions are done, that way we can still have compilation of later functions run in parallel with generation of unwind info for this function).

I haven't looked into compilers or engines besides cranelift+jit.

xofyarg pushed a commit to xofyarg/wasmer that referenced this issue Mar 18, 2021
This is to test wasmerio#2173, the empty test always pass while the table one
fails sometimes.
@syrusakbary
Copy link
Member

I've investigated a bit more the issue. If you are using the JIT engine, the issue comes from Cranelift. It emits non-deterministic results when compiling functions.

Here are the results for Engines:

  • JIT: deterministic
  • Native: not deterministic

Compilers:

  • Singlepass: deterministic
  • Cranelift: non-deterministic
  • LLVM: deterministic

@syrusakbary syrusakbary added the priority-high High priority issue label Oct 20, 2021
@syrusakbary
Copy link
Member

Note: what was not deterministic was the serialization due to use of HashMaps

@wchaudry wchaudry added this to the Wasmer Runtime 2.x milestone Oct 21, 2021
@syrusakbary
Copy link
Member

This PR is testing that we have deterministic builds:
#2184

Amanieu pushed a commit that referenced this issue Nov 22, 2021
This is to test #2173, the empty test always pass while the table one
fails sometimes.
bors bot added a commit that referenced this issue Nov 22, 2021
2692: Make module serialization deterministic r=syrusakbary a=Amanieu

Fixes #2173

Includes the tests from #2184.

Co-authored-by: Amanieu d'Antras <amanieu@gmail.com>
Co-authored-by: Anbang Wen <anbang@cloudflare.com>
@bors bors bot closed this as completed in 277ab29 Nov 23, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants