ContentsThe library

Turning Text Into Instructions

The Compiler Deleted Your Safety Check Because You Had Already Broken the Rule

Last timeMaking It Faster Without Changing It

A language rule you did not know you made is a fact the optimiser is entitled to assume. Here is one real case followed from the source to the surprising output.

The promises you made

Every language definition has a list of things a program must not do. Reading

through a reference that points at nothing. Reading past the end of an array.

Letting a signed number exceed its range. Using a value that was never set.

The important part is what the definition says about a program that does one

of these anyway. It does not say the result is wrong, or that it varies by

machine, or that it is left to the implementation. It says nothing at all. The

behaviour is undefined, which means the language has made no promise about

what the program does from that point, or indeed before it.

Put that next to the rule from the previous lesson. An optimisation must

preserve the observable behaviour of the program. If the program has no defined

behaviour, there is nothing to preserve, and every transformation is correct.

That sounds like a lawyer trick and it is not. A compiler cannot generate code

for a program whose meaning the language declines to specify. It has exactly

two options: refuse to compile anything that might do such a thing, which is

impossible for the reasons in lesson three, or assume it does not happen. Every

compiler takes the second option, and so would you.

Assuming it cannot happen

The assumption is more powerful than it first appears, because it works

backwards as well as forwards.

FIG 1The chain of reasoning
stepstepwhat the compiler knowswhat it concludeswhat happened
11this line reads through the reference pnothing yetAn ordinary operation. The compiler emits a load and moves on, or so you would expect.
22reading through a reference is undefinedif this program is correct then p pointsThe language rule, turned around. The program is assumed correct, so the condition that would make it incorrect must be false.
33a later line tests whether p points at nthe test is false, because that was estaThe crucial move. The fact flows forwards from the read to the test, even though a human reads the test as the thing that establishes the fact.
44one arm of the test can never be takendelete the test and the armOrdinary dead code removal, applied to a fact that came from the language definition rather than from anything the programmer wrote.
4 steps
Each step is a correct inference and none of them is unusual. The surprise comes from the second step, which treats a rule of the language as information about this particular program, which is exactly what it is.
FIG 2A safety check that is not there in the output
plaintext
what was written:

    value = read the field of p
    if p points at nothing then
        report an error and return
    use value

what the compiler derives:

    the first line is undefined if p points
    at nothing, so p points at something
    therefore the test is always false
    therefore the error path is unreachable

what is generated:

    value = read the field of p
    use value
The check is gone. If this function is ever called with a reference pointing at nothing, the program now reads through it and continues with whatever it found, rather than reporting the error the author carefully wrote. The code was wrong before the optimiser touched it, and the optimiser is what made the wrongness visible.

One case, all the way through

Notice the ordering mistake at the heart of it. The author used the reference

on the first line and checked it on the second. Written the other way round,

the check comes first, nothing undefined has happened by the time the compiler

reaches it, no assumption is available, and the check stays.

This is why the advice is always to check before you act. It is not a style

preference. In the first order, the check is dead code as a matter of language

definition, and no compiler is required to keep it.

The lesson stops here

1 more paragraph to go

You have read the opening. The rest of the argument, the problems that check whether it landed, and the lines worth keeping at the end all come with a plan.

The first lesson of every course in the library reads the whole way through, free, so you can see exactly what the rest of them are.

See the planThe contents

This is the reading half

Starting the course gives you your own copy of it. Every idea on every page has problems standing under it, marked with a reason rather than a tick, and any sentence you do not believe can be opened and argued with. None of that can happen on a page nobody owns.

The contents

The rest of this course

  1. 01Before It Can Read Your Program It Has to Decide Where the Words End
  2. 02Precedence Is Not a Table You Memorise, It Is the Shape of the Rulesopening only
  3. 03The Stage That Finds Out Whether Your Names Refer to Anythingopening only
  4. 04One Neutral Form in the Middle Turns a Multiplication Into an Additionopening only
  5. 05Every Transformation Obeys One Rule, and the Rule Is Narrower Than You Thinkopening only
  6. 06The Compiler Deleted Your Safety Check Because You Had Already Broken the Ruleyou are here
  7. 07Thousands of Values and Sixteen Places to Keep Themopening only
  8. 08Your Instructions Arrive in Order and Are Executed in Whatever Order Suitsopening only

Read alongside