Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.std.c > #5989 > unrolled thread
| Started by | Philipp Klaus Krause <pkk@spth.de> |
|---|---|
| First post | 2019-10-25 12:45 +0200 |
| Last post | 2019-11-25 12:19 +0100 |
| Articles | 9 — 2 participants |
Back to article view | Back to comp.std.c
register at file scope Philipp Klaus Krause <pkk@spth.de> - 2019-10-25 12:45 +0200
Re: register at file scope David Brown <david.brown@hesbynett.no> - 2019-10-26 17:33 +0200
Re: register at file scope Philipp Klaus Krause <pkk@spth.de> - 2019-10-26 19:24 +0200
Re: register at file scope David Brown <david.brown@hesbynett.no> - 2019-10-27 12:39 +0100
Re: register at file scope Philipp Klaus Krause <pkk@spth.de> - 2019-10-28 12:22 +0100
Re: register at file scope David Brown <david.brown@hesbynett.no> - 2019-10-28 15:28 +0100
Re: register at file scope Philipp Klaus Krause <pkk@spth.de> - 2019-10-29 10:00 +0100
Re: register at file scope David Brown <david.brown@hesbynett.no> - 2019-10-29 13:46 +0100
Re: register at file scope Philipp Klaus Krause <pkk@spth.de> - 2019-11-25 12:19 +0100
| From | Philipp Klaus Krause <pkk@spth.de> |
|---|---|
| Date | 2019-10-25 12:45 +0200 |
| Subject | register at file scope |
| Message-ID | <qoujmq$bf1$1@solani.org> |
register allows the programmer to promise not to take the address of an object. IMO, being able to give this promise would be very useful for objects at file scope, especially, if these are shared across multiple translation units. After all with such an object declared as register, the compiler could be sure that no pointers that have been obtained from who-knows-whereever point to the object. http://colecovision.eu/stuff/proposal-register.html Philipp
[toc] | [next] | [standalone]
| From | David Brown <david.brown@hesbynett.no> |
|---|---|
| Date | 2019-10-26 17:33 +0200 |
| Message-ID | <qp1p09$f8l$1@dont-email.me> |
| In reply to | #5989 |
On 25/10/2019 12:45, Philipp Klaus Krause wrote: > register allows the programmer to promise not to take the address of an > object. IMO, being able to give this promise would be very useful for > objects at file scope, especially, if these are shared across multiple > translation units. > After all with such an object declared as register, the compiler could > be sure that no pointers that have been obtained from > who-knows-whereever point to the object. > > http://colecovision.eu/stuff/proposal-register.html > > Philipp > Just make the object static, and the compiler can quickly figure out if its address is taken or not. I don't see that making this explicit is worth the cost of having a keyword for the purpose. The "register" keyword in C has morphed into a pretty much useless feature (except when used in extensions to actually put objects in specific registers, such as gcc's "asm register" feature). It doesn't do what it says it does, since almost no serious compiler uses it as a "put this in a register" hint when optimising, and the compiler can already see if the address is taken. C++ has deprecated it (with the aim of reusing the word for something useful in the future), and I believe it is highly unlikely that its use would be extended in standard C.
[toc] | [prev] | [next] | [standalone]
| From | Philipp Klaus Krause <pkk@spth.de> |
|---|---|
| Date | 2019-10-26 19:24 +0200 |
| Message-ID | <qp1vg1$jr5$1@solani.org> |
| In reply to | #5998 |
Am 26.10.19 um 17:33 schrieb David Brown: > > Just make the object static, and the compiler can quickly figure out if > its address is taken or not. I don't see that making this explicit is > worth the cost of having a keyword for the purpose. Using static obviously won't work for objects used across multiple translation units, which is where allowing register would be the most useful. Philipp
[toc] | [prev] | [next] | [standalone]
| From | David Brown <david.brown@hesbynett.no> |
|---|---|
| Date | 2019-10-27 12:39 +0100 |
| Message-ID | <qp3vl6$j5h$1@dont-email.me> |
| In reply to | #6000 |
On 26/10/2019 19:24, Philipp Klaus Krause wrote: > Am 26.10.19 um 17:33 schrieb David Brown: >> >> Just make the object static, and the compiler can quickly figure out if >> its address is taken or not. I don't see that making this explicit is >> worth the cost of having a keyword for the purpose. > > Using static obviously won't work for objects used across multiple > translation units, which is where allowing register would be the most > useful. > I appreciate that - but it would surely be a tiny, tiny use-case. I can't see a language change being made to support a slight optimisation on a rare use-case. After all, what optimisations would actually be enabled here? When a file-static variable is known not to escape (it's address is not taken, or the address is not passed out of the unit) then the compiler can make various optimisations - it might eliminate it entirely, reduce its size, use value or range information to improve code, etc. When it is globally accessible in a way unknown to the compiler - any other unit might read it or change it - then it has lost this information. Taking its address does not make it lose more information. Can you give concrete examples of the kind of optimisation a compiler (even in theory) could make given that "int x;" is accessible from other independently compiled units but where the compiler knows the address of x will never be taken? Remember also that the trend in optimising compilers is towards cross-TU information exchange and optimisation (link-time optimisation, whole-program optimisation, omniscient compilation, and various other terms). Such cross-unit information exchange and optimisation provides improvements that subsume your suggestion here, and provide vastly more benefits in addition.
[toc] | [prev] | [next] | [standalone]
| From | Philipp Klaus Krause <pkk@spth.de> |
|---|---|
| Date | 2019-10-28 12:22 +0100 |
| Message-ID | <qp6j0d$kaq$1@solani.org> |
| In reply to | #6004 |
Am 27.10.19 um 12:39 schrieb David Brown:
>
> After all, what optimisations would actually be enabled here? When a
> file-static variable is known not to escape (it's address is not taken,
> or the address is not passed out of the unit) then the compiler can make
> various optimisations - it might eliminate it entirely, reduce its size,
> use value or range information to improve code, etc. When it is
> globally accessible in a way unknown to the compiler - any other unit
> might read it or change it - then it has lost this information. Taking
> its address does not make it lose more information.
>
> Can you give concrete examples of the kind of optimisation a compiler
> (even in theory) could make given that "int x;" is accessible from other
> independently compiled units but where the compiler knows the address of
> x will never be taken?
>
Consdier e.g.
int i;
void g(void);
void f(int *p)
{
if(i <= *p)
i = *p + 1;
if (*p & 0xa5)
…
}
If int i was declared a register int i, the compiler could assume that p
does not point at i, and thus could reuse the earlier value of *p in the
second if condition instead of having to reload from memory.
>
> Remember also that the trend in optimising compilers is towards cross-TU
> information exchange and optimisation (link-time optimisation,
> whole-program optimisation, omniscient compilation, and various other
> terms). Such cross-unit information exchange and optimisation provides
> improvements that subsume your suggestion here, and provide vastly more
> benefits in addition.
>
While LLVM is going that way, there is a large number of C
implementations (most timplementations tagret some small embedded
systems) out there, most of which probably don't have the manpower to
switch to whole-program optimization. There are still implementations
that work a function at a time, not even taking full advantage of static.
Philipp
[toc] | [prev] | [next] | [standalone]
| From | David Brown <david.brown@hesbynett.no> |
|---|---|
| Date | 2019-10-28 15:28 +0100 |
| Message-ID | <qp6tuh$14r$1@dont-email.me> |
| In reply to | #6007 |
On 28/10/2019 12:22, Philipp Klaus Krause wrote:
> Am 27.10.19 um 12:39 schrieb David Brown:
>>
>> After all, what optimisations would actually be enabled here? When a
>> file-static variable is known not to escape (it's address is not taken,
>> or the address is not passed out of the unit) then the compiler can make
>> various optimisations - it might eliminate it entirely, reduce its size,
>> use value or range information to improve code, etc. When it is
>> globally accessible in a way unknown to the compiler - any other unit
>> might read it or change it - then it has lost this information. Taking
>> its address does not make it lose more information.
>>
>> Can you give concrete examples of the kind of optimisation a compiler
>> (even in theory) could make given that "int x;" is accessible from other
>> independently compiled units but where the compiler knows the address of
>> x will never be taken?
>>
>
> Consdier e.g.
>
> int i;
>
> void g(void);
>
> void f(int *p)
> {
> if(i <= *p)
> i = *p + 1;
>
> if (*p & 0xa5)
> …
> }
>
> If int i was declared a register int i, the compiler could assume that p
> does not point at i, and thus could reuse the earlier value of *p in the
> second if condition instead of having to reload from memory.
>
You can easily achieve that in C today by storing *p in a local variable:
void f(int *p)
{
int x = *p;
if(i <= x)
i = x + 1;
if (x & 0xa5)
…
}
You can also make p "restrict".
And, again, you could make "i" static to the file - something that is
generally considered good style anyway.
So we are talking about a rather minor improvement to rare uses in code
where the same efficiency can be achieved in other ways without
significant effort. I don't see that being good enough to justify
modifying a keyword that is, I believe, more likely to be deprecated
than expanded.
If you could argue a significant benefit to code safety, I'd be a lot
more supportive.
>>
>> Remember also that the trend in optimising compilers is towards cross-TU
>> information exchange and optimisation (link-time optimisation,
>> whole-program optimisation, omniscient compilation, and various other
>> terms). Such cross-unit information exchange and optimisation provides
>> improvements that subsume your suggestion here, and provide vastly more
>> benefits in addition.
>>
>
> While LLVM is going that way, there is a large number of C
> implementations (most timplementations tagret some small embedded
> systems) out there, most of which probably don't have the manpower to
> switch to whole-program optimization. There are still implementations
> that work a function at a time, not even taking full advantage of static.
>
For the compilers with limited optimisation, the issue is irrelevant
anyway - they are not going to support optimisations allowed by this
enhancement when there are huge areas of improvement open already.
Whole-program optimisation has been available in the embedded world from
before LLVM was started. It is used in the top range (and top price)
compilers from folks like IAR, ByteCraft and Keil. It has also been
available in more limited ways from other compilers for a long time -
gcc has had "-fwhole-program" for a good many years.
The embedded toolchain arena is divided into four groups. There is the
"cheap and cheerful" group, where ease of use and customer service are
prioritised far higher than optimal code generation. Such tools will
not support a change like this - many barely support C99. And they
won't support the optimisations it makes available anyway.
There are legacy compilers, or limited compilers for legacy devices -
clearly these will not support a new feature.
There are the top-end compilers - these support whole-program
optimisation, and have no need of such enhancements.
And then there is gcc (along with a small proportion of clang usage),
which also supports whole-program optimisations for those that are
aiming for the very best efficiency.
I simply don't see a significant area of toolchain suppliers that would
be able to generate more efficient code with this feature, and which
would actually do so if this feature were standardised. And thus I also
don't see a significant user-base for the feature.
If there are compilers for which it might be useful, I think they would
be better served with a compiler extension for the purpose.
[toc] | [prev] | [next] | [standalone]
| From | Philipp Klaus Krause <pkk@spth.de> |
|---|---|
| Date | 2019-10-29 10:00 +0100 |
| Message-ID | <qp8v2t$6hf$1@solani.org> |
| In reply to | #6008 |
Am 28.10.19 um 15:28 schrieb David Brown:
>
> You can easily achieve that in C today by storing *p in a local variable:
>
> void f(int *p)
> {
> int x = *p;
> if(i <= x)
> i = x + 1;
>
> if (x & 0xa5)
> …
> }
Yes, but it somtimes makes the code less readable, especially, if
multiple pointers / arrays are involved
>
> You can also make p "restrict".
In this simple case yes. But in real-worls examples there can be
multiple pointers and some of them might indeed be allowed to point to
the same memory.
>
> And, again, you could make "i" static to the file - something that is
> generally considered good style anyway.
>
Obviously variables at file scope are _not_ declared static, when they
are indeed accessed from other translation units.
That might not be considered good style, but is still very common when
programming embedded systems..
>
> So we are talking about a rather minor improvement to rare uses in code
> where the same efficiency can be achieved in other ways without
> significant effort. I don't see that being good enough to justify
> modifying a keyword that is, I believe, more likely to be deprecated
> than expanded.
Better alias analysis can help with many optimizations. Use of global
variables is still common in small embedded systems - in some ways the
dveices, where optimization matters the most, due to tight memory,
energy and runtime constraints.
>
> If you could argue a significant benefit to code safety, I'd be a lot
> more supportive.
>
My motivation is optimization. By giving hte compiler morre information,
which of course, can also be used for diagnostics.
>
>>>
>>> Remember also that the trend in optimising compilers is towards cross-TU
>>> information exchange and optimisation (link-time optimisation,
>>> whole-program optimisation, omniscient compilation, and various other
>>> terms). Such cross-unit information exchange and optimisation provides
>>> improvements that subsume your suggestion here, and provide vastly more
>>> benefits in addition.
>>>
>>
>> While LLVM is going that way, there is a large number of C
>> implementations (most timplementations tagret some small embedded
>> systems) out there, most of which probably don't have the manpower to
>> switch to whole-program optimization. There are still implementations
>> that work a function at a time, not even taking full advantage of static.
>>
>
> For the compilers with limited optimisation, the issue is irrelevant
> anyway - they are not going to support optimisations allowed by this
> enhancement when there are huge areas of improvement open already.
>
> Whole-program optimisation has been available in the embedded world from
> before LLVM was started. It is used in the top range (and top price)
> compilers from folks like IAR, ByteCraft and Keil. It has also been
> available in more limited ways from other compilers for a long time -
> gcc has had "-fwhole-program" for a good many years.
>
> The embedded toolchain arena is divided into four groups. There is the
> "cheap and cheerful" group, where ease of use and customer service are
> prioritised far higher than optimal code generation. Such tools will
> not support a change like this - many barely support C99. And they
> won't support the optimisations it makes available anyway.
>
> There are legacy compilers, or limited compilers for legacy devices -
> clearly these will not support a new feature.
>
> There are the top-end compilers - these support whole-program
> optimisation, and have no need of such enhancements.
>
> And then there is gcc (along with a small proportion of clang usage),
> which also supports whole-program optimisations for those that are
> aiming for the very best efficiency.
>
Where would SDCC fit into your picture?
>
> I simply don't see a significant area of toolchain suppliers that would
> be able to generate more efficient code with this feature, and which
> would actually do so if this feature were standardised. And thus I also
> don't see a significant user-base for the feature.
>
> If there are compilers for which it might be useful, I think they would
> be better served with a compiler extension for the purpose.
Introducing this as a compiler extension first and also evaluate the
impact of placing register in appropriate places in some real-world code
or a benchmark would indeed make sense.
Philipp
[toc] | [prev] | [next] | [standalone]
| From | David Brown <david.brown@hesbynett.no> |
|---|---|
| Date | 2019-10-29 13:46 +0100 |
| Message-ID | <qp9cbq$aif$1@dont-email.me> |
| In reply to | #6012 |
On 29/10/2019 10:00, Philipp Klaus Krause wrote:
> Am 28.10.19 um 15:28 schrieb David Brown:
>>
>> You can easily achieve that in C today by storing *p in a local variable:
>>
>> void f(int *p)
>> {
>> int x = *p;
>> if(i <= x)
>> i = x + 1;
>>
>> if (x & 0xa5)
>> …
>> }
>
> Yes, but it somtimes makes the code less readable, especially, if
> multiple pointers / arrays are involved
>
Often in such cases the use of an extra local variable makes the code
/more/ readable, because you have less repetition of a complex
expression for accessing the data via the pointer or array.
But these are just vague generalisations - the details of what is most
"readable" varies from case to case.
Still, we are talking about a minor micro-optimisation here. If you are
writing code for which such optimisations are relevant, then it is
acceptable to have code that is less clear to understand. Different
ways of writing code have different balances of things like readability,
portability, efficiency, etc. - you pick according to need.
>>
>> You can also make p "restrict".
>
> In this simple case yes. But in real-worls examples there can be
> multiple pointers and some of them might indeed be allowed to point to
> the same memory.
>
>>
>> And, again, you could make "i" static to the file - something that is
>> generally considered good style anyway.
>>
>
> Obviously variables at file scope are _not_ declared static, when they
> are indeed accessed from other translation units.
> That might not be considered good style, but is still very common when
> programming embedded systems..
>
Another feature of such embedded programming is that it makes far less
use of pointers than "normal" C programming. This is due to a number of
factors - you generally have smaller data sets, you rarely have dynamic
memory, and small embedded cpus are often significantly more efficient
at accessing data directly from fixed linker-assigned addresses than
indirectly via pointers.
>>
>> So we are talking about a rather minor improvement to rare uses in code
>> where the same efficiency can be achieved in other ways without
>> significant effort. I don't see that being good enough to justify
>> modifying a keyword that is, I believe, more likely to be deprecated
>> than expanded.
>
> Better alias analysis can help with many optimizations. Use of global
> variables is still common in small embedded systems - in some ways the
> dveices, where optimization matters the most, due to tight memory,
> energy and runtime constraints.
>
If you had been suggesting this 20 years ago, or even 10 years ago, when
it still made sense to pick limited 8-bit CISC microcontrollers with
simple compilers, it might have been worth considering. But these days
if you want the smallest, cheapest, lowest power devices, you probably
have a 32-bit core - and you can use a compiler that will do cross-unit
optimisation if you want.
Think about the timing required for making a modification to the C
standards and having it implemented in toolchains. You can expect years
between a proposal and the new feature being in the standard. It took 7
years between C11 and C18, and C18 contains no new features - just a few
corrections (and an improved layout and document format). Then how long
will it be before the feature is supported in these small, limited
compilers? Another few years, if ever. Then they would need to
implement optimisations based on the feature - that is hardly likely to
be a priority for them. Let's be optimistic and say you can expect the
standardised feature and optimisation to be available in small device
compilers in 10 years. What kind of microcontrollers do you think will
be popular by then? How many will be programmed using something other
than a compiler that can already handle this kind of optimisation today?
>>
>> If you could argue a significant benefit to code safety, I'd be a lot
>> more supportive.
>>
>
> My motivation is optimization. By giving hte compiler morre information,
> which of course, can also be used for diagnostics.
How about a different idea - one that can allow similar (or more)
optimisations, give the reader useful information as well as the
compiler, and improve code safety in many cases (especially the kind of
embedded systems under discussion)?
Very often when you have "global variables" - objects that are defined
in a file "unit.c" as non-static, and where "unit.h" has an extern
declaration - you want to data to be read-only from outside its owning
unit. So in "unit.h" you could have:
extern const int counter;
This means that other units can read "counter", but can't change it.
However, if "unit.c" has an #include "unit.h", as is standard practice
and helps spot mistakes, it cannot then define "int counter;" as the
qualifiers don't match. And if it defines "const int counter;", there
is no way for the unit to change the value of the object.
My suggestion then is to have a "mutable" keyword, copied from C++ as a
way to say "I know this is declared "const", but I want to change it
anyway":
mutable int counter;
This would make counter modifiable within the unit, even if the previous
"extern const int counter" has been seen. It would give the same
meaning as if the compiler had simply seen "int counter;" and not the
"extern const int counter;" declaration.
The result would be that the user of "unit" gets to view the object, but
not change it, improving safety and allowing some optimisations. The
owning unit can modify the value as needed. And you don't have to play
risky tricks with the pre-processor to make it all work.
It would give you at least some of the optimisation benefits found in
your suggestion, have wider use, and be helpful even for whole-program
optimising compilers.
>
>>
>>>>
>>>> Remember also that the trend in optimising compilers is towards cross-TU
>>>> information exchange and optimisation (link-time optimisation,
>>>> whole-program optimisation, omniscient compilation, and various other
>>>> terms). Such cross-unit information exchange and optimisation provides
>>>> improvements that subsume your suggestion here, and provide vastly more
>>>> benefits in addition.
>>>>
>>>
>>> While LLVM is going that way, there is a large number of C
>>> implementations (most timplementations tagret some small embedded
>>> systems) out there, most of which probably don't have the manpower to
>>> switch to whole-program optimization. There are still implementations
>>> that work a function at a time, not even taking full advantage of static.
>>>
>>
>> For the compilers with limited optimisation, the issue is irrelevant
>> anyway - they are not going to support optimisations allowed by this
>> enhancement when there are huge areas of improvement open already.
>>
>> Whole-program optimisation has been available in the embedded world from
>> before LLVM was started. It is used in the top range (and top price)
>> compilers from folks like IAR, ByteCraft and Keil. It has also been
>> available in more limited ways from other compilers for a long time -
>> gcc has had "-fwhole-program" for a good many years.
>>
>> The embedded toolchain arena is divided into four groups. There is the
>> "cheap and cheerful" group, where ease of use and customer service are
>> prioritised far higher than optimal code generation. Such tools will
>> not support a change like this - many barely support C99. And they
>> won't support the optimisations it makes available anyway.
>>
>> There are legacy compilers, or limited compilers for legacy devices -
>> clearly these will not support a new feature.
>>
>> There are the top-end compilers - these support whole-program
>> optimisation, and have no need of such enhancements.
>>
>> And then there is gcc (along with a small proportion of clang usage),
>> which also supports whole-program optimisations for those that are
>> aiming for the very best efficiency.
>>
>
> Where would SDCC fit into your picture?
It is "cheap and cheerful", as far as I know (though I have not used it
myself). As I understand it, it is fine for a lot of tasks but it does
not compare well to Keil or IAR in terms of features or optimisation
(while thrashing them in value for money, and you don't have to fight
with licensing systems, dongles, and so on).
Being open source, it is more likely for SDCC to start supporting a new
feature if there is someone who wants it and is willing and able to
change the compiler. But the same applies to making the feature as a
compiler-specific extension, which would be very much more efficient as
it avoids all the standardisation bureaucracy.
>
>>
>> I simply don't see a significant area of toolchain suppliers that would
>> be able to generate more efficient code with this feature, and which
>> would actually do so if this feature were standardised. And thus I also
>> don't see a significant user-base for the feature.
>>
>> If there are compilers for which it might be useful, I think they would
>> be better served with a compiler extension for the purpose.
>
> Introducing this as a compiler extension first and also evaluate the
> impact of placing register in appropriate places in some real-world code
> or a benchmark would indeed make sense.
>
There is also the fact that the C (and C++) standardisation committees
are a lot happier to see proposed new features that have been proven in
the field, with implementations in a compiler, used in real code - that
would show that the feature can be implemented and is useful to users.
[toc] | [prev] | [next] | [standalone]
| From | Philipp Klaus Krause <pkk@spth.de> |
|---|---|
| Date | 2019-11-25 12:19 +0100 |
| Message-ID | <qrgdc1$qv7$1@solani.org> |
| In reply to | #6012 |
Am 29.10.19 um 10:00 schrieb Philipp Klaus Krause: > Introducing this as a compiler extension first and also evaluate the > impact of placing register in appropriate places in some real-world code > or a benchmark would indeed make sense. > > Philipp > I did a first experiment; there, the improvements in benchmarks are far less than what one sees in synthetic examples. Experiment: * SDCC version based on current SDCC with support for register at file scope added. For now, the information is used in two places in redundancy elimination only. * Annotated file scope variables in 4 benchmarks (Coremark, Dhrystone, stdcbench, Whetstone) with register storage class where appropriate. * Compiled the benchmarks for STM8 with strong optimization (--max-allocs-per-node 100000), using default balanced optimization goal. Results: * stdcbench, Coremark: No difference at all. No surprise, since they both use very few file scope variables, and typically not in the inner loops. * Whetstone: 0.8 % increase in score, 0.3 % reduction in code size. Whtstone uses some file scope variables, but doesn't do much with pointers, so no surprise here, either. * Dhrystone: 1.4 % increase in score, 0.1 % increase in code size. IMO, 1.4 % is a surprisingly large increase, considering that Dhrystone tends to spent most of its time in string functions from the standard library, which are not affected by the change at all. Philipp
[toc] | [prev] | [standalone]
Back to top | Article view | comp.std.c
csiph-web