cranelift: Use cranelift-jit in runtests#4453
cranelift: Use cranelift-jit in runtests#4453jameysharp merged 2 commits intobytecodealliance:mainfrom
cranelift-jit in runtests#4453Conversation
Subscribe to Label Actioncc @fitzgen DetailsThis issue or pull request has been labeled: "cranelift", "fuzzing"Thus the following users have been cc'd because of the following labels:
To subscribe or unsubscribe from this label, edit the |
c15a323 to
67518ee
Compare
3e13a39 to
3e158aa
Compare
cfallin
left a comment
There was a problem hiding this comment.
Thanks for this! The change overall looks reasonable. I'm not sure about the Windows runtime issue; tagged @peterhuene for more thoughts.
23824ab to
32f167a
Compare
32f167a to
497be14
Compare
Using `cranelift-jit` in run tests allows us to preform relocations and libcalls. This is important since some instruction lowerings fallback to libcall's when an extension is missing, or when it's too complicated to implement manually. This is also a first step to being able to test `call`'s between functions in the runtest suite. It should also make it easier to eventually test TLS relocations, symbol resolution and ABI's. Another benefit of this is that we also get to test the JIT more, since it now runs the runtests, and gets some fuzzing via `fuzzgen` (which uses the `SingleFunctionCompiler`). This change causes regressions in terms of runtime for the filetests. I haven't done any serious benchmarking but what I've been seeing is that it now takes about ~3 seconds to run the testsuite while it previously took around 2 seconds.
497be14 to
cfb1b80
Compare
|
With the CRT issue resolved I think this is ready to merge. The only changes since the last review were a rebase and enabling the FMA tests that we couldn't enable in #4460. Thanks for reviewing! |
jameysharp
left a comment
There was a problem hiding this comment.
Overall I think this is great. I'd just like to take a little more time on ownership of TargetIsa and reuse of SingleFunctionCompiler. I'm open to merging it as-is if you feel that it's better this way, @afonso360, but it seems to me that it's useful to be able to reuse these.
| /// - compile a `Trampoline` for the [Function]'s signature (or used a cached `Trampoline`; | ||
| /// this makes it possible to call functions when the signature is not known until runtime. | ||
| pub fn compile(&mut self, function: Function) -> Result<CompiledFunction, CompilationError> { | ||
| pub fn compile(self, function: Function) -> Result<CompiledFunction, CompilationError> { |
There was a problem hiding this comment.
Changing SingleFunctionCompiler::compile from taking &mut self to just self feels unfortunate, since it forces callers to build a new TargetIsa for every function they compile. What would you think of changing JITBuilder to take a shared reference instead? Either &dyn TargetIsa or Rc<dyn TargetIsa>, whichever is easier. In the latter case, SingleFunctionCompiler would also take an Rc instead of a Box.
There was a problem hiding this comment.
I remember (sorry, this was a while ago) trying to change to &dyn TargetIsa, but it brought a bunch of changes due to lifetimes, and I gave up on that path.
I didn't try Rc<> but see the comment down below.
|
|
||
| let mut fn_builder_ctx = FunctionBuilderContext::new(); | ||
| let mut func = Function::with_name_signature(ExternalName::user(0, 0), sig.clone()); | ||
| let mut func = Function::with_name_signature(ExternalName::user(0, 1), sig.clone()); |
There was a problem hiding this comment.
What is this change for?
There was a problem hiding this comment.
The name user(0, 0) was the trampoline IIRC and the JIT was complaining about it. I'm not too sure, but I can go check.
| // We can't use the requested ISA directly since it does not contain info | ||
| // about the operating system / calling convention / etc.. | ||
| // | ||
| // Copy the requested ISA flags into the host ISA and use that. | ||
| let isa = build_host_isa(false, context.flags.clone(), requested_isa.isa_flags()); | ||
|
|
||
| let compiled_fn = | ||
| SingleFunctionCompiler::new(isa).compile(func.clone().into_owned())?; |
There was a problem hiding this comment.
I'd prefer not to move this code into the loop, if my earlier suggestion on shared references to the TargetIsa works out.
| let isa = create_target_isa(&test_file.isa_spec)?; | ||
| let compiled_fn = SingleFunctionCompiler::new(isa).compile(func.clone())?; |
There was a problem hiding this comment.
I'd prefer not to move this code into the loop, if my earlier suggestion on shared references to the TargetIsa works out.
| // Build and declare the trampoline in the module | ||
| let trampoline_name = format!("{}", trampoline.name); | ||
| let trampoline_id = | ||
| module.declare_function(&trampoline_name, Linkage::Local, &trampoline.signature)?; |
There was a problem hiding this comment.
If it works out to keep the SingleFunctionCompiler reusable, do you think it'd be worth keeping the cache of trampolines around too?
|
@jameysharp I agree with your comments, however this is sort of an intermediate PR with the follow up cleaning pretty much all of it. See #4667, we do away with SingleFunctionCompiler and link the entire test file at once, so the repeated compilation issue goes away. I'd prefer not to have to clean this up, since it's going away as soon as that is done. But let me know if this is a blocker. |
|
I hadn't looked at 4667 yet, and that resolves my concerns. Thanks! |
|
As of this commit, I'm now seeing test failures on s390x (Ubuntu 20.04), specifically when running when running the sample code in Note that this attempts to use Instead, you should use separate |
|
👋 Hey,
Edit: Ignore that, I misread your comment and thought that only the doc tests were failing.
If so, this might be related to: #4986 |
|
No, only the doc tests are indeed failing. I've been debugging this right now, and the difference is that doc test sample program is single-threaded and uses the main heap, where How would I use the |
That's interesting, and weird!
This should do it. However I think that we probably should switch to a mmap allocator since that apparently can solve a bunch of other issues with cranelift-jit. |
The sample program in cranelift/filetests/src/function_runner.rs would abort with an mprotect failure under certain circumstances, see bytecodealliance#4453 (comment) Root cause was that enabling PROT_EXEC on the main process heap may be prohibited, depending on Linux distro and version. This only shows up in the doc test sample program because the main clif-util is multi-threaded and therefore allocations will happen on glibc's per-thread heap, which is allocated via mmap, and not the main process heap. Work around the problem by enabling the "selinux-fix" feature of the cranelift-jit crate dependency in the filetests. Note that this didn't compile out of the box, so a separate fix is also required and provided as part of this PR. Going forward, it would be preferable to always use mmap to allocate the backing memory for JITted code.
The sample program in cranelift/filetests/src/function_runner.rs would abort with an mprotect failure under certain circumstances, see bytecodealliance#4453 (comment) Root cause was that enabling PROT_EXEC on the main process heap may be prohibited, depending on Linux distro and version. This only shows up in the doc test sample program because the main clif-util is multi-threaded and therefore allocations will happen on glibc's per-thread heap, which is allocated via mmap, and not the main process heap. Work around the problem by enabling the "selinux-fix" feature of the cranelift-jit crate dependency in the filetests. Note that this didn't compile out of the box, so a separate fix is also required and provided as part of this PR. Going forward, it would be preferable to always use mmap to allocate the backing memory for JITted code.
The sample program in cranelift/filetests/src/function_runner.rs would abort with an mprotect failure under certain circumstances, see #4453 (comment) Root cause was that enabling PROT_EXEC on the main process heap may be prohibited, depending on Linux distro and version. This only shows up in the doc test sample program because the main clif-util is multi-threaded and therefore allocations will happen on glibc's per-thread heap, which is allocated via mmap, and not the main process heap. Work around the problem by enabling the "selinux-fix" feature of the cranelift-jit crate dependency in the filetests. Note that this didn't compile out of the box, so a separate fix is also required and provided as part of this PR. Going forward, it would be preferable to always use mmap to allocate the backing memory for JITted code.
👋 Hey,
This PR changes our run tests to run via
cranelift-jit. Its based on top of #4450 so its probably not worth doing any serious review until that is merged.Using
cranelift-jitin run tests allows us to preform relocations andlibcalls. This is important since some instruction lowerings fallback
to libcall's when an extension is missing, or when it's too complicated
to implement manually.
This is also a first step to being able to test
call's between functionsin the runtest suite. It should also make it easier to eventually test
TLS relocations, symbol resolution and ABI's.
Another benefit of this is that we also get to test the JIT more, since
it now runs the runtests, and gets some fuzzing via
fuzzgen(whichuses the
SingleFunctionCompiler).This change causes regressions in terms of runtime for the filetests.
I haven't done any serious benchmarking but what I've been seeing is
that it now takes about ~3 seconds to run the testsuite while it
previously took around 2 seconds.
Edit:
This also closes #3030 since we can now do relocations in runtests