-
Notifications
You must be signed in to change notification settings - Fork 89
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
reg,pass: refactor allocation of aliased registers #121
Conversation
This is super awesome. All my code "compiles" now and the power feels... overwhelming. Still have no idea if my code works, but at least now I can generate it and start debugging 👍 |
Codecov Report
@@ Coverage Diff @@
## master #121 +/- ##
==========================================
+ Coverage 77.6% 77.62% +0.01%
==========================================
Files 55 55
Lines 33326 33376 +50
==========================================
+ Hits 25864 25909 +45
Misses 7354 7354
- Partials 108 113 +5
Continue to review full report at Codecov.
|
15142b7
to
de139fc
Compare
It seems like something is still a bit weird. I have two pieces of code that after some changes that shouldn't cause allocation changes are now causing issues. This is probably the easiest to understand. In this case it simply skips over the piece of code that are inlined below. Adding the branch should not make any difference on allocation, but it seems to do so. This skips a whole bunch of code if taken, but none of the registers allocated in that piece of code (rep, left, right) is referenced in where it is jumping to. The registers at the destination should already be carried over because the rest of the code can end up there anyway. It can easily be a problem in my code, which is why it would be great to have more info than "failed to allocate registers". |
FWIW, this commit is where it 'broke'. No other changes than size-related AFAICT. I am looking at changing parts of it so I can see where it breaks. Edit: ok, made a "mimimal" reproducer: These changes breaks the allocator. Before this change things are fine, but when the changes are applied it runs out of registers. There is nothing in the change that by my understanding should change register allocation since we are just using 'As32()' to get the 32 bit equivalent. |
@klauspost Sorry there's another problem, and thanks for your effort debugging this! There's something I've been aware of but didn't think was going to be an immediate problem, specifically the behavior of 32-bit operands in 64-bit mode. From the Intel Manual:
This means that 32-bit instructions will actually cause a write to the full 64-bit register. However the current implementation in The fact that your problem manifests when you change sizes from 64-bit to 32-bit makes me think the above issue might be to blame. However this is just a guess at this point, I'll have to dig into the details (I'll have time later today). |
Not completely sure I understand what it does. Kinda just assumed a Anyway, if you don't crack it, a workaround would be great. I can at least run parts of the code now, and I got a reasonable setup with delve, so I can actually debug it somewhat, so making progress. |
Okay, using my experimental debug printer (https://github.com/mmcloughlin/avo/compare/regalloc-debug) we see
Note the |
ok, I managed to work around it by not converting registers as much. |
Good to hear. I'm going to try to work on a proper fix for this.
Just to clarify what the problem is, consider the following: ...
x := GP64()
MOVW(U16(42), x.As16())
ADDQ(x, ...)
... The move instruction only writes the low 16 bits, and the add consume the full 64 bits. Therefore ...
x := GP64()
MOVL(U32(42), x.As32())
ADDQ(x, ...)
... This should not exhibit the same problem as the 16 bit version, because in x86-64 the 32-bit write caused by Ultimately this means |
Test to ensure that `avo` correctly accounts for zero-extension of 32-bit operands to 64 bits. Updates #121
Test to ensure that `avo` correctly accounts for zero-extension of 32-bit operands to 64 bits. Updates #121
@klauspost I have added an additional pass that handles the 32-bit zero extension issue I described. With this change the code you referenced in #121 (comment) doesn't fail allocation. Please let me know how you get on with this new version. I actually think your change in klauspost/compress@214d2e2 is probably preferable anyway, not just as a workaround for this problem. Once again, thanks for working through this with me 😄 |
Requires all virtual registers to specify a mask rather than just a size. Currently avo allows specifying an 8-bit register, which could resolve to a low or high byte register. This complicates liveness analysis and register allocation for what ends up being a very niche use case. Removes the `reg.Width` type since this is no longer used anywhere. Updates #100
Provides register IDs. These will be used to help unify handling of physical/virtual registers in liveness and allocation phases. Updates #100
For liveness and allocation we actually want to know which parts of each register are live at each time. The `reg.Set` structure was not correct. This diff replaces it with `reg.MaskSet`, mapping a register ID to the active mask. Updates #100
Refactors the register allocator to be based on IDs, with masks applied later. This is just the result of hacking and slashing; it builds but has not been tested at all. Updates #100
Refactored allocator works on the ./examples/... generators. Updates #100
Small example that triggers the aliasing problem. The refactored allocator does not have the same problem. Updates #100
Create a synthentic test case to verify correct handling of masking in liveness analysis and the allocator. Updates #100
a27b743
to
d8b7ea5
Compare
Issue #100 demonstrated that register allocation for aliased registers is
fundamentally broken. The root of the issue is that currently accesses to the
same virtual register with different masks are treated as different registers.
This PR takes a different approach: