Is there a way to flush registers and tell the C compiler to refill?

  • Thread starter Emmanuel Stapf [ES]
  • Start date
E

Emmanuel Stapf [ES]

Hi,

Because using volatile would prevent some C compilation optimization,
I'm looking at an alternative where I would inform the C compiler that
from known points in my program code, no values are in registers and
thus the generated code should refetch the data from memory (and then
use register whenever it can).

For example,

struct complex_struct s {
int value;
};

void f () {

int i;

i = s.value + s.value; /* 1 */

routine_that_will_indirectly_modify_s_value(); /* 2 */

i = s.value + s.value; /* 3 */

}

If the C compiler had optimized the access to `s.value' in a register in
#1, I want to prevent the C compiler from using that register in #3
because the value was externally modified in #2.

If I was making volatile, then all accesses to `s.value' would be
penalized and I don't want that.

Thanks for the help,
Manu
 
E

Emmanuel Stapf [ES]

Eric said:
It is the compiler's business to make sure that any modifications
routine...() might make to `s' will be visible to the rest of the
program. If the compiler allows /* 3 */ to ignore the new value of `s'
and use a "stale" value instead, the compiler is broken.

Will you consider that a C compiler should always refetch `s.value'
after #2 in this slightly modified sample?

void f () {

int i;
struct complex_struct s {
int value;
};

routine_storing_address_of_struct (&s);

i = s.value + s.value; /* 1 */

routine_modifying_s_value_via_recorded_address_above(); /* 2 */

i = s.value + s.value; /* 3 */

}

Thanks,
Manu
 
L

Lew Pitcher

Hi,

Because using volatile would prevent some C compilation optimization,
I'm looking at an alternative where I would inform the C compiler that
from known points in my program code, no values are in registers and
thus the generated code should refetch the data from memory (and then
use register whenever it can).

For example,

struct complex_struct s {
int value;
};

A question to the experts here...

Is the above declaration even legal? Does it declare s to be of type "struct
complex_struct"?

AFAICT, the syntax of the declaration of s should be
struct complex_type { int value; } s;
conforming to the pattern of
<type-specifier> <declarator>
where
<type-specifier>
is a
<struct-or-union-specifier>
consisting of
struct <identifier> { <struct-declaration-list> }

The given declaration does not match the <struct-or-union-specifier>, in
that it contains _two_ <identifier>s. Further, the whole statement does not
conform to the <type-specifier> <declarator> pattern, as it lacks the
<declarator> that follows the completed <type-specifier>.

Is my understanding correct?

--
Lew Pitcher

Master Codewright & JOAT-in-training | Registered Linux User #112576
http://pitcher.digitalfreehold.ca/ | GPG public key available by request
---------- Slackware - Because I know what I'm doing. ------
 
E

Emmanuel Stapf [ES]

Eric said:
Lew said:
Hi,

Because using volatile would prevent some C compilation optimization,
I'm looking at an alternative where I would inform the C compiler that
from known points in my program code, no values are in registers and
thus the generated code should refetch the data from memory (and then
use register whenever it can).

For example,

struct complex_struct s {
int value;
};

A question to the experts here...

Is the above declaration even legal? Does it declare s to be of type
"struct
complex_struct"?
[...]

You're right: It's illegal (barring preprocessor tricks).
I'm sorry to say that I skimmed straight past it, reading it
as `struct complex_struct {...} s;', which is what I imagine
the O.P. actually intended. Good catch.

My bad indeed.

Manu
 
W

Walter Banks

Emmanuel Stapf said:
Hi,

Because using volatile would prevent some C compilation optimization,
I'm looking at an alternative where I would inform the C compiler that
from known points in my program code, no values are in registers and
thus the generated code should refetch the data from memory (and then
use register whenever it can).

For example,

struct complex_struct s {
int value;
};

void f () {

int i;

i = s.value + s.value; /* 1 */

routine_that_will_indirectly_modify_s_value(); /* 2 */

i = s.value + s.value; /* 3 */

}

If the C compiler had optimized the access to `s.value' in a register in
#1, I want to prevent the C compiler from using that register in #3
because the value was externally modified in #2.

If I was making volatile, then all accesses to `s.value' would be
penalized and I don't want that.

The data flow analysis should determine if the s is modified in
routine_that_will_indirectly_modify_s_value which will flag the
s.value as unknown after the function call. That would force
s.value to be reloaded. In your simple example it is entirely possible
that no code would be generated for #1 because i is not
read until it is re-assigned.

It would be rare for the global variable s to be allocated to
processor registers in anything other than in trivial cases.
"i" on the other hand may very likely be allocated to a register
possibly two different registers in many cases for #1 and #3
assuming a slightly more complex program that forced #1
to generate code, for example passing i to the function a
case that most likely would optimize the write to "i" in
#1.

i = s.value + s.value; /* 1 */
routine_that_will_indirectly_modify_s_value(i); /* 2 */
i = s.value + s.value; /* 3 */

The short answer is I don't think in this example there is much
danger the variable to register optimization in the compiler
will cause undesired program behaviour.

Regards,
 
K

Kaz Kylheku

Hi,

Because using volatile would prevent some C compilation optimization,
I'm looking at an alternative where I would inform the C compiler that
from known points in my program code, no values are in registers and
thus the generated code should refetch the data from memory (and then
use register whenever it can).

There is no such interface in C. But using external functions may achieve this
on many compilers.
For example,

struct complex_struct s {
int value;
};

you mean:

struct complex_struct { ... } s;
void f () {

int i;

i = s.value + s.value; /* 1 */

routine_that_will_indirectly_modify_s_value(); /* 2 */

i = s.value + s.value; /* 3 */

}

Note that if the routine modifes s in a correct, well-defined way, then there
is nothing to worry about (other than a broken compiler). Such a caching
optimization applied blindly is forbidden. A sequence point occurs immediately
before the function call, and the function must see the stable value of object
s. A sequence point occurs before the function returns also.

So even if the aggressive caching is done, it must be done in such a way that
the routine can see the cached value. If it cannot be ensured that the routine
can used the cached value, then the caching cannot be done.

But the name of your function hints at the fact that s is somehow manipulated
indirectly, like through some aliased pointer or some such thing. In that case,
you are on your own, because the behavior is undefined. The language has no
interface by which you can say ``please do not cache this value''.

However, what you may be able to rely on is making the function external to
your translation unit, and also giving the variable s external linkage.
These facts taken together will forbid most compilers from making any caching
optimizations on s.

This is because the external function has access to the variable and can change
it arbitrarily.

Caching s in spite of this would require global program optimization: code
optimization and generation would have to be delayed until the point when the
program is being linked, at which time it could be proven that the external
function does not touch the variable, and the code could be optimized
accordingly before the final link.

Unless you have this kind of toolchain, this solution should be good enough.
Most C compilers do a more or less complete job of generating the code in
complete isolation, and the final linking stages only resolve the external
references to objects and functions, without rewriting any code in any
significant way.
If I was making volatile, then all accesses to `s.value' would be
penalized and I don't want that.

volatile is a poorly-defined stupidity which should be confined to only the
documented, required ISO C uses: declaring the type of a flag set by an
asynchronous signal handler to be of type ``volatile sig_atomic_t'',
and applying the volatile qualification on local variables that are modified
between setjmp and longjmp whose values are used after the longjmp.
volatile has no other uses that can be relied upon to be portable. For any
specific assurances, you want to use the appropriate compiler extension, if you
have it, or write in assembly language, etc.
 
R

robertwessel2

Hi,

Because using volatile would prevent some C compilation optimization,
I'm looking at an alternative where I would inform the C compiler that
from known points in my program code, no values are in registers and
thus the generated code should refetch the data from memory (and then
use register whenever it can).

For example,

struct complex_struct s {
        int value;

};

void f () {

        int i;

        i = s.value + s.value; /* 1 */

        routine_that_will_indirectly_modify_s_value(); /* 2 */

        i = s.value + s.value; /* 3 */

}

If the C compiler had optimized the access to `s.value' in a register in
#1, I want to prevent the C compiler from using that register in #3
because the value was externally modified in #2.

If I was making volatile, then all accesses to `s.value' would be
penalized and I don't want that.


One thing you can do is have a second copy of s.value, and use only
those in the majority of your program. Make the original s.value
volatile, and then copy any updates to both places, and in all the
places you are proposing to flush registers, copy the original s.value
to the working copy. Most compilers will probably generate a few
additional stores for that code, but probably still better than doing
all the loads forced by making the (only) copy of the variable
volatile.

If the variable is basically read only except in
routine_that_will_indirectly_modify_s_value(), you basically need to
load the working copy once at startup (by copying from the base copy),
and then the assignments in r_t_w_i_m_s_v() will take care of the
rest.
 
K

Kaz Kylheku

I do not think that volatile has the same meaning to hardware that it
used to

Used to? When was this, and what was the meaning?
the multilevel caches change the fundamentals of the mapping
between registers and memory that there used to be.

Not from the point of view of a single processor, or even multiple processors
when cache coherency is maintained. Also, memory-mapped I/O is typically in
regions designated as uncached.

But none of that changes the reality that volatile is a largely meaningless
dongle in the language, and always has been.
 
E

Emmanuel Stapf [ES]

One thing you can do is have a second copy of s.value, and use only
those in the majority of your program. Make the original s.value
volatile, and then copy any updates to both places, and in all the
places you are proposing to flush registers, copy the original s.value
to the working copy. Most compilers will probably generate a few
additional stores for that code, but probably still better than doing
all the loads forced by making the (only) copy of the variable
volatile.

This is a good idea. I'll see what the trade off is.

Regards,
Manu
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top