<table border="1" cellspacing="0" cellpadding="8">
<tr>
<th>Issue</th>
<td>
<a href=https://github.com/llvm/llvm-project/issues/234098>234098</a>
</td>
</tr>
<tr>
<th>Summary</th>
<td>
[CodeGenPrepare] `optimizeExtUses` makes a sign check read `poison` through `zext nneg`
</td>
</tr>
<tr>
<th>Labels</th>
<td>
new issue
</td>
</tr>
<tr>
<th>Assignees</th>
<td>
</td>
</tr>
<tr>
<th>Reporter</th>
<td>
dyslexicsteak
</td>
</tr>
</table>
<pre>
## Summary
`optimizeExtUses` in `CodeGenPrepare` can make a sign check on a value read the `poison` of that value's `zext nneg` instead of the value itself. On `x86_64-pc-windows-msvc` this becomes a miscompile, since `WinEHPrepare`'s cleanup then folds the check to true and lets negative values through. I ran into this and found it reported previously at rust-lang/rust#163734, but the IR is language- and target-independent and also shows up on i686 with narrower types.
## Reproduction
The reproducer below loads a 32-bit `%id`, checks that it isn't negative, and only then widens it with `zext nneg`, which is valid because the check guards the block.
```llvm
define i64 @f(ptr %p, i1 %c) {
entry:
%id = load i32, ptr %p, align 4
br i1 %c, label %test, label %exit
direct:
%z = zext nneg i32 %id to i64
ret i64 %z
test:
%nonneg = icmp sgt i32 %id, -1
br i1 %nonneg, label %direct, label %exit
exit:
ret i64 -1
}
```
Running it through `opt` like so:
```
opt -S -mtriple=x86_64-unknown-linux-gnu -passes='require<profile-summary>,function(codegenprepare)' f.ll
```
gives this:
```llvm
define i64 @f(ptr %p, i1 %c) {
entry:
%id = load i32, ptr %p, align 4
%z = zext nneg i32 %id to i64 ; <-- moved up, still nneg
br i1 %c, label %test, label %exit
direct:
ret i64 %z
test:
%0 = trunc i64 %z to i32
%nonneg = icmp sgt i32 %0, -1 ; <-- reads poison when %id < 0
br i1 %nonneg, label %direct, label %exit
exit:
ret i64 -1
}
```
On `x86_64-pc-windows-msvc`, this variant with an MSVC personality turns the `poison` into wrong code:
```llvm
define i64 @f(ptr %p, i1 %c) personality ptr @__CxxFrameHandler3 {
entry:
%id = load i32, ptr %p, align 4
br i1 %c, label %test, label %exit
direct:
%z = zext nneg i32 %id to i64
ret i64 %z
use:
br i1 %nonneg, label %direct, label %exit
test:
%nonneg = icmp sgt i32 %id, -1
br i1 %c, label %use, label %exit
exit:
ret i64 -1
}
declare i32 @__CxxFrameHandler3(...)
```
Running it through `opt` like so:
```
opt -S -mtriple=x86_64-pc-windows-msvc -passes='require<profile-summary>,function(codegenprepare,win-eh-prepare)' g.ll
```
gives this:
```llvm
define i64 @f(ptr %p, i1 %c) personality ptr @__CxxFrameHandler3 {
entry:
%id = load i32, ptr %p, align 4
%z = zext nneg i32 %id to i64
br i1 %c, label %test, label %exit
direct:
ret i64 %z
use:
br label %direct ; <-- the check was folded to true
test:
br i1 %c, label %use, label %exit
exit:
ret i64 -1
}
```
The check is gone, and `use` is just `br label %direct`, so `f` ends up returning 2147483648 instead of -1 for an `%id` of `0x80000000`.
This reproduces on 23.1.4 and on `main` at 12b3aded3fc0.
## Diagnosis
What happens:
- `optimizeExt` first moves the `zext nneg` up next to the load so the two can become one extending load, and keeps `nneg`. That's ok on its own, since nothing outside `direct` uses `%z` yet.
- `optimizeExtUses` then comes along and rewrites the other uses of `%id` to read `trunc %z`, including the sign check. When `%id` is negative, `%z` is `poison`, so the check reads `poison`.
`optimizeExtUses` goes wrong even without the move, when the `zext nneg` already sits in the load's block. I'd expect it to either drop `nneg` from `%z` when it rewrites the check or just leave the check reading `%id`.
The fold in the second example comes from `WinEHPrepare`, which runs `SimplifyInstructionsInBlock` from `cleanupPreparedFunclets` for functions with an MSVC personality, and InstSimplify uses `nneg` to fold the check to true. Without `codegenprepare` the check survives. `-disable-cleanups` only hides the fold, because while the check survives, it still reads the poisoned `trunc`.
These examples run correctly everywhere else, including the first one on `x86_64-pc-windows-msvc`, because while they get the same `poison`, no pass in the pipeline folds the check and triggers a miscompile, but any pass that trusts `nneg` could break them. It isn't limited to 64-bit either. On `i686-unknown-linux-gnu`, a `zext nneg i8` to `i32` is moved beside the load the same way, and the sign check reads it through `trunc`.
## Suggested fix
I found 2 prior fixes that dropped poison-generating flags in `CodeGenPrepare` in similar situations:
- #72103, which dropped `nneg` when type promotion moved a `zext`
- #90382, which dropped flags after hoisting in `optimizeBranch`
I think the simplest fix is to do the same here, and call `dropPoisonGeneratingFlags()` on the extension once `optimizeExtUses` has rewritten a use.
</pre>
<img width="1" height="1" alt="" src="http://email.email.llvm.org/o/eJzUWEuP2zjy_zTsS0GCTPnVBx8Sd3qmD3_8B0l2cwxoqSxxmiK1JOVHPv2iSMqPdieZnWQDrNFA21Kx3vWrKgrnZKMRV2z2ls0e7sTgW2NX9dEpPMjKeRTPdxtTH1eMl4yX8GHoOmGPrHhDf_PC9F528gu-O_h_OHRsXoDUwObF2tT4G-o_LPbCIj2vhIZOPCMIIKFQtVg9g9EgYCfUgGBR1OBbpOO9kc5oOma24FvhIw3jC0evv-DBg9bYRIGkZx0pMTGT3qHa5vD_QZvDcv55Ps36KttLXZu9yzq3q-iwb6WDDVamQwcCOukq0_VSIeNrcFJXQZ1PUr_7_WxLUKNSKPTQk0wNW6NqF8RHs7wBbwcEoWtQ6B1obISXu6QekVozNG0OT2CFBqnpBOlCJ7Zm0DVIDxZ7Yz3W0FvcSTM4dQThwQ7OZ0rohvFH-s54OZmXi3JKSm8GHxR5eg_SAVENosEsMPbCNugzqWvsUdeofXgslDPgWrN3MPQUEjlfzmEvfQtaWGv2aMEfe3R5CnxMhvfYW1MPlZdGxxcfWwpjfIoWNqjMHpQRNfm25NlGegj-m8k6_F9Hf7kYY-lBOs34wp_8RSSkotHqGF29lzVqR7RBwRfZQPT7VlYtGb8TStYUXTE4vIhOMwibwrVRpnrOT_kc_5Tadax4U-NWagQ5nwKbFlvGl723wPisJylyQl8rxu-BLd6y4g1qb4-sJE4AwURg5UMwH2TJ6czleaGoCqaBemPP7NagxAYV_fJIsb14gAfpo661tFj5C2lfgrCTK0hiUsIbMiHQWfTRHD77EvkEEZEL4zNtwlniJKuuB9f4MyPSJJtc6xsPXOmYNHtd7fAtKT0qE3iyxcNlBCL1-0FrqRuKdaoXiJhDlavkM4IzkduLs6b3kH2ArPNW9gpZ-ZAQYNDP2ux1pqQeDlmjB8h64Qi5ygfGFxb_NUiLrFz31mylwswlwCvfMb7eDjomO19WpsYGdZ8wgd8zvoBtrtStFY3chYKX7lbXH8-0_yzR_kKiwOnDyrfAynWWQWd2WMMQ2DkvlYrl9hNS95sZSU-LoK63g65OhEHTko8k38jaIibtazZRu3EQGw3sCVlGV66h-JVZ_p0WRTJCZ9gJK4VOsCc0_N-Hf66hR-uMFkr6I_jBanfbQUNv2VujG6Cs_fEkvJQZSKbF58_rw-HRig5_F7pWaMv_ZVAcHI5M_nYO_CCwXttMCv29VKOwVkpYjOJeixTjyzzPCcJ-AQC_yO-fBb_rvdQZttk1Hjf_dTz-RaXwV1P5p4PxdSW8yHx4_XOG2PPEtRcuzMhYj6PxLdj_SO5_H2U_nnSRDhqjT5MlmxckgoDSwZ-DCxPqrbEJiZ2h11siR12HedkiAS-VCZ9MF9NlOZ8uL5eSbAJbYwmxL0ZfesHmRXFYFvHD5kU-airdeYp2NI_zMp_k0zQI07lOyIDtwsOEb0pRY11uq-J6Pn-QotHGSReffqIRuxV9jzplfgbXKxxx3ErrfGj4p1ZytW0NPWj6HfYVjNnr4ne_N2HLi-sUGI2AB4-6Jt8Q4ejyZ8Q-7HGJaQ4fW-HDVmXCSii9A7PX5x1MG98SFzN4J-ug1SksMDh0ybdf6PcRff6KdeOCGraItPAp6oqkksW9lT7ZbHyLNrKNYTpFzZu4prJ5EUeSJDPAgq7UEGwlHucdN4dPYbq4YCPd1YJzobt0V807pdy5kOLUckmSf2sZbwy61PtxF5Yn35q0IFKM47qE-tVQC0XSjuAoHFKf4h0CFRcneGJ8UQMeesIDGdICZXBfbU1_EWPYWtNdWhrEhh33wvPpUsDGQlQodvjCePLvhSvzc3UTvoxqOqyMJr1E1ytM0R41uNnmx5XRDjr49oPseiW3xyftvI37rXvSb8niS0vSDUDiVD8OuqJdP5AYC2O7cl-d2MZ6IDmjzFM2j37zJlp2c7mQw6cUTdLluiHGNE_0brA76nk5EWa1dGKjMEvaB3XDdt3KOkWB5IWrhLQ571up8BWGIet92ghiZhJRTE08V8lVnByOYXHkcKiMpUJWR8pQe9y3aBFQRdS_rqmITYQr5vsz843yR2gwZr4THd6UmTZAs8iYQb3sUdEQ8PJqJ9yjWNk0aG9vjDaDB6GPkVO40_B2cP4qoJUZVA0bi-KZ2HY5PJ3vPZTspI9dcj4N9yWxnMabLDlfzm-32GSCuCphkMuUP3Su5Ale4i63wYCjJwg_uWUvTml5DWMpwNej4Ivwnm4JmwYdmbGVh_jmKV1qceitpOKQB0weIqDosU5pkzWo0QpPMd8q0bivXihKDU52UglLCDWIUGunxsbLBZ8U5bm4RzEXkYjId-wRems6QwySe06ejPND4HdflEt-yy8qKbYeLbRGuqB51HkE5LdW6Ko9jSJPNHzq5-TfUAmeHELR8QZqc44GFcMYjkooFRqfNf0fwVe_nVz1SEowvqTBN9RzYBG6ryOrTLrIfKVFtMIlDPaoQRD-UDTv6lVZ35f34g5Xk8U9n90vyunirl1tio1AxArryXQ52eISp2JScTEpluJezPidXPGCzycFnxSLcsF5vl2IarFcbBZFKSazSrBpgZ2QKqdpOze2uZPODbji5bS4X96FwcuFK2nONe4hvGWcs9nDnV3RoWwzNI5NCyWdd2c2XnoV7rJfJMvs4Su2d-I5XPu-yPLr_fki3S_b491g1ar1vg85xx8Zf2ykb4dNXpmO8cewSsR_WW_Nn2FVfAy2OMYfk7G7Ff93AAAA___iXVYp">