<div dir="auto"><div dir="auto">Hello all,</div><div dir="auto"><br></div><div dir="auto">LangRef says it is undefined behavior to pass null to a nonnull argument (`call f(nonnull null);`), but the semantics is too strong for a few existing optimizations.</div><div dir="auto">To support these, we can relax the semantics so `f(nonnull null)` is equivalent to `f(poison)`, but (A) it again blocks another set of optimizations, and (B) this makes the semantics of nonnull deviate from other attributes like dereferenceable.</div><div dir="auto">I found that there was a similar discussion about this issue in the past as well, but seems it is not settled yet.</div><div dir="auto">What should the semantics of nonnull be?</div><div dir="auto">I listed a few optimizations that are relevant with this issue.</div><div dir="auto"><br></div><div dir="auto"><br></div><div dir="auto"><div>1. Propagating nonnull attribute to callee's arg (<a href="https://godbolt.org/z/-cVsVP" rel="noreferrer noreferrer noreferrer noreferrer noreferrer noreferrer noreferrer" target="_blank">https://godbolt.org/z/-cVsVP</a> )</div><div><br></div><div>g(i8* ptr) {</div><div>f(nonnull ptr);</div><div>}<br></div><div>=></div><div>g(i8* nonnull ptr) {</div><div>f(nonnull ptr);</div><div>}<br></div><div><br></div><div>This is correct if f(nonnull null) is UB. If ptr == null, f(nonnull null) should have raised UB, so ptr shouldn't be null.<br></div><div>However, this transformation is incorrect if f(nonnull null) is equivalent to f(poison).</div><div>If f was an empty function, f(nonnull null) never raises UB regardless of ptr. So we can't guarantee ptr != null at other uses of ptr.<br></div><div></div></div><div dir="auto"><br></div><div dir="auto"><br></div><div dir="auto">2. InstCombine (<a href="https://godbolt.org/z/HDQ7rD" rel="noreferrer noreferrer noreferrer noreferrer noreferrer noreferrer noreferrer" target="_blank">https://godbolt.org/z/HDQ7rD</a> ):</div><div dir="auto"><br></div><div dir="auto">%ptr_inb = gep inbounds %any_ptr, 1</div><div dir="auto">f(%ptr_inb)</div><div dir="auto">=></div><div dir="auto">%ptr_inb = .. (identical)</div><div dir="auto">f(nonnull %ptr_inb)</div><div dir="auto"><br></div><div dir="auto">This optimization is incorrect if `f(nonnull null)` is UB. The reason is as follows.</div><div dir="auto">If `gep inbounds %any_ptr, 1` yields poison, the source is `f(poison)` whereas the optimized one is `f(nonnull poison)`.</div><div dir="auto">`f(nonnull poison)` should be UB because `f(nonnull null)` is UB. So, the transformation introduced UB.</div><div dir="auto">This optimization is correct if both `f(nonnull null)` and `f(nonnull poison)` are equivalent to `f(poison)`.</div><div dir="auto"><br></div><div dir="auto"><br></div><div dir="auto">3. <a href="https://reviews.llvm.org/D69477" rel="noreferrer noreferrer noreferrer noreferrer noreferrer noreferrer noreferrer" target="_blank">https://reviews.llvm.org/D69477</a> </div><div dir="auto"><br></div><div dir="auto">f(nonnull ptr);</div><div dir="auto">use(ptr);</div><div dir="auto">=></div><div dir="auto">llvm.assume(ptr != null);</div><div dir="auto">use(ptr);</div><div dir="auto">f(nonnull ptr);</div><div dir="auto"><br></div><div dir="auto">If f(nonnull null) is f(poison), this is incorrect. If ptr was null, the added llvm.assume(ptr != null) raises UB whereas the source may not raise UB at all. (e.g. assume that f() was an empty function)</div><div dir="auto">If f(nonnull null) is UB, this is correct.</div><div dir="auto"><br></div><div dir="auto"><br></div><div dir="auto">4. Dead argument elimination (from <a href="https://reviews.llvm.org/D70749" rel="noreferrer noreferrer noreferrer noreferrer noreferrer noreferrer noreferrer" target="_blank">https://reviews.llvm.org/D70749</a> )</div><div dir="auto"><br></div><div dir="auto">f(nonnull ptr); // f’s argument is dead</div><div dir="auto">=></div><div dir="auto">f(nonnull undef);</div><div dir="auto"><br></div><div dir="auto">This is incorrect if f(nonnull null) is UB. To make this correct, nonnull should be dropped. This becomes harder to fix if nonnull was attached at the signature of a function (not at the callee site).</div><div dir="auto">It is correct if f(nonnull null) is f(poison).</div><div dir="auto"><br></div><div dir="auto">Actually the D70749's thread had an end-to-end miscompilation example due to the interaction between this DAE and the optimization 3 (insertion of llvm.assume).</div><div dir="auto"><br></div><div dir="auto">Thanks,</div><div dir="auto">Juneyoung Lee</div></div>