• Chapter 6, p. 121. In the first sentence of the Parsing Conditional Expressions subsection, the ? : operator is backwards. This sentence should read:

    The conditional ? : operator is a ternary operator…

  • Chapter 9, p. 180. On the last line of Listing 9-21, the call to typecheck_block is missing the symbols argument. This line should be:

           typecheck_block(decl.body, symbols)
    

    Thanks to Ian for catching this!

  • Chapter 13, p. 335. The assembly code given in Table 13-3 to convert from double to unsigned long includes an unnecessary instruction. As the last step in the conversion, this code copies the immediate 9223372036854775808 into a register, then adds it to the destination:

    Mov(Quadword, Imm(9223372036854775808), Reg(<R>))
    Binary(Add, Quadword, Reg(<R>), dst)
    

    This step takes two instructions because the source operand of add can’t be bigger than INT_MAX. However, it’s better to generate a single invalid add instruction at this stage and let the instruction fix-up pass rewrite it. That is, instead of those two instructions, we can just generate:

    Binary(Add, Quadword, Imm(9223372036854775808), dst)
    

    This is better because it uses one of our scratch registers (R10) instead of a non-scratch register, which will reduce register pressure once we implement register allocation in Chapter 20.

    Thanks to Yoshi Sono for catching this!

  • Chapter 14, p. 351. The “Null Pointers and Type Conversions” section on page 351 presents three examples of illegal implicit type versions and says:

    GCC warns about the implicit conversions in the previous three code snippets, but it still compiles them.

    GCC 14.1 and later treats all of these examples as errors by default. Hooray!

  • Chapter 20, p. 665-666. The coalesce function, discussed on pages 665-666, needs to handle an additional edge case if you completed Part II. You should not coalesce two pseudoregisters of different sizes (e.g. don’t coalesce a Longword into a Quadword or vice versa). If we coalesce a larger pseudoregister into a smaller one, and we end up spilling the coalesced register, we won’t allocate enough stack space for it, resulting in a potential miscompilation. Normally, the Briggs test should prevent the coalesced register from spilling, but the “ugly detail about the George test” discussed on page 661 means this isn’t guaranteed. Besides, as that discussion on page 661 points out, the Briggs and George tests are performance heuristics and we shouldn’t rely on them for correctness.

    Alternatively, you could coalesce pseudoregisters of different sizes but make sure to coalesce the smaller one into the larger one; however, that’s more fragile, so I don’t recommend it.