-
Notifications
You must be signed in to change notification settings - Fork 14.9k
[CodeGen] Ensure clearRegisterKills clears inside bundles. #149177
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
@llvm/pr-subscribers-backend-aarch64 Author: Ricardo Jesus (rj-jesus) ChangesWhen clearing register kills, ensure we do so inside a bundle too. This came up in AArch64LdStOpt where we attempt to clear kill flags that affect the first store's register. If the kill happens in a bundle, we were only clearing it in the bundle operands, but not in the constituent instructions. This was leading to a verification error, for example: https://godbolt.org/z/WjhEWGcPW. Fixes #149092. Full diff: https://github.com/llvm/llvm-project/pull/149177.diff 2 Files Affected:
diff --git a/llvm/lib/CodeGen/MachineInstr.cpp b/llvm/lib/CodeGen/MachineInstr.cpp
index da3665b3b6a0b..a966add78a97e 100644
--- a/llvm/lib/CodeGen/MachineInstr.cpp
+++ b/llvm/lib/CodeGen/MachineInstr.cpp
@@ -2177,12 +2177,12 @@ void MachineInstr::clearRegisterKills(Register Reg,
const TargetRegisterInfo *RegInfo) {
if (!Reg.isPhysical())
RegInfo = nullptr;
- for (MachineOperand &MO : operands()) {
- if (!MO.isReg() || !MO.isUse() || !MO.isKill())
+ for (MIBundleOperands O(*this); O.isValid(); ++O) {
+ if (!O->isReg() || !O->isUse() || !O->isKill())
continue;
- Register OpReg = MO.getReg();
+ Register OpReg = O->getReg();
if ((RegInfo && RegInfo->regsOverlap(Reg, OpReg)) || Reg == OpReg)
- MO.setIsKill(false);
+ O->setIsKill(false);
}
}
diff --git a/llvm/test/CodeGen/AArch64/sve-vls-ldst-opt.mir b/llvm/test/CodeGen/AArch64/sve-vls-ldst-opt.mir
index 49453bc178914..28c5220642ec5 100644
--- a/llvm/test/CodeGen/AArch64/sve-vls-ldst-opt.mir
+++ b/llvm/test/CodeGen/AArch64/sve-vls-ldst-opt.mir
@@ -72,3 +72,23 @@ body: |
# CHECK: STURQi killed renamable $q1, renamable $x1, 16 :: (store (s128))
# CHECK: STURQi killed renamable $q2, renamable $x1, 48 :: (store (s128))
# CHECK: STR_ZXI killed renamable $z3, renamable $x1, 4 :: (store (<vscale x 1 x s128>))
+---
+name: clear-kill-in-bundle
+tracksRegLiveness: true
+body: |
+ bb.0:
+ liveins: $x0, $z0
+
+ STR_ZXI $z0, $x0, 0 :: (store (<vscale x 1 x s128>))
+ BUNDLE implicit-def $z1, implicit-def $q1, implicit killed $z0 {
+ $z1 = ADD_ZZZ_D $z0, killed $z0
+ }
+ STR_ZXI renamable $z1, $x0, 1 :: (store (<vscale x 1 x s128>))
+
+ RET_ReallyLR
+...
+# CHECK-LABEL: name: clear-kill-in-bundle
+# CHECK: BUNDLE implicit-def $z1, implicit-def $q1, implicit $z0 {
+# CHECK: $z1 = ADD_ZZZ_D $z0, $z0
+# CHECK: }
+# CHECK: STPQi $q0, $q1, $x0, 0 :: (store (<vscale x 1 x s128>))
|
|
Wonder if the other functions should also operate on bundles or if just this one needs to |
Ah I see this was only tangentially related to #134068 - it just made it mode likely to trigger. I fixed #146415 recently for a similar issue, but there it used a different iterator type to make sure the instruction in the bundle were also visited, not skipped over. Perhaps @arsenm knows if this sounds OK for the generic MachineInstr::clearRegisterKills? It seems sensible to me but we could just try and make sure the the AArch64LoadStore optimizer visited the instruction in the bundle too. |
Yeah, I think so, due to the MOVPRFX bundles. Would you rather I move this over to AArch64LoadStoreOpt (perhaps with a different iterator for the instructions)? It looks like it might be helpful in |
I'm not sure, bundle iterators are already used elsewhere (for example in LiveRegUnits::accumulateUsedDefed), but it might be that there are a few places where they are missed or where different strategies are used (for example as @davemgreen suggested). |
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.
For the various MachineInstr property checks, there is the QueryType for how to handle the bundle, which all default to AnyInBundle. The more sophisticated checks seem to have never been updated to do anything with bundle. I guess doing this in all of these type functions is the most internally consistent way to do it
Hi @arsenm, when you write:
By this, are you referring to updating "the more sophisticated checks (that) seem to have never been updated to do anything with bundle", or are you referring to adding something like AnyInBundle to clearRegisterKills, and potentially other such functions, to control how they handle a bundle? (I'm sorry if this is very obvious, but I just got confused interpreting the path you're suggesting.) |
Yes, something like that. All of these register based functions, e.g. substituteRegister, clearRegisterKills, addRegisterDead and so on. They possibly should have a bundle query, like the predicate functions. |
Thanks very much, that sounds good to me. In that case, if you think this is a reasonable approach, I'll update this PR as you're suggesting for clearRegisterKills so that we can progress the linked issue, and create a separate PR to extend this to the remaining functions. Does this sound okay to you? |
Yes |
IgnoreBundle, // Ignore bundles | ||
AnyInBundle, // Return true if any instruction in bundle has property | ||
AllInBundle // Return true if all instructions in bundle have property | ||
IgnoreBundle, // Ignore bundles |
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.
These formatting changes were made to avoid code formatter errors.
@@ -1700,7 +1700,8 @@ class MachineInstr | |||
/// Clear all kill flags affecting Reg. If RegInfo is provided, this includes | |||
/// all aliasing registers. | |||
LLVM_ABI void clearRegisterKills(Register Reg, | |||
const TargetRegisterInfo *RegInfo); | |||
const TargetRegisterInfo *RegInfo, | |||
QueryType Type = AllInBundle); |
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.
@arsenm For now I've reused the pre-existing QueryType. If you think it would be better to define a separate type for updates, please let me know.
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.
Should the default be IgnoreBundle to match the previous behaviour? (And maybe that would help for compile-time, I'm not sure how much it makes a difference)
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.
If we go that route, would we then override the default in AArch64LoadStoreOptimizer
?
I'm happy with that as it solves the direct issue in #149092, but I would assume that not clearing kills in bundles could be an issue elsewhere.
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.
If the code calling clearRegisterKills already iterates over all instructions they might be fine already. It's hard to tell from the code which is which, but I think the Arm uses are probably OK (there are many more bundles used there, they are rarer on AArch64 before movprfx).
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.
Sounds good, thanks for the suggestion. I'll open a separate PR to iterate through all instructions in AArch64LoadStoreOptimizer instead.
Ping |
@@ -1700,7 +1700,8 @@ class MachineInstr | |||
/// Clear all kill flags affecting Reg. If RegInfo is provided, this includes | |||
/// all aliasing registers. | |||
LLVM_ABI void clearRegisterKills(Register Reg, | |||
const TargetRegisterInfo *RegInfo); | |||
const TargetRegisterInfo *RegInfo, | |||
QueryType Type = AllInBundle); |
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.
Should the default be IgnoreBundle to match the previous behaviour? (And maybe that would help for compile-time, I'm not sure how much it makes a difference)
As an alternative to #149177, iterate through all instructions in `AArch64LoadStoreOptimizer`.
I'm closing this PR since the original motivating issue was instead fixed via #152994. |
When clearing register kills, ensure we do so inside a bundle too. This came up in AArch64LdStOpt when we attempt to clear kill flags that affect the first store's register. If the kill happens in a bundle, we were only clearing it in the bundle operands, but not in the constituent instructions. This was leading to a verification error, for example: https://godbolt.org/z/WjhEWGcPW.
Fixes #149092.