<div dir="ltr"><div class="gmail_extra"><div class="gmail_quote">On Thu, Mar 15, 2018 at 9:44 PM, Nick Lewycky <span dir="ltr">&lt;<a href="mailto:nlewycky@google.com" target="_blank">nlewycky@google.com</a>&gt;</span> wrote:<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div class="gmail_quote"><div>As-if transformations maintain the actions of the written program under the assumption that undefined behaviour does not occur. The two are fundamentally intertwined.</div></div></div></blockquote><div><br>Nonsense.  Compilers have been doing as-if manipulations forever without reference<br>to undefined behavior.  Things like strength reduction, loop unrolling, replacement of<br>multiplication by shifts, and a host of other transformations work to produce the same<br>results as the written code.  The obvious implication of your statement, if it were true,<br>is that compilers would be unable to perform as-if transformations for programming<br>languages in which all behavior was well defined.  That&#39;s obviously wrong.<br><br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div class="gmail_quote"><div>Here&#39;s a few thought experiments:</div></div></div></blockquote><div><br>The overall problem with your list of examples below is that they are postulating<br>meta-knowledge outside of the program itself, while my objections to undefined<br>behavior issues deal with the objects of the program.  Your examples are about<br>process, my desiderata are about results.  So your examples are basically straw<br>men.<br> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div class="gmail_quote"><div>1. may a compiler replace &quot;x+1+1&quot; with &quot;x+2&quot; assuming x is an int? The two produce the same results in all cases, even if you choose wrapping, overflowing, or saturating arithmetic. Suppose you are the vendor of a compiler and the user complains &quot;I know the add instruction encoding on my CPU, I crafted a pointer to my instruction stream, I failed to find two add-with-constant-1 instructions&quot;. With what rules would you justify the transformation &quot;x+1+1&quot; to &quot;x+2&quot;? Currently the answer is that there is no means to form such a pointer: any attempt to do so necessarily executes UB, therefore we assume it has not occurred.</div></div></div></blockquote><div><br>The language and the compiler do not promise the generation of any particular<br>sequence of instructions.  (They could if they wanted, but they don&#39;t.)  What is<br>promised, or what was promised by K&amp;R, is that the arithmetic in the language<br>maps to the arithmetic provided by the underlying instruction set of the computer<br>on which the program runs.  Today, and even then, this is overwhelmingly 2&#39;s-<br>complement arithmetic.  The compiler can replace x+1+1 with x+2 provided that<br>the underlying instruction set produces the same result when either of the two<br>operations are executed.<br> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div class="gmail_quote"><div>2. may a compiler remove &quot;int i = *ptr;&quot; where variable &#39;i&#39; is subsequently unused? What if &#39;ptr&#39; is unaligned or null? Suppose you are the vendor of a compiler and the user complains, &quot;the underlying hardware traps on misaligned/null pointers, I expected this execution to trap with the appropriate type of trap&quot;. With what rules would you justify deleting this dead code? Currently the answer is that null pointer dereference and misaligned access (well, access of wrong type) are unde</div></div></div></blockquote><div><br>Yes.  As-if transformations allow for code to be eliminated if it does not produce<br>results visible within constructs of the language.  Furthermore, C and C++ have<br>already made provision for users who want the trap to be produced.  Declare the<br>pointed-to object to be volatile, and the compiler will generate the access.<br><br>I believe failure to dereference a provably null pointer at one point caused a<br>significant error in the Linux kernel, because within the operating system code<br>it happened that there was valid memory at address zero that needed to be<br>read and written.<br> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div class="gmail_quote"><div>3. may a compiler remove &quot;int i = 0xdeadbeef;&quot; where &#39;i&#39; is unused? <span style="color:rgb(34,34,34);font-family:sans-serif;font-size:13px;font-style:normal;font-variant-ligatures:normal;font-variant-caps:normal;font-weight:400;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;background-color:rgb(255,255,255);text-decoration-style:initial;text-decoration-color:initial;float:none;display:inline">Suppose you are the vendor of a compiler and the user complains &quot;I put that value in a stack variable so that I could look through my stack for the magic number and know where I was in the stack&quot;. With what rules would you justify deleting an unused variable? Currently the answer is that there is no means to form such a pointer: any attempt to do so necessarily executes UB, therefore we assume it has not occurred.</span></div></div></div></blockquote><div><br>&quot;The stack&quot; is not an object defined by the language, and as-if transformations<br>allow for the removal of code that does not have visible effect on objects defined<br>by the language.  Once again, the variable can be declared volatile, and then the<br>value will be written, and the user can find it.<br> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div class="gmail_quote"><div>3a) A variation on question 3 is to put two variable declarations inside two arms of an if/else, assigning them to different values. Today every compiler will allocate space on the stack for all variables once as part of function prologue, except for functions with VLAs or dynamic alloca calls. What if the user looks up the stack to determine which side of the if/else they were being called from?</div></div></div></blockquote><div><br>Once again, volatile variables will be written.<br><br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div class="gmail_quote"><div>Today, all these optimizations are justified by the fact that any attempt to observe the difference before and after optimization must necessarily execute undefined behaviour, and executing undefined behaviour is a priori assumed to be impossible.<br></div></div></div></blockquote><div><br>No.  All these optimizations are justifiable by reference to the constructs of the<br>language.<br><br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div class="gmail_quote"><div>It&#39;s possible that there&#39;s another way to show that these and other optimizations are valid, but I would need to see a proposal on how to do that. I&#39;ve tried to create one myself and failed. Frankly, I&#39;m starting to believe that our current situation with UB really is the best possible design choice.</div></div></div></blockquote><div><br>The key is side-effects.  Programs make the results of their computation visible<br>externally in some fashion.  If those results are the same as those of the abstract<br>machine, then the program has been compiled correctly.  Otherwise, not.<br><br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div class="gmail_quote"><div>The difficulty is that the compiler would then need to have a processor emulator for the target platform. This is not presently how compilers work.</div></div></div></blockquote><div><br>2&#39;s-complement integer arithmetic is the same everywhere, so it doesn&#39;t need to<br>be &quot;emulated&quot;, just executed.  Floating-point was more difficult in the old days<br>(and in fact, that&#39;s why template value parameters can&#39;t be floats), but with IEEE<br>floating-point now being ubiquitous, getting exactly equivalent results for that is<br>now also relatively easy.<br><br>The notion that a compiler cannot easily evaluate an expression in the same way<br>that a target platform would is frankly ridiculous, especially for integer expressions.<br> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div class="gmail_quote"><div> It&#39;s possible that this emulator could in fact be made simple by dealing only with the subset of cases that the compiler itself relies on. Presently compilers simulate execution on top of a single abstract machine described in the language specification, and produce programs with equivalent behaviour to the abstract machine. The details of the underlying machine *largely* don&#39;t trickle upwards to affect evaluation in the abstract machine (except when it does, like in sizeof).</div></div></div></blockquote><div><br>Sizeof isn&#39;t a &quot;trickling upward&quot; of the machine into the compiler, except to the tiny<br>extent that the compiler has to pick some size and alignment for its objects.  Once<br>those are established, calculations of array and record sizes are just simple arithmetic,<br>with no machine peculiarities.<br><br>Unless you have an example that shows otherwise?<br><br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div class="gmail_quote"><div>I&#39;m not clever enough to see what possible argument you could make. Maybe you can suggest something? The setup is that you&#39;ve written a program and the compiler emits assembly you didn&#39;t consider &quot;the obvious lowering&quot;. You complain to the compiler authors and they say &quot;yes, this is the obvious lowering&quot;. Now how would you proceed? T<span style="color:rgb(34,34,34);font-family:sans-serif;font-size:13px;font-style:normal;font-variant-ligatures:normal;font-variant-caps:normal;font-weight:400;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;background-color:rgb(255,255,255);text-decoration-style:initial;text-decoration-color:initial;float:none;display:inline">here&#39;s no specification to reference to determine whether the compiler did emit the &quot;obvious lowering&quot; or not, </span>that&#39;s what I meant by &quot;not having standing&quot;.</div></div></div></blockquote><div><br>I guess we would have the same useless argument that we&#39;re having here,<br>they would leave the compiler unchanged, and I would continue to be annoyed.<br>What can I say?  I&#39;ve pretty much decided that no one can ever be convinced<br>that they&#39;re wrong, but that doesn&#39;t mean I&#39;ll stop presenting my side.<br><br>(For what it&#39;s worth, I have given up on my notion that reading uninitialized<br>memory should produce arbitrary but constant results, because of how<br>MADV_DONTNEED works.)<br><br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div class="gmail_quote"><div>I agree! But I arrived at the opposite conclusion. I wish compilers exploited undefined behaviour to the fullest extent, and I wish they had started doing more of it longer ago. If only the optimizer broke people&#39;s programs the first time they ran them, programmers would have noticed the failure sooner and not proceeded with their buggy code. The sooner the optimizers get better at breaking buggy code in mean and nasty ways, the sooner we&#39;ll stop writing even more code with these kinds of bugs in it. I would rather deal with that pain now so that we have a better world tomorrow.</div></div></div></blockquote><div><br>The code is only buggy because the language has made something undefined<br>that should have been implementation-defined at worst.  And the fundamental<br>problem with undefined behavior is exactly the same as with unspecified order<br>of evaluation.  The programmers write programs that they think express their<br>intent correctly.  The translation system produces an executable that matches<br>their expectations by accident, and years later, another translation system does<br>not, and produces a program that no longer works correctly.<br><br>As I have said here before ad nauseum, the purpose of a program is to control<br>the operation of a computer.  If the programming language allows a program to<br>have multiple meanings, then the intent of the programmer may not be mirrored<br>by the programming system at a point in the future far removed from when the<br>program was written, and literal disaster may ensue.<br><br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div class="gmail_quote"><div>The as-if rule only applies if the program can not tell the different between the before program and the after program. If you can form pointers this way, the program now has a way to observe the difference, revoking the compiler&#39;s permission to perform the optimization. With these rules, the compiler may no longer remove stack variables from the stack, or reorder them, etc. I don&#39;t think that&#39;s the language we want. Do you have a suggestion on how to both permit the construction of such pointers and also permit this optimization?</div></div></div></blockquote><div><br>You can form pointers to objects in this way if those objects exist in a form<br>that can be pointed to.  But that&#39;s not required by the language.  For things<br>that are in memory, the compiler must make conservative assumptions in<br>the presence of pointer derefernce unless it can prove that a pointer cannot<br>access a particular region of storage.<br> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div class="gmail_quote"><span class=""><div>I don&#39;t think C++ users want a language where copies of all automatic storage variables must be present on the stack.<br></div></span></div></div></blockquote><div><br>Neither do I.<br> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div class="gmail_quote"><span class=""><div></div></span><div><br></div><div>There might be a market for a different language which really does behave that way, but I should point out that it&#39;s been tried. The proposal for Friendly C: <a href="https://blog.regehr.org/archives/1180" target="_blank">https://blog.regehr.org/<wbr>archives/1180</a> and later The Problem with Friendly C: <a href="https://blog.regehr.org/archives/1287" target="_blank">https://blog.regehr.org/<wbr>archives/1287</a> .</div></div></div></blockquote><div> </div>&quot;There are just too many variations, each with its own set of performance tradeoffs, for consensus to be possible.&quot; <br><br>In other words, derailed by optimizationists.  I&#39;m not even a little surprised.</div></div></div>