-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Add ability to call CLIF functions with arbitrary arguments in filetests #1517
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
Conversation
|
I expect this to fail because there are no encodings for booleans in |
Subscribe to Label Actioncc @bnjbvr DetailsThis issue or pull request has been labeled: "cranelift"Thus the following users have been cc'd because of the following labels:
To subscribe or unsubscribe from this label, edit the |
430fa31 to
6bec6d9
Compare
|
@alexcrichton, here's the PR with the trampolines I was referring to. (You might notice they look pretty similar to the ones you created 😉). |
|
Nice! FWIW reading this over I think there's actually not a whole lot that we'd share between wasmtime and cranelift. Only a few small loops/etc would be shared, and otherwise it seems like we'd probably bend over backwards too much trying to cater to both use cases. In that sense I'd personally be ok if we just left this duplication in the two? |
e1faf45 to
6c2e2b5
Compare
|
Ok, I think this is ready: since the x86 backend does not have encodings for loading and storing booleans, I took @sunfishcode's suggestion and convert boolean values from ( |
ac158d3 to
ede2aa2
Compare
8450ad2 to
9aa94a9
Compare
bnjbvr
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! (and sorry about the long review times)
Looking good, nice jobs with the trampolines. A few suggestions below to harmonize the code a bit.
cranelift/reader/src/run_command.rs
Outdated
| /// Check if a CLIF comment is potentially parseable as a [RunCommand]. | ||
| pub fn is_potential_run_command(comment: &str) -> bool { | ||
| let trimmed = Self::trim_comment_chars(comment); | ||
| trimmed.starts_with("run") || trimmed.starts_with("print") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead, could this look if trimmed is exactly run or print? I don't see the value in the ambiguity. If you agree with this, then we could rename this function is_run_or_print_command (since otherwise, there's a "run_command" called "run" and another called "print", which is a bit confusing).
In addition to this, could this return the command it found (so an Option, with None equivalent to current's false), instead of letting the caller clean up comments again and analyze the value?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, this is a good direction. These weird trimmed_comment_chars and is_potential_run_command functions were trying to do some lookahead to see if we actually needed to really parse the comment. I moved that logic into parse_run_command (which is in the parser module, where this functionality should be) and made it return ParseResult<Option<RunCommand>> instead of ParseResult<RunCommand>. The signature is a bit more complex but I think it is headed more in the direction you are suggesting.
| /// - 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> { | ||
| if function.signature.call_conv != self.isa.default_call_conv() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could it also check that the host architecture is equal to the requested ISA's architecture?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Those checks should already exist at a higher level, e.g. test_run.rs, and I didn't want to limit this code too much because there are cases (x86 32-bit vs 64-bit) where multiple Cranelift ISAs could run on the same machine.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
32bit x86 doesn't work in 64bit processes.
| panic!("argument type mismatch"); | ||
| } | ||
| unsafe { | ||
| Trampoline::write_value_to(arg, slot); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we just do a write here, using a transmute? It avoids the indirection through Trampoline::write_value_to, which is a bit unsettling.
If we did the same for reads, then the need for the Trampoline data structure is much lower, and could probably be avoided. Or we could hide the writing and reading into a different new UnboxedValues struct type (that'd be a vec); then the slot_size could be an attribute of this struct, a const func returning the size of the values it's storing (and then we can remove a bunch of comments, and have more confidence in the size being sync'd with the storage type). What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like having the Trampoline structure around to know what we are talking about; otherwise we have an Mmap floating around that is implicitly a trampoline. But that could be a type alias, I guess. I think your UnboxedValues makes sense and I started down this road but I'm not too confident about the transmute stuff--I will ping you on Zulip to see what you think.
dc0bcb5 to
1f02a95
Compare
bnjbvr
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, thanks!
This resolves the work started in bytecodealliance/cranelift#1231 and bytecodealliance#1436. Cranelift filetests currently have the ability to run CLIF functions with a signature like `() -> b*` and check that the result is true under the `test run` directive. This PR adds the ability to call functions with arbitrary arguments and non-boolean returns and either print the result or check against a list of expected results: - `run` commands look like `; run: %add(2, 2) == 4` or `; run: %add(2, 2) != 5` and verify that the executed CLIF function returns the expected value - `print` commands look like `; print: %add(2, 2)` and print the result of the function to stdout To make this work, this PR compiles a single Cranelift `Function` into a `CompiledFunction` using a `SingleFunctionCompiler`. Because we will not know the signature of the function until runtime, we use a `Trampoline` to place the values in the appropriate location for the calling convention; this should look a lot like what @alexcrichton is doing with `VMTrampoline` in wasmtime (see https://github.com/bytecodealliance/wasmtime/blob/3b7cb6ee64469470fcdd68e185abca8eb2a1b20a/crates/api/src/func.rs#L510-L526, https://github.com/bytecodealliance/wasmtime/blob/3b7cb6ee64469470fcdd68e185abca8eb2a1b20a/crates/jit/src/compiler.rs#L260). To avoid re-compiling `Trampoline`s for the same function signatures, `Trampoline`s are cached in the `SingleFunctionCompiler`.
This resolves the work started in bytecodealliance/cranelift#1231 and #1436. Cranelift filetests currently have the ability to run CLIF functions with a signature like
() -> b*and check that the result is true under thetest rundirective. This PR adds the ability to call functions with arbitrary arguments and non-boolean returns and either print the result or check against a list of expected results:runcommands look like; run: %add(2, 2) == 4or; run: %add(2, 2) != 5and verify that the executed CLIF function returns the expected valueprintcommands look like; print: %add(2, 2)and print the result of the function to stdoutTo make this work, this PR compiles a single Cranelift
Functioninto aCompiledFunctionusing aSingleFunctionCompiler. Because we will not know the signature of the function until runtime, we use aTrampolineto place the values in the appropriate location for the calling convention; this should look a lot like what @alexcrichton is doing withVMTrampolinein wasmtime (seewasmtime/crates/api/src/func.rs
Lines 510 to 526 in 3b7cb6e
wasmtime/crates/jit/src/compiler.rs
Line 260 in 3b7cb6e
Trampolines for the same function signatures,Trampolines are cached in theSingleFunctionCompiler.