-
Notifications
You must be signed in to change notification settings - Fork 1.4k
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
x64: Refactor assembler ISLE constructors #10276
base: main
Are you sure you want to change the base?
x64: Refactor assembler ISLE constructors #10276
Conversation
This commit is spawned out of discussion between me and Andrew in conjunction with the thoughts in bytecodealliance#10238. The goal here is to pave a way forward for various kinds of instructions in the future as well as give access to more instructions today we already have formats for. The major changes in this commit are: * `Assembler*` types are gone from ISLE, except for immediates. Instead types like `Gpr` and `GprMem` are used instead. * Rust-defined constructors for each instruction return `MInst` instead of implicitly performing an `emit` operation. * Instructions with a read/write `GprMem` operand now generate two ISLE constructors instead of one. One constructor takes `Gpr` and returns `Gpr`, the other takes `Amode` and returns `SideEffectNoResult`. * Generated ISLE constructors now match the SSA-like form of VCode/ISLE we already have. For example `AssemblerReadWriteGpr` is never used as a result, it's just `Gpr` instead. Conversions happen in Rust during construction of assembler instructions. Using this new support various `x64_*_mem` instructions have now moved over to the new assembler and using that instead. Looking to the future this is intended to make it easier to generate constructors that return `ProducesFlags` or `ConsumesFlags` such as `x64_adc` and friends. This will require more refactoring to enable this but the goal is to move roughly in such a direction. I've attempted to make this abstract enough that it'll be relatively easily extensible in the future to more ISLE constructors with minimal changes, so some abstractions here may not be fully used just yet but the hope is that they will be in the near future.
I'll also note that this creates a lot of conflicts with #10273, and I'm happy to have that go through first and rebase around that, or also have this go in first and I can help that PR rebase around this. Either way works for me |
Subscribe to Label Action
This issue or pull request has been labeled: "cranelift", "cranelift:area:x64", "isle"
Thus the following users have been cc'd because of the following labels:
To subscribe or unsubscribe from this label, edit the |
An example of extending this is alexcrichton@ea299c4 where I've updated the |
Adding myself as a reviewer here -- want to take a look, excited to see progress on untangling some of the layers of abstraction. |
Some before/after of ISLE constructors for this PR are: before
after
|
Ok, I'll take a look at this today. After we talked, I took the approach a few steps further so I'll try to reconcile that with this. |
Ok, as a point of comparison, here is where I ended up for the same instructions above:
What's going on in this version is that (1) the What is
I was thinking that this core enum can be matched on or auto-converted ( [edit: I know that up above we don't want some of those |
Another thought: at some point soon we could move all the code that generates ISLE out of |
Two thoughts to add:
Basically I'm hoping for simplicity via reduction in layers of abstraction as far as possible without losing type safety; that was the original state of play, the flags abstraction added a bit but it was seen as necessary, now the separate assembler layer whose types are being kept separate (rather than propagated through Cranelift) is ratcheting the boilerplate up past a critical threshold IMHO. Curious to hear your thoughts. |
I think you and I basically converged on the same idea. In this PR the Also I can confirm that with something like
💯 from me on this, agreed that should be the end state.
To me this is what it comes down to, the question of "what is the lowest level thing?" As-is today it's not possible to create In the end my goal here was to have the "raw" constructor be as general-purpose as possible, aka the "narrow waist" that all ISLE abstractions would be built on. It's more-or-less @abrown's Overall I feel like we're all basically in agreement about how to design this -- a "raw" thing with fancy ISLE abstractions on top -- and are more-or-less trying to figure out what to paint the bikeshed. What I might concretely propose is to hand-write this in ISLE:
The FFI layer in Rust would always return an To me this strikes a nice balance of:
I think it would also make sense to define all the helper functions that converts That's a lot of words, but WDYT? |
Not quite; I'm imaging that the external Rust constructor for (e.g.) a variant of Some of this goes back to early ISLE design philosophy, but: my intent with the initial design was to hew as close to a "value semantics" view of the program operators and instructions as possible, because this is what enables reasoning about expression equivalence, makes verification possible, etc. From that point of view, it makes sense that some instruction that is a binary operator (i) takes two registers and (ii) returns a register, just like its CLIF cousins. The bit of lowering logic that bridges the gap is hiding the emit in the constructor and returning the value. The magic of data dependencies means that it is physically impossible to call the ctors in an order that is not a valid toposort of operations, so emitting-when-constructed is always valid. We started carrying This is part of the reason I'm not so sure that "return the So if possible I'd prefer to bias us back toward implicit emission everywhere, and I do think it's possible here, as described above -- the fact that the instruction produces flags is fundamental, so that one is the one that we define; and an implicit converter can throw away the flags if we don't care. Thoughts? |
This commit is spawned out of discussion between me and Andrew in conjunction with the thoughts in #10238. The goal here is to pave a way forward for various kinds of instructions in the future as well as give access to more instructions today we already have formats for.
The major changes in this commit are:
Assembler*
types are gone from ISLE, except for immediates. Instead types likeGpr
andGprMem
are used instead.MInst
instead of implicitly performing anemit
operation.GprMem
operand now generate two ISLE constructors instead of one. One constructor takesGpr
and returnsGpr
, the other takesAmode
and returnsSideEffectNoResult
.AssemblerReadWriteGpr
is never used as a result, it's justGpr
instead. Conversions happen in Rust during construction of assembler instructions.Using this new support various
x64_*_mem
instructions have now moved over to the new assembler and using that instead. Looking to the future this is intended to make it easier to generate constructors that returnProducesFlags
orConsumesFlags
such asx64_adc
and friends. This will require more refactoring to enable this but the goal is to move roughly in such a direction.I've attempted to make this abstract enough that it'll be relatively easily extensible in the future to more ISLE constructors with minimal changes, so some abstractions here may not be fully used just yet but the hope is that they will be in the near future.