-
Notifications
You must be signed in to change notification settings - Fork 246
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 a JIT based on Cranelift #86
Conversation
Thanks and apologies for the delay, I'll try to find some time to look into this soon 🙏 |
Personal business has kept me mostly away from GitHub last week... I'll do my best this week, apologies again |
So this is a lot of work, thanks for this PR! To be honest with you, I'm not really sure what to do of it. We already have a JIT-compiler in rbpf, and I'm not looking forwards to maintain a separate implementation. This being said, support for the various architectures and platforms you mentioned is definitely something interesting. I'm thinking of accepting the PR, especially if you use it on your side, but I'd have the feature opt-in rather than opt-out (my understanding is that I've got a few questions:
Some observations:
|
Hey, Thanks for reviewing this!
That is completely reasonable, and mostly the reason I posted such an early draft (with build warnings!). This really is a weekend project, and I used it to learn a bit more about how eBPF works. I don't currently use it anywhere or have any real plans for using it.
Yeah, that makes sense.
Not really, it really was just an experiment into learning how eBPF works.
I don't think we are at feature parity. The test file for cranelift is pretty much a copy from the existing JIT's tests and we pass all of those, however the cranelift JIT doesn't yet implement the From reading the existing JIT I think the only extra feature we implement is that we do bounds checks on all loads and stores. Additionally we use some of the existing cranelift features such as constant propagation and some other optimizations. This mostly means that some of the test cases end up being optimized down to a couple of instructions. (As an aside this ended up revealing a missing optimization in cranelift!)
Natively I've only tested on x86_64 Linux. I've also ran the testsuite for AArch64 and RISC-V under QEMU. RISC-V failed due to an instruction not yet being implemented on cranelift, but that has since been fixed in the latest version and the testsuite passes with that version. I haven't tested s390x, or any other OS. That being said, they should be supported.
Those make sense! This really is a sort of early draft which I was planning on fixing up if you decide you'd like this upstreamed. Let me know! And I completely understand that this might be too much of a maintenance burden! Especially for something that already pretty much exists. |
Thanks for the details! Nice to have validation on several architectures already 💪. OK let's go for an opt-in feature then. I've also thought of keeping it in a separate branch, but the project is not active enough and the feature would likely end up dying there. At least in Would be nice to get the load/stores with the One other question, I remember seeing that you build the CFG for the program at some point in the code. What is it for?
Pretty neat! |
👍 Sounds good, I'll clean it up a bit and add those missing features.
Cranelift's IR builder does not allow us to go back and edit a block after the branch / terminator instruction has been inserted (Called a "filled block" in cranelift's terminology). So what happens is that when we have a backwards branch into the middle of a previous block, we no longer can go back, split that previous block in two and emit a jump into it. So I've built a two pass solution, first we scan and mark all offsets where we either land from another instruction, or we jump into another instruction, and emit the IR blocks according to that. When translating the instructions into IR, we switch blocks according to that previous scan. |
185b5e0
to
7074ec9
Compare
I've cleaned up this PR a little bit. I've also made the following changes:
I'd appreciate a review especially on the mbuf handling part, which I'm not entirely sure is correct. We store either the mbuf or the mem pointer in Edit: There are also quite a few WIP commits on this branch, should I squash this into a single commit? |
Apologies for the delay, again... This time I must admit that I completely forgot to come back to this during the week 😬 but I've started, now.
Not necessarily a single one, ideally logical commits (New JIT addition, adding the feature, enabling in CI, ...). CI is happy, this is great! |
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.
Looks good on high-level code organisation. I'd like to take a closer look, at least for the changes in src/lib.rs, but it's already late here so this will take a few more days.
|
||
|
||
#[test] | ||
fn test_cranelift_ldabsb() { |
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 see that this file contains all the same tests as in tests/ubpf_jit_x86_64.rs
, which is great! It also has the following additional tests:
ldabsb
ldabsdw
ldabsh
ldabsw
ldindb
ldinddw
ldindh
ldindw
... for which the existing JIT has equivalent tests under test/misc.rs
. The reason why they're not in the same file is that tests/ubpf_jit_x86_64.rs
only contains those tests that were copied from uBPF, and ldabs*
/ldind
were not supported there, at least at the time. It would probably make sense to move related tests together though.
This comment is mostly for myself. Looks good, and OK to have all your tests in tests/cranelift.rs 👍.
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.
👍 It'd be nice to eventually have a unified test suite that would run in cranelift, the JIT and the interpreter!
"${{ env.CONFORMANCE_IMAGE }}" \ | ||
bin/bpf_conformance_runner --test_file_directory tests \ | ||
--plugin_path /rbpf/target/release/examples/rbpf_plugin \ | ||
--exclude_regex "lock" \ |
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.
Isn't that possible to reuse "${{ env.KNOWN_FAILURES }}"
here? Or is the mem-len
test passing?
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.
mem-len
is passing, so that's why I didn't reuse it. Getting lock
to work might also not be too much work, but It might be best as a follow up, and then we could remove this entirely.
No worries! I'm not in a hurry with this, so take as long as you'd like.
Cool, I'll rework those commits into something slightly more reasonable. |
459d39c
to
1bb71ae
Compare
Run rustfmt on src/lib.rs to improve formatting. Suggested-by: Afonso Bordado <[email protected]> Signed-off-by: Quentin Monnet <[email protected]>
Signed-off-by: Afonso Bordado <[email protected]> Signed-off-by: Quentin Monnet <[email protected]>
Signed-off-by: Afonso Bordado <[email protected]> Signed-off-by: Quentin Monnet <[email protected]>
I'm taking you at your word 😅. This time I started to look more into it though. My main comment would be that we could clean up the commits a bit more - I started to comment on the PR, but I realised I was maybe being too picky and imposing too much work on you. So instead I cleaned it up on my side and created another branch (I didn't want to overwrite yours). So would you mind checking whether https://github.com/qmonnet/rbpf/tree/pr/cranelift-jit-reworked is OK with you, and if so, updating the current PR? Changes include:
I think that's about it. I checked that the tests are still passing at each commit, so hopefully I didn't break your PR too much while reorganising the contents between the different commits 🙂. I note there's a test which is commented out in |
Signed-off-by: Afonso Bordado <[email protected]> Signed-off-by: Quentin Monnet <[email protected]>
Signed-off-by: Afonso Bordado <[email protected]> Signed-off-by: Quentin Monnet <[email protected]>
Signed-off-by: Afonso Bordado <[email protected]> Signed-off-by: Quentin Monnet <[email protected]>
Signed-off-by: Afonso Bordado <[email protected]> Signed-off-by: Quentin Monnet <[email protected]>
Signed-off-by: Afonso Bordado <[email protected]> Signed-off-by: Quentin Monnet <[email protected]>
Signed-off-by: Afonso Bordado <[email protected]> Signed-off-by: Quentin Monnet <[email protected]>
Signed-off-by: Afonso Bordado <[email protected]> Signed-off-by: Quentin Monnet <[email protected]>
Signed-off-by: Afonso Bordado <[email protected]> Signed-off-by: Quentin Monnet <[email protected]>
Signed-off-by: Afonso Bordado <[email protected]> Signed-off-by: Quentin Monnet <[email protected]>
Signed-off-by: Afonso Bordado <[email protected]> Signed-off-by: Quentin Monnet <[email protected]>
This tests the Cranelift JIT in CI Signed-off-by: Afonso Bordado <[email protected]>
Signed-off-by: Afonso Bordado <[email protected]> Signed-off-by: Quentin Monnet <[email protected]>
Signed-off-by: Afonso Bordado <[email protected]>
Signed-off-by: Afonso Bordado <[email protected]>
Signed-off-by: Afonso Bordado <[email protected]> Signed-off-by: Quentin Monnet <[email protected]>
Signed-off-by: Afonso Bordado <[email protected]>
Some operations may not work in some older CPU's and may need to be lowered to a libcall. Signed-off-by: Afonso Bordado <[email protected]>
a85b1e4
to
37a6284
Compare
That's awesome! Thanks! Your branch looks great to me!
Not really, I just kinda forgot about it 🙃 . I've now updated commit 83146eb to include that test, and also some better error messages when we don't find the required helper. |
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.
OK, I've been through the changes one more time and I think we're good 🎉. I'm not familiar with Cranelift to provide constructive feedback on the new JIT implementation, but it's an opt-in feature, it's self-contained, the changes to src/lib.rs is limited to the new related API functions, and we have CI tests that pass nearly all the existing tests. We can always refine or improve as a follow-up if necessary. The small loss of CI coverage reported is acceptable.
Do you have any more changes to bring to the PR, or shall I merge it?
🎉
Not right now, I might send a follow up PR soon to update the cranelift version when the new one is out. But It's probably not worth waiting for a release. |
OK. No need to update for the sake of it though, no need to update if there's no benefit in it (perf improvement, relevant security fixes, etc.). We should also update the REAMDE.md at some point to mention the feature and document the API functions. I can look into it when I find the time, if you don't beat me to it. |
👋 Hey,
I've been playing around with translating eBPF and running it with Cranelift as a weekend project. Cranelift is a Rust based optimizing compiler that includes a JIT.
This is a prototype and it is still a bit rough around the edges, but maybe you'd be interested in having this upstream?
This can also help with #21 and #48 since Cranelift supports x86_64, AArch64, s390x and RISC-V, as well as Windows, Linux, MacOS and some BSD's.
(I accidentally ran rustfmt on src/lib.rs, but If you'd like me to polish this PR and upstream it, I'll revert that change)