C++ Logo

SG12

Advanced search

Subject: Re: [ub] A proposal to define signed overflow submitted?
From: Lawrence Crowl (Lawrence_at_[hidden])
Date: 2018-03-16 13:09:45


On 3/14/18, Hyman Rosen <hyman.rosen_at_[hidden]> wrote:
> On Mar 14, 2018, Nick Lewycky <nlewycky_at_[hidden]> wrote:
> The concept of undefined behavior is erroneous except for things like
> dereferencing pointers that do not point into owned memory, where there
> is literally no meaningful thing that can happen.

There are machines that do not define the behavior of some instructions
with some arguments and/or in come contexts. Are those machines
erroneous?

> "Trust the programmer" must mean to make the actions of the compiled
> code follow the actions of the written code, or act as if it does.
> Anything else is literally not trusting the programmer.

The meaning of "x trusts y" is "x relies on the (promised) behavior of
y". I see no trust in your interpretation because the compiler does
not rely on any behavior on the part of the programmer. Your view is
blind obedience.

In contrast, in "the compiler trusts the programmer to not write wrong
programs", there is demonstrable trust on the part of the compiler.

> The compiler still has as-if latitude. As-if transformations maintain
> the actions of the written program.

As-if only has meaning with respect to an abstract machine. The issue
here is what you think the abstract machine should be.

> Assuming undefined behavior doesn't happen does not.

That assumption is the trust.

> The compiler has a mapping from abstract machine to its target computer,
> whereby it represents program objects as machine objects. The target
> computer has its own semantics for arithmetic operations. By far the
> most common semantics for machine integer arithmetic is fixed-width
> 2's-complement, and when that's the case, that's what the quoted phrase
> means - do the arithmetic in 2's-complement and return the result.

In this phrase you are trying to impose a change to the C/C++ abstract
machine. The standard defines the abstract machine; you do not. If you
want to change that machine, you need to change the standard.

> That is what the standard explicitly requires everywhere for atomic
> ints, so I don't see why you would find this surprising or difficult.

Atomic ints are a very special case because programners cannot anticipate
or avoid undefined behavior. The operations on atomic ints must be
fully closed. Those constraints do not apply elsewhere.

> But C and C++ already have the maximum possible stupidity of allowing
> floating-point expressions to be calculated at different precisions, so
> that it is literally possible for the expression a + b == a + b to be
> false when the operands are normal floating-point values. And everyone
> seems to accept that with equanimity. Optimization, naturally.

No, not optimization. Some floating-point hardware had operations that
were no commutative. IIRC, one was IBM 360 floating point. Machines
still run this instruction set. This license in the standard merely
reflects the underlying hardware space.

>> Suppose I have a function with two local variables, "int x, y;" and
>> I take &x, I can use x[1] (or x[-1]) to make changes to 'y'?
>
> Yes, provided you know that the memory layout corresponds to that.
>
>> And similarly, I can construct a pointer to stack variables in
>> another call stack frame whose address was never taken? As long as
>> I can cast the right integer to a pointer?
>
> Yes.
>
>> Given these rules, when would it be valid to move automatic local
>> variables into registers?
>
> Always. If a variable is not in memory, then a pointer can't be made
> to point to it, no matter how the pointer is manipulated. As I said,
> you must know the memory layout in order to do tricks with pointers.

This statement seems inconsistent. Since I can make a pointer to any
variable, then it must be in memory.

>> Only when there are no opaque pointers used and no opaque functions
>> called?
>
> No, always. In fact, that's why we had the register keyword. That
> told the compiler that the address of such variables could never be
> taken.

No, that's not why we had the register keyword. We had the register
keyword because some programmers were not happy with the simple
mapping of the abstract machine to the hardware. They wanted a means
to override the mapping. The 'cannot take the address' part is a
consequence, not a cause.

-- 
Lawrence Crowl

SG12 list run by herb.sutter at gmail.com