<div dir="ltr">I don't think we should worry about trying to provide sane behavior for when people violate stated assumptions---doing so makes the compiler's work harder, and encourages people to violate the assumptions.  It is important to document clearly what the assumptions are, of course.<div><br></div><div>-Iavor<br><div><br></div></div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Thu, Dec 3, 2020 at 6:52 AM Eric Seidel <<a href="mailto:eric@seidel.io">eric@seidel.io</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">On Thu, Dec 3, 2020, at 05:29, Simon Marlow wrote:<br>
> My understanding of what happened in C compilers is that the optimisers <br>
> want to do some dataflow analysis to determine the ranges of values <br>
> that variables can take, in order to eliminate unnecessary conditionals <br>
> and so forth. These analyses are severely crippled if you can't assume <br>
> the absence of undefined behaviour in C/C++, because most arithmetic <br>
> operations have undefined behaviour: overflow is undefined, for example.<br>
<br>
Right, I think what leads to the surprising behavior in C/C++ is that compilers<br>
sometimes use the results of the dataflow analysis in a manner that violates<br>
causality.<br>
<br>
For example, removing a NULL check because later on the pointer<br>
is dereferenced unconditionally. I don't think anyone would complain about<br>
the compiler removing NULL checks *after* the dereference, but doing so<br>
before is a bit unsettling. Suppose the NULL check the compiler cleverly<br>
elided was meant to call into a custom abort() function. Now we have to hope<br>
the compiler can figure out that our abort() wrapper doesn't return, and we<br>
probably have to annotate our abort() to help the compiler along.<br>
<br>
Another example that always comes to mind is where the compiler may<br>
replace an indirect call via a NULL function pointer with a direct call to a<br>
function f() that is never actually assigned to the function pointer, because<br>
it detected that f() is the only possible target of the pointer and calling a<br>
NULL function pointer is also UB.<br>
<br>
> We don't do this kind of analysis in GHC at the moment, and we don't <br>
> have quite so much undefined behaviour floating around. But I can <br>
> imagine similar issues might arise. I mean, we most definitely assume <br>
> that the result of `unsafeCoerce#` is a value of the claimed type - <br>
> what else can you do? I suppose I'm not really clear on what <br>
> specifically it would mean to adopt "illegal but unchecked" as the <br>
> policy for undefined behaviour. Does it mean the compiler can't assume <br>
> anything about the result of an operation that has any undefined <br>
> behaviour?<br>
<br>
That's a fair point. I'm not entirely clear on where to draw the line either,<br>
but maybe respecting causality would be a good place to start. So it's fine<br>
for us to assume that operations that have UB complete successfully, and<br>
to assume the inputs satisfy the operation's contract from that point on,<br>
but maybe we should avoid propagating those assumptions back before<br>
the call-site that may invoke UB. But then what do we do about loops? It<br>
seems we would either have to special case the first iteration or block<br>
optimizations in the first section of the loop on every iteration.. Both<br>
options sound terrible..<br>
_______________________________________________<br>
ghc-steering-committee mailing list<br>
<a href="mailto:ghc-steering-committee@haskell.org" target="_blank">ghc-steering-committee@haskell.org</a><br>
<a href="https://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-steering-committee" rel="noreferrer" target="_blank">https://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-steering-committee</a><br>
</blockquote></div>