Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.c > #61986 > unrolled thread

Sequence points and commit to memory

Started by"James Harris" <james.harris.1@gmail.com>
First post2015-05-14 11:43 +0100
Last post2015-05-24 08:11 -0700
Articles 20 on this page of 101 — 15 participants

Back to article view | Back to comp.lang.c


Contents

  Sequence points and commit to memory "James Harris" <james.harris.1@gmail.com> - 2015-05-14 11:43 +0100
    Re: Sequence points and commit to memory Richard Heathfield <rjh@cpax.org.uk> - 2015-05-14 12:02 +0100
      Re: Sequence points and commit to memory David Brown <david.brown@hesbynett.no> - 2015-05-14 13:39 +0200
        Re: Sequence points and commit to memory Richard Heathfield <rjh@cpax.org.uk> - 2015-05-14 13:18 +0100
          Re: Sequence points and commit to memory David Brown <david.brown@hesbynett.no> - 2015-05-14 23:11 +0200
    Re: Sequence points and commit to memory Stephen Sprunk <stephen@sprunk.org> - 2015-05-14 09:06 -0500
    Re: Sequence points and commit to memory Siri Cruz <chine.bleu@yahoo.com> - 2015-05-14 08:35 -0700
      Re: Sequence points and commit to memory David Kleinecke <dkleinecke@gmail.com> - 2015-05-14 10:23 -0700
        Re: Sequence points and commit to memory Siri Cruz <chine.bleu@yahoo.com> - 2015-05-14 10:44 -0700
          Re: Sequence points and commit to memory David Kleinecke <dkleinecke@gmail.com> - 2015-05-14 11:09 -0700
            Re: Sequence points and commit to memory Keith Thompson <kst-u@mib.org> - 2015-05-14 12:58 -0700
            Re: Sequence points and commit to memory Morris Dovey <mrdovey@iedu.com> - 2015-05-14 16:31 -0500
              Re: Sequence points and commit to memory Keith Thompson <kst-u@mib.org> - 2015-05-14 17:24 -0700
                Re: Sequence points and commit to memory Morris Dovey <mrdovey@iedu.com> - 2015-05-14 20:18 -0500
                  Re: Sequence points and commit to memory David Brown <david.brown@hesbynett.no> - 2015-05-15 09:34 +0200
                    Re: Sequence points and commit to memory Morris Dovey <mrdovey@iedu.com> - 2015-05-15 10:18 -0500
                    Re: 'register' attribute (was: Sequence points and commit to memory) Morris Dovey <mrdovey@iedu.com> - 2015-05-16 01:26 -0500
                      Re: 'register' attribute David Brown <david.brown@hesbynett.no> - 2015-05-17 00:02 +0200
                        Re: 'register' attribute Morris Dovey <mrdovey@iedu.com> - 2015-05-16 19:29 -0500
              Re: Sequence points and commit to memory David Kleinecke <dkleinecke@gmail.com> - 2015-05-15 09:52 -0700
                Re: Sequence points and commit to memory James Kuyper <jameskuyper@verizon.net> - 2015-05-15 13:27 -0400
                  Re: Sequence points and commit to memory Keith Thompson <kst-u@mib.org> - 2015-05-15 10:50 -0700
                Re: Sequence points and commit to memory Morris Dovey <mrdovey@iedu.com> - 2015-05-15 12:32 -0500
        Re: Sequence points and commit to memory David Brown <david.brown@hesbynett.no> - 2015-05-14 23:23 +0200
          Re: Sequence points and commit to memory David Kleinecke <dkleinecke@gmail.com> - 2015-05-15 09:42 -0700
            Re: Sequence points and commit to memory James Kuyper <jameskuyper@verizon.net> - 2015-05-15 13:13 -0400
              Re: Sequence points and commit to memory David Kleinecke <dkleinecke@gmail.com> - 2015-05-16 08:32 -0700
            Re: Sequence points and commit to memory Keith Thompson <kst-u@mib.org> - 2015-05-15 10:35 -0700
            Re: Sequence points and commit to memory Keith Thompson <kst-u@mib.org> - 2015-05-15 11:08 -0700
              Re: Sequence points and commit to memory Robert Wessel <robertwessel2@yahoo.com> - 2015-05-15 16:40 -0500
              Re: Sequence points and commit to memory David Kleinecke <dkleinecke@gmail.com> - 2015-05-16 08:48 -0700
            Re: Sequence points and commit to memory "James Harris" <james.harris.1@gmail.com> - 2015-05-15 21:46 +0100
              Re: Sequence points and commit to memory David Kleinecke <dkleinecke@gmail.com> - 2015-05-16 08:56 -0700
                Re: Sequence points and commit to memory "James Harris" <james.harris.1@gmail.com> - 2015-05-16 19:35 +0100
                  Re: Sequence points and commit to memory David Kleinecke <dkleinecke@gmail.com> - 2015-05-18 09:49 -0700
                    Re: Sequence points and commit to memory "James Harris" <james.harris.1@gmail.com> - 2015-05-19 09:04 +0100
                    Re: Sequence points and commit to memory Keith Thompson <kst-u@mib.org> - 2015-05-19 08:42 -0700
      Re: Sequence points and commit to memory "James Harris" <james.harris.1@gmail.com> - 2015-05-14 22:00 +0100
        Re: Sequence points and commit to memory Siri Cruz <chine.bleu@yahoo.com> - 2015-05-14 14:26 -0700
          Re: Sequence points and commit to memory "James Harris" <james.harris.1@gmail.com> - 2015-05-14 22:33 +0100
        Re: Sequence points and commit to memory David Brown <david.brown@hesbynett.no> - 2015-05-14 23:38 +0200
          Re: Sequence points and commit to memory "James Harris" <james.harris.1@gmail.com> - 2015-05-14 23:04 +0100
            Re: Sequence points and commit to memory Stephen Sprunk <stephen@sprunk.org> - 2015-05-15 13:09 -0500
              Re: Sequence points and commit to memory "James Harris" <james.harris.1@gmail.com> - 2015-05-15 21:56 +0100
                Re: Sequence points and commit to memory James Kuyper <jameskuyper@verizon.net> - 2015-05-15 17:30 -0400
                  Re: Sequence points and commit to memory "James Harris" <james.harris.1@gmail.com> - 2015-05-16 07:51 +0100
                    Re: Sequence points and commit to memory James Kuyper <jameskuyper@verizon.net> - 2015-05-16 22:10 -0400
                      Re: Sequence points and commit to memory David Brown <david.brown@hesbynett.no> - 2015-05-19 00:25 +0200
                        Re: Sequence points and commit to memory James Kuyper <jameskuyper@verizon.net> - 2015-05-18 18:55 -0400
                          Re: Sequence points and commit to memory David Brown <david.brown@hesbynett.no> - 2015-05-19 09:10 +0200
                            Re: Sequence points and commit to memory James Kuyper <jameskuyper@verizon.net> - 2015-05-19 12:05 -0400
                              Re: Sequence points and commit to memory David Brown <david.brown@hesbynett.no> - 2015-05-19 18:42 +0200
                Re: Sequence points and commit to memory Stephen Sprunk <stephen@sprunk.org> - 2015-05-16 16:33 -0500
                  Re: Sequence points and commit to memory David Brown <david.brown@hesbynett.no> - 2015-05-19 00:27 +0200
                    Re: Sequence points and commit to memory James Kuyper <jameskuyper@verizon.net> - 2015-05-18 19:05 -0400
                      Re: Sequence points and commit to memory glen herrmannsfeldt <gah@ugcs.caltech.edu> - 2015-05-19 00:09 +0000
                        Re: Sequence points and commit to memory Richard Heathfield <rjh@cpax.org.uk> - 2015-05-19 01:11 +0100
                          Re: Sequence points and commit to memory Tim Rentsch <txr@alumni.caltech.edu> - 2015-05-23 07:02 -0700
                        Re: Sequence points and commit to memory James Kuyper <jameskuyper@verizon.net> - 2015-05-18 23:21 -0400
                          Re: Sequence points and commit to memory glen herrmannsfeldt <gah@ugcs.caltech.edu> - 2015-05-19 06:25 +0000
                            Re: Sequence points and commit to memory David Brown <david.brown@hesbynett.no> - 2015-05-19 08:58 +0200
                              Re: Sequence points and commit to memory glen herrmannsfeldt <gah@ugcs.caltech.edu> - 2015-05-19 23:20 +0000
                                Re: Sequence points and commit to memory David Brown <david.brown@hesbynett.no> - 2015-05-20 12:31 +0200
                            Re: Sequence points and commit to memory James Kuyper <jameskuyper@verizon.net> - 2015-05-19 12:10 -0400
                              Re: Sequence points and commit to memory glen herrmannsfeldt <gah@ugcs.caltech.edu> - 2015-05-19 23:24 +0000
                      Re: Sequence points and commit to memory David Brown <david.brown@hesbynett.no> - 2015-05-19 09:05 +0200
                        Re: Sequence points and commit to memory Keith Thompson <kst-u@mib.org> - 2015-05-19 08:37 -0700
                        Re: Sequence points and commit to memory James Kuyper <jameskuyper@verizon.net> - 2015-05-19 12:14 -0400
                          Re: Sequence points and commit to memory David Brown <david.brown@hesbynett.no> - 2015-05-19 19:02 +0200
              Re: Sequence points and commit to memory Robert Wessel <robertwessel2@yahoo.com> - 2015-05-15 16:46 -0500
            Re: Sequence points and commit to memory David Brown <david.brown@hesbynett.no> - 2015-05-17 00:20 +0200
              Re: Sequence points and commit to memory "James Harris" <james.harris.1@gmail.com> - 2015-05-17 18:19 +0100
                Re: Sequence points and commit to memory Philip Lantz <prl@canterey.us> - 2015-05-17 16:11 -0700
                  Re: Sequence points and commit to memory "James Harris" <james.harris.1@gmail.com> - 2015-05-18 08:30 +0100
                    Re: Sequence points and commit to memory David Kleinecke <dkleinecke@gmail.com> - 2015-05-18 10:31 -0700
                      Re: Sequence points and commit to memory Keith Thompson <kst-u@mib.org> - 2015-05-18 11:11 -0700
                        Re: Sequence points and commit to memory David Kleinecke <dkleinecke@gmail.com> - 2015-05-19 09:37 -0700
                      Re: Sequence points and commit to memory Ian Collins <ian-news@hotmail.com> - 2015-05-19 17:40 +1200
                        Re: Sequence points and commit to memory Morris Dovey <mrdovey@iedu.com> - 2015-05-19 01:31 -0500
                          Re: Sequence points and commit to memory Ian Collins <ian-news@hotmail.com> - 2015-05-19 20:38 +1200
                            Re: Sequence points and commit to memory Morris Dovey <mrdovey@iedu.com> - 2015-05-19 10:50 -0500
                    Re: Sequence points and commit to memory David Brown <david.brown@hesbynett.no> - 2015-05-19 00:46 +0200
                Re: Sequence points and commit to memory David Brown <david.brown@hesbynett.no> - 2015-05-19 00:35 +0200
        Re: Sequence points and commit to memory Robert Wessel <robertwessel2@yahoo.com> - 2015-05-15 16:42 -0500
          Re: Sequence points and commit to memory "James Harris" <james.harris.1@gmail.com> - 2015-05-16 07:58 +0100
            Re: Sequence points and commit to memory Robert Wessel <robertwessel2@yahoo.com> - 2015-05-16 02:18 -0500
              Re: Sequence points and commit to memory "James Harris" <james.harris.1@gmail.com> - 2015-05-16 08:36 +0100
                Re: Sequence points and commit to memory Robert Wessel <robertwessel2@yahoo.com> - 2015-05-17 04:31 -0500
                  Re: Sequence points and commit to memory "James Harris" <james.harris.1@gmail.com> - 2015-05-17 18:24 +0100
                    Re: Sequence points and commit to memory David Brown <david.brown@hesbynett.no> - 2015-05-19 00:55 +0200
    Re: Sequence points and commit to memory Keith Thompson <kst-u@mib.org> - 2015-05-14 11:26 -0700
      Re: Sequence points and commit to memory "James Harris" <james.harris.1@gmail.com> - 2015-05-14 22:07 +0100
        Re: Sequence points and commit to memory Keith Thompson <kst-u@mib.org> - 2015-05-14 14:22 -0700
          Re: Sequence points and commit to memory "James Harris" <james.harris.1@gmail.com> - 2015-05-14 22:36 +0100
      Re: Sequence points and commit to memory Tim Rentsch <txr@alumni.caltech.edu> - 2015-05-22 08:16 -0700
        Re: Sequence points and commit to memory Keith Thompson <kst-u@mib.org> - 2015-05-22 11:31 -0700
    Re: Sequence points and commit to memory James Kuyper <jameskuyper@verizon.net> - 2015-05-15 00:05 -0400
      Re: Sequence points and commit to memory "James Harris" <james.harris.1@gmail.com> - 2015-05-15 22:25 +0100
        Re: Sequence points and commit to memory James Kuyper <jameskuyper@verizon.net> - 2015-05-15 17:56 -0400
    Re: Sequence points and commit to memory Nobody <nobody@nowhere.invalid> - 2015-05-17 10:04 +0100
    Re: Sequence points and commit to memory Tim Rentsch <txr@alumni.caltech.edu> - 2015-05-24 08:11 -0700

Page 3 of 6 — ← Prev page 1 2 [3] 4 5 6  Next page →


#62030

FromDavid Brown <david.brown@hesbynett.no>
Date2015-05-14 23:38 +0200
Message-ID<mj34ho$jll$1@dont-email.me>
In reply to#62020
On 14/05/15 23:00, James Harris wrote:
> "Siri Cruz" <chine.bleu@yahoo.com> wrote in message
> news:chine.bleu-804F61.08350514052015@news.eternal-september.org...
>> In article <mj1u70$r5d$1@dont-email.me>,
>> "James Harris" <james.harris.1@gmail.com> wrote:
>>
>>> How about:
>>>
>>>    u = 0;
>>>    f();
>>>
>>> Is u guaranteed to have been written to memory before function f is
>>
>> No. There's nothing in the above that exposes the address of u to f,
>> so f has no
>> legitmate way to know about u.
>>
>>     extern int *v; v = &u;
>>     u = 0;
>>     f();
>>
>> Now the f might have access to the address of u. The compiler would
>> need to have
>> the implementation of f at hand to see if uses any (extern int*), all
>> the places
>> those are used, etc, to safely determine that f still does not have
>> access to u.
>
> If u is a global or, as you point out, its address has been taken and
> stored elsewhere, then the compiler would have to spill u to memory
> before making the function call unless it could prove that f did not
> read or write u. Putting f in another translation unit should do the
> latter part. According to
>
>    http://port70.net/~nsz/c/c89/c89-draft.html#2.1.2.3
>
> "Alternatively, an implementation might perform various optimizations
> within each translation unit, such that the actual semantics would agree
> with the abstract semantics only when making function calls across
> translation unit boundaries. In such an implementation, at the time of
> each function entry and function return where the calling function and
> the called function are in different translation units, the values of
> all externally linked objects and of all objects accessible via pointers
> therein would agree with the abstract semantics. "
>
>> MIPS used its loader as a compiler so that it had the entire program
>> text
>> available. It could then do this kind of global analysis.
>>
>>> called? Does f need to be in a different translation unit to force
>>> prior
>>> commits to memory (so that pointers to their values are valid)?
>>
>> If you never take the address of u, it does not need memory allocated;
>> equivalently if you declare register int u, it does not need memory
>> allocated.
>>
>>
>> volatile int u means that no assignment nor load can be redundant. The
>> compiler
>> must assume some unknown process can alter u asynchronously anywhere
>> even in the
>> execution of any instruction except an atomic load or store.
>
> In reality volatile is too strong a restriction. I don't need the values
> to be read from memory each time they are referenced. I only need to
> ensure that updates to them are written to memory.
>
> In the absence of being able to code a proper memory barrier (as per
> David's suggestion) volatile is a good choice.
>
> For the moment I have chosen to make some updates in a function in a
> separate translation unit with the idea that when the called function
> returns across the TU boundary its updates will *have* to have been
> written to memory.

/Bad/ idea.  You are introducing additional pessimations in a hope of 
making the code correct - and restricting yourself to using a compiler 
(or compiler options) that don't allow cross-unit optimisations.

Making u volatile would be simpler, safer, and more efficient - even if 
it leads to occasional extra accesses.  You can always make a local copy 
of it in a non-volatile variable if that helps.

Another option is to use a macro like this:

#define volatileAccess(v) *((volatile typeof((v)) *) &(v))

Then even if u is not volatile, you can write

	volatileAccess(u) = 0;

to force that particular storage to be volatile.

(The macro uses the gcc "typeof" extension, which you may or may not like.)



> Example function below. In this case I had to make
> two updates atomic so have added interrupt disable and restore around it
> but the main point is that the updates *should* AFAICS be guaranteed to
> end up in memory before the function returns. After all, there's no way
> they can be kept in registers after the function ends if the compiler
> has no way to do inter-function optimisation.
>
> void dnode_delete(dnode_t *x) {
>    unsigned state;
>
>    /* Disable interrupts */
>    state = irqrx_set(IRQRX_DISABLE);
>
>    x->prev.next = x.next;
>    x->next.prev = x.prev;
>
>    /* Restore interrupt handling */
>    irqrx_set(state);
> }
>
> The code is ostentibly simple but making it robust is not!
>

And it is still not correct.  There is a good chance that the compiler 
will generate the code you want, but it certainly does not have to - so 
you are playing a very dangerous game.

The key issue is that while the irqrx_set function is presumably 
volatile (or uses a volatile access to the irq flag), and thus the 
compiler guarantees that the interrupts will be disabled and then 
restored, the standards say nothing about the ordering of volatile 
accesses with respect to non-volatile accesses.  The compiler could move 
the assignments to before the interrupt disable, or after the restore, 
or in pretty much any other order it wants.

You have two options here.  Make the accesses volatile (by using the 
volatileAccess macro above, or declaring x as "volatile dnode_t *x"), or 
use a memory barrier after the interrupt disable, and another one before 
the re-enable.

You are correct that this stuff is not easy - it's part of why good 
embedded developers are in high demand, and poor ones put comments at 
the start of files saying "This module must be compiled with 
optimisation disabled!".

[toc] | [prev] | [next] | [standalone]


#62031

From"James Harris" <james.harris.1@gmail.com>
Date2015-05-14 23:04 +0100
Message-ID<mj363p$tfs$1@dont-email.me>
In reply to#62030
"David Brown" <david.brown@hesbynett.no> wrote in message 
news:mj34ho$jll$1@dont-email.me...
> On 14/05/15 23:00, James Harris wrote:

...

>> In the absence of being able to code a proper memory barrier (as per
>> David's suggestion) volatile is a good choice.
>>
>> For the moment I have chosen to make some updates in a function in a
>> separate translation unit with the idea that when the called function
>> returns across the TU boundary its updates will *have* to have been
>> written to memory.
>
> /Bad/ idea.  You are introducing additional pessimations in a hope of 
> making the code correct - and restricting yourself to using a compiler 
> (or compiler options) that don't allow cross-unit optimisations.

I have not heard the word "pessimations" before.

I think I am safe from cross-unit optimisations because I compile each 
source file separately. There is no communication between them, even to 
say which object files will work with which other object files, until 
the linker is run.

> Making u volatile would be simpler, safer, and more efficient - even 
> if it leads to occasional extra accesses.  You can always make a local 
> copy of it in a non-volatile variable if that helps.

Agreed.

...

>> Example function below. In this case I had to make
>> two updates atomic so have added interrupt disable and restore around 
>> it
>> but the main point is that the updates *should* AFAICS be guaranteed 
>> to
>> end up in memory before the function returns. After all, there's no 
>> way
>> they can be kept in registers after the function ends if the compiler
>> has no way to do inter-function optimisation.
>>
>> void dnode_delete(dnode_t *x) {
>>    unsigned state;
>>
>>    /* Disable interrupts */
>>    state = irqrx_set(IRQRX_DISABLE);
>>
>>    x->prev.next = x.next;
>>    x->next.prev = x.prev;
>>
>>    /* Restore interrupt handling */
>>    irqrx_set(state);
>> }
>>
>> The code is ostentibly simple but making it robust is not!
>>
>
> And it is still not correct.  There is a good chance that the compiler 
> will generate the code you want, but it certainly does not have to - 
> so you are playing a very dangerous game.
>
> The key issue is that while the irqrx_set function is presumably 
> volatile (or uses a volatile access to the irq flag), and thus the 
> compiler guarantees that the interrupts will be disabled and then 
> restored, the standards say nothing about the ordering of volatile 
> accesses with respect to non-volatile accesses.

The irqrx_set function is not even in C. It is written in assembly.

But the main point is that I intended the above dnode_delete function to 
commit all of its changes to memory before it returns to its caller.

>  The compiler could move the assignments to before the interrupt 
> disable, or after the restore, or in pretty much any other order it 
> wants.

That is a big surprise - and a big problem. Are you sure that a compiler 
could do that? If it did it would change the semantics of the program.

James

[toc] | [prev] | [next] | [standalone]


#62059

FromStephen Sprunk <stephen@sprunk.org>
Date2015-05-15 13:09 -0500
Message-ID<mj5clq$jj$1@dont-email.me>
In reply to#62031
On 14-May-15 17:04, James Harris wrote:
> "David Brown" <david.brown@hesbynett.no> wrote:
>> /Bad/ idea.  You are introducing additional pessimations in a hope
>> of making the code correct - and restricting yourself to using a
>> compiler (or compiler options) that don't allow cross-unit
>> optimisations.
> 
> I have not heard the word "pessimations" before.

I think that should be "pessimisations".

I've also seen "pessimal", to mirror "optimal".

> I think I am safe from cross-unit optimisations because I compile
> each source file separately. There is no communication between them,
> even to say which object files will work with which other object
> files, until the linker is run.

Some implementations will "compile" source files to an intermediate
language, then finish translation during the linking stage, making
cross-unit (aka "whole program") optimizations possible.

>> The compiler could move the assignments to before the interrupt 
>> disable, or after the restore, or in pretty much any other order it
>>  wants.
> 
> That is a big surprise - and a big problem. Are you sure that a
> compiler could do that? If it did it would change the semantics of
> the program.

"Interrupts" are outside the scope of the C Standard, so any use of them
will put you outside the realm of defined behavior.

"volatile" is often useful for such issues, but what it guarantees may
not exactly match what you need.  In particular, it often goes _far_
beyond your requirements, and it conflicts with many optimizations that
may actually be safe, leading to suboptimal performance.

S

-- 
Stephen Sprunk         "God does not play dice."  --Albert Einstein
CCIE #3723         "God is an inveterate gambler, and He throws the
K5SSS        dice at every possible opportunity." --Stephen Hawking

[toc] | [prev] | [next] | [standalone]


#62069

From"James Harris" <james.harris.1@gmail.com>
Date2015-05-15 21:56 +0100
Message-ID<mj5mf3$770$1@dont-email.me>
In reply to#62059
"Stephen Sprunk" <stephen@sprunk.org> wrote in message 
news:mj5clq$jj$1@dont-email.me...
> On 14-May-15 17:04, James Harris wrote:
>> "David Brown" <david.brown@hesbynett.no> wrote:

...

>>> The compiler could move the assignments to before the interrupt
>>> disable, or after the restore, or in pretty much any other order it
>>>  wants.
>>
>> That is a big surprise - and a big problem. Are you sure that a
>> compiler could do that? If it did it would change the semantics of
>> the program.
>
> "Interrupts" are outside the scope of the C Standard, so any use of 
> them
> will put you outside the realm of defined behavior.

It is not interrupts which are the problem. If I understood him 
correctly David mentioned that in

  function_call()
  pointer_manipulations
  function_call()

the compiler could move the pointer manipulations to before the initial 
function call or after the final function call. That would simply break 
the code.

> "volatile" is often useful for such issues, but what it guarantees may
> not exactly match what you need.  In particular, it often goes _far_
> beyond your requirements, and it conflicts with many optimizations 
> that
> may actually be safe, leading to suboptimal performance.

Agreed. It is too heavyweight a tool but could be used. As someone said, 
I may be able to maniuplate values in a non-volatile variable and copy 
them to and from the volatile one when required. That use of a "cached 
copy" would help ameliorate some of the potential performance issues.

James

[toc] | [prev] | [next] | [standalone]


#62074

FromJames Kuyper <jameskuyper@verizon.net>
Date2015-05-15 17:30 -0400
Message-ID<5556657B.5050609@verizon.net>
In reply to#62069
On 05/15/2015 04:56 PM, James Harris wrote:
> "Stephen Sprunk" <stephen@sprunk.org> wrote in message 
...
> It is not interrupts which are the problem. If I understood him 
> correctly David mentioned that in
> 
>   function_call()
>   pointer_manipulations
>   function_call()
> 
> the compiler could move the pointer manipulations to before the initial 
> function call or after the final function call. That would simply break 
> the code.

Are you sure he said that? I don't recall seeing him raise that issue.

That can happen only if there's no sequence points separating those
steps. Assuming that function_call() returns 'int', and that 'p' has the
type "T*", then the expression

   T* final = function_call() + (p++) + function_call();

does leave it unspecified whether p++ occurs before or after either of
the calls. However, if you need to be sure of the order, all that's
required is to explicitly specify it:

   int temp1 = function_call();
   T* final = temp1 + p++;
   final += function_call();

The first approach says "I don't care what order these sub-expressions
are evaluated in". That's fine - if it's the truth. However, if you do
care about the order, then the second approach is one way to tell the
compiler what the order should be (there's several other functionally
equivalent ways of doing it). And it's only the second approach that has
the sequence points that are the "Subject:" of this thread.

[toc] | [prev] | [next] | [standalone]


#62098

From"James Harris" <james.harris.1@gmail.com>
Date2015-05-16 07:51 +0100
Message-ID<mj6pca$5ab$1@dont-email.me>
In reply to#62074
"James Kuyper" <jameskuyper@verizon.net> wrote in message 
news:5556657B.5050609@verizon.net...
> On 05/15/2015 04:56 PM, James Harris wrote:
>> "Stephen Sprunk" <stephen@sprunk.org> wrote in message
> ...
>> It is not interrupts which are the problem. If I understood him
>> correctly David mentioned that in
>>
>>   function_call()
>>   pointer_manipulations
>>   function_call()
>>
>> the compiler could move the pointer manipulations to before the 
>> initial
>> function call or after the final function call. That would simply 
>> break
>> the code.
>
> Are you sure he said that? I don't recall seeing him raise that issue.

No, I am not sure. The paragraph in question was

DB> The key issue is that while the irqrx_set function is presumably
DB> volatile (or uses a volatile access to the irq flag), and thus the
DB> compiler guarantees that the interrupts will be disabled and then
DB> restored, the standards say nothing about the ordering of volatile
DB> accesses with respect to non-volatile accesses.  The compiler could 
move
DB> the assignments to before the interrupt disable, or after the 
restore,
DB> or in pretty much any other order it wants.

It is in the post dated 14 May 2015 at 21:38 GMT. The code he was 
commenting on was

 void dnode_delete(dnode_t *x) {
    unsigned state;

    /* Disable interrupts */
    state = irqrx_set(IRQRX_DISABLE);

    x->prev.next = x.next;
    x->next.prev = x.prev;

    /* Restore interrupt handling */
    irqrx_set(state);
 }

> That can happen only if there's no sequence points separating those
> steps.

That should be OK then. IIUC there is a sequence point before a function 
call after the operands have been evaluated.

Perhaps he meant that the reads of memory could be reordered to before 
the first function call and the writes could take place after the last 
function call unless the variables were marked as volatile.

My point (to which David was responding) was that wrapping the above 
into a function in a separate translation unit and having no 
inter-translation unit optimisation should ensure that the variables 
x->prev.next and x->next.prev were updated in memory and not kept in 
registers before the function returned.

James

[toc] | [prev] | [next] | [standalone]


#62138

FromJames Kuyper <jameskuyper@verizon.net>
Date2015-05-16 22:10 -0400
Message-ID<mj8t7s$2rm$1@dont-email.me>
In reply to#62098
On 05/16/2015 02:51 AM, James Harris wrote:
> "James Kuyper" <jameskuyper@verizon.net> wrote in message 
> news:5556657B.5050609@verizon.net...
>> On 05/15/2015 04:56 PM, James Harris wrote:
>>> "Stephen Sprunk" <stephen@sprunk.org> wrote in message
>> ...
>>> It is not interrupts which are the problem. If I understood him
>>> correctly David mentioned that in
>>>
>>>   function_call()
>>>   pointer_manipulations
>>>   function_call()
>>>
>>> the compiler could move the pointer manipulations to before the 
>>> initial
>>> function call or after the final function call. That would simply 
>>> break
>>> the code.
>>
>> Are you sure he said that? I don't recall seeing him raise that issue.
> 
> No, I am not sure. The paragraph in question was
> 
> DB> The key issue is that while the irqrx_set function is presumably
> DB> volatile (or uses a volatile access to the irq flag), and thus the
> DB> compiler guarantees that the interrupts will be disabled and then
> DB> restored, the standards say nothing about the ordering of volatile
> DB> accesses with respect to non-volatile accesses.  The compiler could 
> move
> DB> the assignments to before the interrupt disable, or after the 
> restore,
> DB> or in pretty much any other order it wants.
> 
> It is in the post dated 14 May 2015 at 21:38 GMT. The code he was 
> commenting on was
> 
>  void dnode_delete(dnode_t *x) {
>     unsigned state;
> 
>     /* Disable interrupts */
>     state = irqrx_set(IRQRX_DISABLE);
> 
>     x->prev.next = x.next;
>     x->next.prev = x.prev;
> 
>     /* Restore interrupt handling */
>     irqrx_set(state);
>  }
> 
>> That can happen only if there's no sequence points separating those
>> steps.
> 
> That should be OK then. IIUC there is a sequence point before a function 
> call after the operands have been evaluated.

More importantly, there's a sequence point at the end of every
statement. That would be, in itself, sufficient to render his concerns
irrelevant.
-- 
James Kuyper

[toc] | [prev] | [next] | [standalone]


#62254

FromDavid Brown <david.brown@hesbynett.no>
Date2015-05-19 00:25 +0200
Message-ID<mjdoqe$gdj$1@dont-email.me>
In reply to#62138
(Sorry for the delay in replying to the posts in this thread - I have 
been mostly out of touch for the past few days.)

On 17/05/15 04:10, James Kuyper wrote:
> On 05/16/2015 02:51 AM, James Harris wrote:
>> "James Kuyper" <jameskuyper@verizon.net> wrote in message
>> news:5556657B.5050609@verizon.net...
>>> On 05/15/2015 04:56 PM, James Harris wrote:
>>>> "Stephen Sprunk" <stephen@sprunk.org> wrote in message
>>> ...
>>>> It is not interrupts which are the problem. If I understood him
>>>> correctly David mentioned that in
>>>>
>>>>    function_call()
>>>>    pointer_manipulations
>>>>    function_call()
>>>>
>>>> the compiler could move the pointer manipulations to before the
>>>> initial
>>>> function call or after the final function call. That would simply
>>>> break
>>>> the code.
>>>
>>> Are you sure he said that? I don't recall seeing him raise that issue.
>>

It /is/ roughly what I said.

>> No, I am not sure. The paragraph in question was
>>
>> DB> The key issue is that while the irqrx_set function is presumably
>> DB> volatile (or uses a volatile access to the irq flag), and thus the
>> DB> compiler guarantees that the interrupts will be disabled and then
>> DB> restored, the standards say nothing about the ordering of volatile
>> DB> accesses with respect to non-volatile accesses.  The compiler could
>> move
>> DB> the assignments to before the interrupt disable, or after the
>> restore,
>> DB> or in pretty much any other order it wants.
>>
>> It is in the post dated 14 May 2015 at 21:38 GMT. The code he was
>> commenting on was
>>
>>   void dnode_delete(dnode_t *x) {
>>      unsigned state;
>>
>>      /* Disable interrupts */
>>      state = irqrx_set(IRQRX_DISABLE);
>>
>>      x->prev.next = x.next;
>>      x->next.prev = x.prev;
>>
>>      /* Restore interrupt handling */
>>      irqrx_set(state);
>>   }
>>
>>> That can happen only if there's no sequence points separating those
>>> steps.
>>
>> That should be OK then. IIUC there is a sequence point before a function
>> call after the operands have been evaluated.
>
> More importantly, there's a sequence point at the end of every
> statement. That would be, in itself, sufficient to render his concerns
> irrelevant.
>

You are missing the "as-if" rule.  Yes, there are sequence points at the 
end of every statement.  But the compiler only has to honour them to the 
extent that the externally visible effects of the code are "as if" it 
had honoured them slavishly.

If the compiler knows that the irqrx_set function cannot affect, or be 
affected by, x, x->prev, x->next, etc., and these are not volatile (and 
therefore externally visible), then the compiler can re-order the 
statements pretty much as it likes.

Of course, this only applies if the compiler /has/ such knowledge.

[toc] | [prev] | [next] | [standalone]


#62260

FromJames Kuyper <jameskuyper@verizon.net>
Date2015-05-18 18:55 -0400
Message-ID<555A6DE5.2010303@verizon.net>
In reply to#62254
On 05/18/2015 06:25 PM, David Brown wrote:
...
> You are missing the "as-if" rule.  Yes, there are sequence points at the 
> end of every statement.  But the compiler only has to honour them to the 
> extent that the externally visible effects of the code are "as if" it 
> had honoured them slavishly.

So, why do you care? If you can write strictly conforming code whose
behavior would otherwise depend upon whether or not the compiler
performed a given optimization, then the as-if rule can not be used to
justify the optimization.

[toc] | [prev] | [next] | [standalone]


#62276

FromDavid Brown <david.brown@hesbynett.no>
Date2015-05-19 09:10 +0200
Message-ID<mjenju$353$1@dont-email.me>
In reply to#62260
On 19/05/15 00:55, James Kuyper wrote:
> On 05/18/2015 06:25 PM, David Brown wrote:
> ...
>> You are missing the "as-if" rule.  Yes, there are sequence points at the 
>> end of every statement.  But the compiler only has to honour them to the 
>> extent that the externally visible effects of the code are "as if" it 
>> had honoured them slavishly.
> 
> So, why do you care? If you can write strictly conforming code whose
> behavior would otherwise depend upon whether or not the compiler
> performed a given optimization, then the as-if rule can not be used to
> justify the optimization.
> 

The problem is that James had written code where the correctness of the
algorithm (i.e., the programmer's idea of correctness and order) depends
on an ordering that is not guaranteed by the rules of C.  Your comment
about sequence points makes it look like sequence points fully determine
ordering of statements in C, and that is simply wrong - they determine
only the "as-if" ordering of the virtual machine, not the real-world
ordering as run by the processor.  And James' code requires certain
ordering on the processor.  To specify that, he needs something stronger
than sequence points - and in C, that "something" is "volatile".

[toc] | [prev] | [next] | [standalone]


#62294

FromJames Kuyper <jameskuyper@verizon.net>
Date2015-05-19 12:05 -0400
Message-ID<555B5F39.1050807@verizon.net>
In reply to#62276
On 05/19/2015 03:10 AM, David Brown wrote:
> On 19/05/15 00:55, James Kuyper wrote:
>> On 05/18/2015 06:25 PM, David Brown wrote:
>> ...
>>> You are missing the "as-if" rule.  Yes, there are sequence points at the 
>>> end of every statement.  But the compiler only has to honour them to the 
>>> extent that the externally visible effects of the code are "as if" it 
>>> had honoured them slavishly.
>>
>> So, why do you care? If you can write strictly conforming code whose
>> behavior would otherwise depend upon whether or not the compiler
>> performed a given optimization, then the as-if rule can not be used to
>> justify the optimization.
>>
> 
> The problem is that James had written code where the correctness of the
> algorithm (i.e., the programmer's idea of correctness and order) depends
> on an ordering that is not guaranteed by the rules of C.  Your comment
> about sequence points makes it look like sequence points fully determine
> ordering of statements in C, and that is simply wrong - they determine
> only the "as-if" ordering of the virtual machine, not the real-world
> ordering as run by the processor.  And James' code requires certain
> ordering on the processor.  To specify that, he needs something stronger
> than sequence points - and in C, that "something" is "volatile".

Sequence points don't fully determine the ordering of statements in C, I
never suggested otherwise. They don't even fully determine the "as-if"
ordering, as you call it: in a()+b(), the two function calls can be
evaluated in either order - a conforming implementation must produce
results "as-if" one of those two orders were followed, but it's free to
choose either one. Also, the standard has always allowed for signal
handlers, and C2011 adds support for multi-threaded code, so a fully
conforming implementation of C can choose from a large number of
different "as-if" orders.

However, the subject of this thread has, from the beginning, been the
guarantees provided by the standard about two expressions separated by a
sequence point where the standard does guarantee the order of the two
sub-expressions. Using the new terms introduced in C2011, that means
that one expression is "sequenced after" (5.1.2.3p3) the other.

Now, you're right that it's only the as-if ordering that is guaranteed
by the standard. But, when one expression is, per the standard,
"sequenced after" another expression, the as-if ordering should be all
that matters, as far as strictly-conforming code is concerned, because
the "as-if" ordering is effectively the only ordering that can be seen
by such code, even if the actual ordering is quite different. If you
insert debugging fprintf()s in various locations in the code, and send
them to an unbuffered stream, the data received by that stream must
match the "as-if" order, even if the actual order in which the relevant
expressions were executed is quite different.

[toc] | [prev] | [next] | [standalone]


#62299

FromDavid Brown <david.brown@hesbynett.no>
Date2015-05-19 18:42 +0200
Message-ID<mjfp31$vgu$1@dont-email.me>
In reply to#62294
On 19/05/15 18:05, James Kuyper wrote:
> On 05/19/2015 03:10 AM, David Brown wrote:
>> On 19/05/15 00:55, James Kuyper wrote:
>>> On 05/18/2015 06:25 PM, David Brown wrote:
>>> ...
>>>> You are missing the "as-if" rule.  Yes, there are sequence points at the
>>>> end of every statement.  But the compiler only has to honour them to the
>>>> extent that the externally visible effects of the code are "as if" it
>>>> had honoured them slavishly.
>>>
>>> So, why do you care? If you can write strictly conforming code whose
>>> behavior would otherwise depend upon whether or not the compiler
>>> performed a given optimization, then the as-if rule can not be used to
>>> justify the optimization.
>>>
>>
>> The problem is that James had written code where the correctness of the
>> algorithm (i.e., the programmer's idea of correctness and order) depends
>> on an ordering that is not guaranteed by the rules of C.  Your comment
>> about sequence points makes it look like sequence points fully determine
>> ordering of statements in C, and that is simply wrong - they determine
>> only the "as-if" ordering of the virtual machine, not the real-world
>> ordering as run by the processor.  And James' code requires certain
>> ordering on the processor.  To specify that, he needs something stronger
>> than sequence points - and in C, that "something" is "volatile".
>
> Sequence points don't fully determine the ordering of statements in C, I
> never suggested otherwise. They don't even fully determine the "as-if"
> ordering, as you call it: in a()+b(), the two function calls can be
> evaluated in either order - a conforming implementation must produce
> results "as-if" one of those two orders were followed, but it's free to
> choose either one. Also, the standard has always allowed for signal
> handlers, and C2011 adds support for multi-threaded code, so a fully
> conforming implementation of C can choose from a large number of
> different "as-if" orders.
>
> However, the subject of this thread has, from the beginning, been the
> guarantees provided by the standard about two expressions separated by a
> sequence point where the standard does guarantee the order of the two
> sub-expressions. Using the new terms introduced in C2011, that means
> that one expression is "sequenced after" (5.1.2.3p3) the other.
>
> Now, you're right that it's only the as-if ordering that is guaranteed
> by the standard. But, when one expression is, per the standard,
> "sequenced after" another expression, the as-if ordering should be all
> that matters, as far as strictly-conforming code is concerned, because
> the "as-if" ordering is effectively the only ordering that can be seen
> by such code, even if the actual ordering is quite different. If you
> insert debugging fprintf()s in various locations in the code, and send
> them to an unbuffered stream, the data received by that stream must
> match the "as-if" order, even if the actual order in which the relevant
> expressions were executed is quite different.
>

What you write here is entirely correct - but it is mostly beside the 
point.  The thread has not been about the "as-if" ordering given by 
sequence points, but the /real/ ordering as seen by interrupt functions. 
  In James' original point, he was wondering if sequence points give 
guarantees about when an assignment actually causes a value to be 
written to memory (since his interrupt routine will see the value in 
memory, but not a value waiting in a register).  Sequence points are 
close to irrelevant here - they only matter in how they give an order to 
volatile accesses and other /visible/ effects (such as your fprintf's).


[toc] | [prev] | [next] | [standalone]


#62128

FromStephen Sprunk <stephen@sprunk.org>
Date2015-05-16 16:33 -0500
Message-ID<mj8d1g$qev$1@dont-email.me>
In reply to#62069
On 15-May-15 15:56, James Harris wrote:
> "Stephen Sprunk" <stephen@sprunk.org> wrote:
>> "Interrupts" are outside the scope of the C Standard, so any use of
>>  them will put you outside the realm of defined behavior.
> 
> It is not interrupts which are the problem. If I understood him 
> correctly David mentioned that in
> 
> function_call()
> pointer_manipulations
> function_call()
> 
> the compiler could move the pointer manipulations to before the
> initial function call or after the final function call. That would
> simply break the code.

Only if function_call() accessed that pointer, of course, and the
compiler _may_ be able to determine that it doesn't, depending on the
specific code at hand.  However, an interrupt routine may invalidate
that determination.

S

-- 
Stephen Sprunk         "God does not play dice."  --Albert Einstein
CCIE #3723         "God is an inveterate gambler, and He throws the
K5SSS        dice at every possible opportunity." --Stephen Hawking

[toc] | [prev] | [next] | [standalone]


#62255

FromDavid Brown <david.brown@hesbynett.no>
Date2015-05-19 00:27 +0200
Message-ID<mjdouv$gdj$2@dont-email.me>
In reply to#62128
On 16/05/15 23:33, Stephen Sprunk wrote:
> On 15-May-15 15:56, James Harris wrote:
>> "Stephen Sprunk" <stephen@sprunk.org> wrote:
>>> "Interrupts" are outside the scope of the C Standard, so any use of
>>>   them will put you outside the realm of defined behavior.
>>
>> It is not interrupts which are the problem. If I understood him
>> correctly David mentioned that in
>>
>> function_call()
>> pointer_manipulations
>> function_call()
>>
>> the compiler could move the pointer manipulations to before the
>> initial function call or after the final function call. That would
>> simply break the code.
>
> Only if function_call() accessed that pointer, of course, and the
> compiler _may_ be able to determine that it doesn't, depending on the
> specific code at hand.  However, an interrupt routine may invalidate
> that determination.
>

The compiler doesn't know about interrupts - only the programmer knows 
about them.  So if the compiler can determine that function_call() is 
independent of pointer_manipulations, but the programmer knows that 
interrupt routines may introduce a dependency, then it is the 
programmer's responsibility to tell this to the compiler by forcing a 
dependency in some way.  Valid methods include memory barriers and the 
use of "volatile".

[toc] | [prev] | [next] | [standalone]


#62264

FromJames Kuyper <jameskuyper@verizon.net>
Date2015-05-18 19:05 -0400
Message-ID<555A7049.6010405@verizon.net>
In reply to#62255
On 05/18/2015 06:27 PM, David Brown wrote:
...
> The compiler doesn't know about interrupts - only the programmer knows 
> about them.  So if the compiler can determine that function_call() is 
> independent of pointer_manipulations, but the programmer knows that 
> interrupt routines may introduce a dependency, then it is the 
> programmer's responsibility to tell this to the compiler by forcing a 
> dependency in some way.  Valid methods include memory barriers and the 
> use of "volatile".

On a platform which supports interrupts, a conforming implementation of
C must generate code that will still produce the behavior defined by the
standard for the source code. If that means spilling registers to memory
whenever a sequence point is crossed, even though an interrupt is the
only possible reason why such spilling is needed - then those registers
must be spilled - because the platform does permit such interruptions.

[toc] | [prev] | [next] | [standalone]


#62267

Fromglen herrmannsfeldt <gah@ugcs.caltech.edu>
Date2015-05-19 00:09 +0000
Message-ID<mjdv04$3k5$1@speranza.aioe.org>
In reply to#62264
James Kuyper <jameskuyper@verizon.net> wrote:

(snip)
> On a platform which supports interrupts, a conforming implementation of
> C must generate code that will still produce the behavior defined by the
> standard for the source code. If that means spilling registers to memory
> whenever a sequence point is crossed, even though an interrupt is the
> only possible reason why such spilling is needed - then those registers
> must be spilled - because the platform does permit such interruptions.

How about on CPUs that support imprecise interrupts?

-- glen

[toc] | [prev] | [next] | [standalone]


#62268

FromRichard Heathfield <rjh@cpax.org.uk>
Date2015-05-19 01:11 +0100
Message-ID<mjdv21$2qu$2@dont-email.me>
In reply to#62267
On 19/05/15 01:09, glen herrmannsfeldt wrote:

> How about on CPUs that support imprecise interrupts?

Excuse me, glen, but... um... never mind.

Oh, actually...

Er, no. Sorry.

-- 
Richard Heathfield
Email: rjh at cpax dot org dot uk
"Usenet is a strange place" - dmr 29 July 1999
Sig line 4 vacant - apply within

[toc] | [prev] | [next] | [standalone]


#62491

FromTim Rentsch <txr@alumni.caltech.edu>
Date2015-05-23 07:02 -0700
Message-ID<kfnfv6n8k9y.fsf@x-alumni2.alumni.caltech.edu>
In reply to#62268
Richard Heathfield <rjh@cpax.org.uk> writes:

> On 19/05/15 01:09, glen herrmannsfeldt wrote:
>
>> How about on CPUs that support imprecise interrupts?
>
> Excuse me, glen, but... um... never mind.
>
> Oh, actually...
>
> Er, no.  Sorry.

Precisely.

[toc] | [prev] | [next] | [standalone]


#62269

FromJames Kuyper <jameskuyper@verizon.net>
Date2015-05-18 23:21 -0400
Message-ID<mjea5a$v64$1@dont-email.me>
In reply to#62267
On 05/18/2015 08:09 PM, glen herrmannsfeldt wrote:
> James Kuyper <jameskuyper@verizon.net> wrote:
> 
> (snip)
>> On a platform which supports interrupts, a conforming implementation of
>> C must generate code that will still produce the behavior defined by the
>> standard for the source code. If that means spilling registers to memory
>> whenever a sequence point is crossed, even though an interrupt is the
>> only possible reason why such spilling is needed - then those registers
>> must be spilled - because the platform does permit such interruptions.
> 
> How about on CPUs that support imprecise interrupts?

I'm unfamiliar with that term. Is there any conceivable method to
generate code that has the behavior defined by the standard, even if
execution of that code is interrupted by imprecise interrupts? If so,
whatever approach needs to be taken to achieve that result is mandatory
for conforming implementations of C on those platforms. If not,
conforming implementations of C are not possible on those platforms.
-- 
James Kuyper

[toc] | [prev] | [next] | [standalone]


#62271

Fromglen herrmannsfeldt <gah@ugcs.caltech.edu>
Date2015-05-19 06:25 +0000
Message-ID<mjel16$ats$1@speranza.aioe.org>
In reply to#62269
James Kuyper <jameskuyper@verizon.net> wrote:

(snip)
>>> On a platform which supports interrupts, a conforming implementation of
>>> C must generate code that will still produce the behavior defined by the
>>> standard for the source code. 

(snip, then I wrote)
>> How about on CPUs that support imprecise interrupts?
 
> I'm unfamiliar with that term. Is there any conceivable method to
> generate code that has the behavior defined by the standard, even if
> execution of that code is interrupted by imprecise interrupts? 

Imprecise interrupts occur on processors with out-of-order execution
and, further, out-of-order retirement. (Many processors now have
out-of-order execution, but manage to arrange things such that the
effects happen in order.)

> If so,
> whatever approach needs to be taken to achieve that result is mandatory
> for conforming implementations of C on those platforms. If not,
> conforming implementations of C are not possible on those platforms.

I suspect that the causes of imprecise interrupts already cause UB,
such that it doesn't matter. They do make debugging harder, though.

Some causes of imprecise interrupts are overflow, underflow,
divide by zero, protection (accessing memory that doesn't belong
to you), unaligned memory access. When one of those occurs, the
CPU has to finish instructions that have already started, possibly
generaring more interrupts. 

But debuggers might not be able to report the cause of a protection
or alignment violation. 

Mostly, imprecise interrupts went away when vitual storage arrived.
If you can't identify the instruction causing a page fault, it isn't
possible to do the page-in and restart. Instructions can't change
any permanent state until it is known that no page fault will occur.

-- glen

[toc] | [prev] | [next] | [standalone]


Page 3 of 6 — ← Prev page 1 2 [3] 4 5 6  Next page →

Back to top | Article view | comp.lang.c


csiph-web