Author Topic: GCC ARM32 compiler too clever, or not clever enough?  (Read 13811 times)

0 Members and 1 Guest are viewing this topic.

Offline newbrain

  • Super Contributor
  • ***
  • Posts: 1719
  • Country: se
Re: GCC ARM32 compiler too clever, or not clever enough?
« Reply #25 on: April 16, 2022, 11:06:18 pm »
Basically this IDE stuff is a tool which got put together by real men, for real men, and anybody who doesn't like it gets beaten up :)
Which you summed up beautifully in "it's a piece of shit".

Neither VS Code, nor full Visual Studio do this. They'll tell they can't find a definition for 'int'.
Moreover, they understand compiler defines and #ifdef, if used correctly, and filter the result accordingly.

With Eclipse, it was always hit and miss.

Nandemo wa shiranai wa yo, shitteru koto dake.
 

Offline brucehoult

  • Super Contributor
  • ***
  • Posts: 4028
  • Country: nz
Re: GCC ARM32 compiler too clever, or not clever enough?
« Reply #26 on: April 17, 2022, 12:59:05 am »
I just don't have time to become an expert in all this.

You're digging into corners that experts don't dig into. At least not microcontroller programming experts. Runtime library and compiler experts maybe, but that's a very different field, with (mostly) different experts.

C has always had the "sizeof" built in function. Just use it.
 

Offline TheCalligrapher

  • Regular Contributor
  • *
  • Posts: 151
  • Country: us
Re: GCC ARM32 compiler too clever, or not clever enough?
« Reply #27 on: April 17, 2022, 01:23:04 am »
C has always had the "sizeof" built in function. Just use it.

`sizeof` is an operator, not a function.
 
The following users thanked this post: newbrain

Offline brucehoult

  • Super Contributor
  • ***
  • Posts: 4028
  • Country: nz
Re: GCC ARM32 compiler too clever, or not clever enough?
« Reply #28 on: April 17, 2022, 02:53:25 am »
C has always had the "sizeof" built in function. Just use it.

`sizeof` is an operator, not a function.

I win my bet.
 

Offline peter-hTopic starter

  • Super Contributor
  • ***
  • Posts: 3694
  • Country: gb
  • Doing electronics since the 1960s...
Re: GCC ARM32 compiler too clever, or not clever enough?
« Reply #29 on: April 17, 2022, 08:11:31 am »
Now look at what I've done :) :) :)
Z80 Z180 Z280 Z8 S8 8031 8051 H8/300 H8/500 80x86 90S1200 32F417
 

Online Nominal Animal

  • Super Contributor
  • ***
  • Posts: 6241
  • Country: fi
    • My home page and email address
Re: GCC ARM32 compiler too clever, or not clever enough?
« Reply #30 on: April 17, 2022, 03:11:15 pm »
Why do you use MMDDYYYY hhmmss format? ISO 8601 defined YYYY-MM-DD hh:mm:ss  (although 8601-1:2019 made the T prefix for time mandatory for some reason, making it YYYY-MM-DDThh:mm:ss which I do not recommed).

The reason the YYYY-MM-DD hh:mm:ss is superior is that not only is it unambiguous, it is internationally known; but most importantly, it sorts correctly without any extra work.  (A plain ASCII/ISO Latin/Windows 457/1252/Unicode/UTF-8 sort will sort them correctly.)

Furthermore, if your date and time fields may have extra flag bits, use the & (binary and) operator to pick only the useful bits, and emit them as unsigned integers:
Code: [Select]
        // minimum size for dtbuf is 4+1+2+1+2+1+2+1+2+1+2 +1 = 20 chars
        snprintf(dtbuf, sizeof dtbuf, "%04u-%02u-%02u %02u:%02u:%02u",
            (boot_time.tm_year & 4095) + 1900,
            (boot_time.tm_mon & 15) + 1,
            (boot_time.tm_mday & 31),
            (boot_time.tm_hour & 31),
            (boot_time.tm_min & 63),
            (boot_time.tm_sec & 63));
(I don't recall the exact details, but I believe there may have been some RTC chips where one of the fields' most significant bit acted as a user-DST flag or something.  I do not think that is the case here, though.)

As pointed out above by nctnico, snprintf() always terminates the buffer with a nul char (\0 - that's what the final +1 is in the dtbuf size calculation).  If the string does not fit the given buffer, it will simply terminate it early, and return the actual number of chars it would have stored if there had been room (but excluding the final nul char, so you actually need room for one more).

The above code, assuming a sane snprintf() implementation, will never cause a buffer overrun; not even if you accidentally or on purpose change the size of the dtbuf array.  In your own version, there is a risk, if you reduce the size of the array to below 16, but forget to update the size in the snprintf() correspondingly.
 

Offline peter-hTopic starter

  • Super Contributor
  • ***
  • Posts: 3694
  • Country: gb
  • Doing electronics since the 1960s...
Re: GCC ARM32 compiler too clever, or not clever enough?
« Reply #31 on: April 17, 2022, 06:30:49 pm »
You are absolutely right; I forgot that "standard" :)

Code: [Select]
struct tm boot_time;
  char dtbuf [20];
getrtc (&boot_time);
snprintf( dtbuf,sizeof(dtbuf),"%04u-%02u-%02u %02u:%02u:%02u", \
(boot_time.tm_year+1900)%2100, (boot_time.tm_mon+1)%13, (boot_time.tm_mday)%32, \
(boot_time.tm_hour)%24, (boot_time.tm_min)%60, (boot_time.tm_sec)%60 );

2022-04-17 18:34:32

If I did

Code: [Select]
snprintf( dtbuf,19,"%04u-%02u-%02u %02u:%02u:%02u"
the compiler did notice that.



The structure tm has no extra bits; it is just clean ints. The RTC registers get converted to tm with another chunk of code.

I am not implementing daylight savings; that (local time) is a complete nightmare in an embedded system. Even having GPS location is no good. Phones do it by obtaining the GSM tower local time (which obviously fails when near some borders) :) PCs and such implement it by periodically downloading the huge table of rules.
« Last Edit: April 17, 2022, 06:39:59 pm by peter-h »
Z80 Z180 Z280 Z8 S8 8031 8051 H8/300 H8/500 80x86 90S1200 32F417
 

Online Nominal Animal

  • Super Contributor
  • ***
  • Posts: 6241
  • Country: fi
    • My home page and email address
Re: GCC ARM32 compiler too clever, or not clever enough?
« Reply #32 on: April 19, 2022, 07:14:33 pm »
Code: [Select]
snprintf( dtbuf,sizeof(dtbuf),"%04u-%02u-%02u %02u:%02u:%02u", \
(boot_time.tm_year+1900)%2100, (boot_time.tm_mon+1)%13, (boot_time.tm_mday)%32, \
(boot_time.tm_hour)%24, (boot_time.tm_min)%60, (boot_time.tm_sec)%60 );
Those particular modulo operators can be slow, and the result is still signed (assuming signed struct tm members as is typical), which is why I do recommend using binary and (&) and powers-of-two-less-one instead.

While it may not affect you right now, also note that due to leap seconds, the valid range of the seconds field is actually 0 to 60, inclusive.

Thus, I do recommend you change that into say
Code: [Select]
snprintf(dtbuf, sizeof dtbuf, "%04u-%02u-%02u %02u:%02u:%02u", \
(boot_time.tm_year+1900)&4095, (boot_time.tm_mon+1)&15, (boot_time.tm_mday)&31, \
(boot_time.tm_hour)&31, (boot_time.tm_min)&63, (boot_time.tm_sec)&63);
If you look at the compiled code, you'll see that these simplify to just one or two instructions per member.  Invalid values will output slightly different values then, but it will always stay in the expected format.

(There is a reason I do not put parentheses around the variable argument of sizeof.  The parentheses are required if the argument is a type, but not when it is a variable or an expression.  That reason is that because sizeof is an operator that only examines its argument without dereferencing any pointers or applying any side effects, expressions like sizeof (*(ptr++)) do not behave like a function call, say sizefunc(*(ptr++)), would: sizeof (*(ptr++)) is identical to sizeof *ptr and sizeof ptr[0].  Similarly, sizeof *(char *)0 is perfectly valid and evaluates to 1, and is not a null pointer dereference bug even though it looks like one.  To me, omitting the parentheses here is a style detail that helps me remember this quirk in its behaviour.  I like anything and everything that reduces my cognitive load without any side effects [except for some people having esthetic or stylistic objections to it: it is useful to me.)
 
The following users thanked this post: peter-h, newbrain

Offline peter-hTopic starter

  • Super Contributor
  • ***
  • Posts: 3694
  • Country: gb
  • Doing electronics since the 1960s...
Re: GCC ARM32 compiler too clever, or not clever enough?
« Reply #33 on: April 19, 2022, 07:33:29 pm »
Thank you again.

I will do this, but a bit of explanation for why I did the previous way:

This code runs only once, at product startup, to write the date/time into a file boot.txt. So not often. Anyway, any "printf" call is about a million clock cycles ;) as I have often found out to my cost in the Z80 etc days (could be literally 100ms with an IAR compiler) so anybody using it can't be expecting "speed".

The slow part of a MOD is a division which IIRC is 16 clocks on the arm32, which is nothing. A mult (int32 or float) is 1 clock.

The values in tm come from code I wrote myself, and it should not be possible to have duff values there to start with.

But you are right and I will do the AND ops because the objective here is to ensure that only 2 digits ever come out. The MOD does not ensure valid values because month lengths vary.

Quote
due to leap seconds, the valid range of the seconds field is actually 0 to 60, inclusive

That will break a lot of systems because loading 60 into an RTC chip will likely do something unpredictable. I reckon it will instantly roll over to 00.
« Last Edit: April 19, 2022, 08:27:45 pm by peter-h »
Z80 Z180 Z280 Z8 S8 8031 8051 H8/300 H8/500 80x86 90S1200 32F417
 

Offline brucehoult

  • Super Contributor
  • ***
  • Posts: 4028
  • Country: nz
Re: GCC ARM32 compiler too clever, or not clever enough?
« Reply #34 on: April 20, 2022, 02:50:42 am »
While it may not affect you right now, also note that due to leap seconds, the valid range of the seconds field is actually 0 to 60, inclusive.

There have been 27 leap seconds added since 1972 (at the end of June 30 or December 31), but the last time one was needed was December 2016, with the ones before that June 2015 and June 2012.

Earth's rotation actually speeded up quite a bit in 2020 and if this continues then rather than needing another leap second any time soon there seems to be a real possibility that we might -- for the first time -- need a NEGATIVE leap second sometime in the next five to ten years. Or not.

If it happens then the time will go straight from 23:59:58 to 00:00:00 on some June 30 or December 31.
 

Offline peter-hTopic starter

  • Super Contributor
  • ***
  • Posts: 3694
  • Country: gb
  • Doing electronics since the 1960s...
Re: GCC ARM32 compiler too clever, or not clever enough?
« Reply #35 on: April 20, 2022, 06:38:46 am »
That's a lesson for anyone writing bad code to run something at time x :)
Z80 Z180 Z280 Z8 S8 8031 8051 H8/300 H8/500 80x86 90S1200 32F417
 

Offline TheCalligrapher

  • Regular Contributor
  • *
  • Posts: 151
  • Country: us
Re: GCC ARM32 compiler too clever, or not clever enough?
« Reply #36 on: April 20, 2022, 11:55:21 pm »
That reason is that because sizeof is an operator that only examines its argument without dereferencing any pointers or applying any side effects, expressions like sizeof (*(ptr++)) do not behave like a function call, say sizefunc(*(ptr++)), would: sizeof (*(ptr++)) is identical to sizeof *ptr and sizeof ptr[0].

While it is true that `sizeof` is an operator, the rest is not entirely accurate. Starting from C99 C language supports variably modified types. When `sizeof` is applied to an argument of variably modified type, the operand is evaluated using the usual rules.

Code: [Select]
#include <stdio.h>

int main(void)
{
  const int n = 1;
  int a[10][n], (*ptr)[n] = a;
 
  printf("%p\n", (void *) ptr);
  sizeof *ptr++;
  printf("%p\n", (void *) ptr);
}

Code: [Select]
0x7fff86d1e3d0
0x7fff86d1e3d4

http://coliru.stacked-crooked.com/a/4056da2ea66b8e8f
 

Offline ve7xen

  • Super Contributor
  • ***
  • Posts: 1192
  • Country: ca
    • VE7XEN Blog
Re: GCC ARM32 compiler too clever, or not clever enough?
« Reply #37 on: April 21, 2022, 01:03:27 am »
73 de VE7XEN
He/Him
 

Offline peter-hTopic starter

  • Super Contributor
  • ***
  • Posts: 3694
  • Country: gb
  • Doing electronics since the 1960s...
Re: GCC ARM32 compiler too clever, or not clever enough?
« Reply #38 on: April 21, 2022, 07:34:09 am »
Some of those are indeed funny (and really stupid, like every Feb being 28 days) but others are likely to be incredibly difficult to code around, like not every month is wholly within the same year, which presumably is a reference to leap seconds, and this could play havoc with trying to start some task at (or more or less at) a given date/time - because that date+time may never actually happen. I think the bottom line is that one cannot use equality on seconds to trigger something. If checking frequently enough, one can use equality on minutes (and upwards) and then discard seconds or use a >= on the seconds. I wonder if the unix time functions (e.g. # of seconds between two date+time values) actually code this correctly.
« Last Edit: April 21, 2022, 07:35:45 am by peter-h »
Z80 Z180 Z280 Z8 S8 8031 8051 H8/300 H8/500 80x86 90S1200 32F417
 

Online Nominal Animal

  • Super Contributor
  • ***
  • Posts: 6241
  • Country: fi
    • My home page and email address
Re: GCC ARM32 compiler too clever, or not clever enough?
« Reply #39 on: April 21, 2022, 08:28:32 am »
Starting from C99 C language supports variably modified types. When `sizeof` is applied to an argument of variably modified type, the operand is evaluated using the usual rules.
Even more reason to consider sizeof "special", in the sense that one does not want to have any side effects in sizeof expressions, wouldn't you agree?

(One also needs to avoid side effects in assert() statements, but that is because the statement is not compiled at all when NDEBUG is defined.  That's the rough description, with exact details for example here.)

Now, not all of us need this kind of reminders, but my own code is more robust and reliable (lower bug density) when I do have them.  I do believe it may be useful tool for others as well, which is why I described it in my post; it is not an arbitrary stylistic choice for me.
 

Offline SiliconWizard

  • Super Contributor
  • ***
  • Posts: 14445
  • Country: fr
Re: GCC ARM32 compiler too clever, or not clever enough?
« Reply #40 on: April 21, 2022, 05:11:05 pm »
Just don't use VLAs, and sizeof is purely a compile-time operator. Or is there any other kind of "variably modified type" in C that I missed?
 

Offline hamster_nz

  • Super Contributor
  • ***
  • Posts: 2803
  • Country: nz
Re: GCC ARM32 compiler too clever, or not clever enough?
« Reply #41 on: April 22, 2022, 09:59:06 am »
If you just want to get your code running...

Code: [Select]
struct tm boot_time;
  char dtbuf [20];
getrtc (&boot_time);
dtbuf[0] = '0'+boot_time.tm_mday/10%10;
dtbuf[1] = '0'+boot_time.tm_mday%10;

        dtbuf[2] = '0'+(boot_time.tm_mon+1)/10%10;
        dtbuf[3] = '0'+(boot_time.tm_mon+1)%10;
                        ...
dtbuf[?]=0;
[code]
Gaze not into the abyss, lest you become recognized as an abyss domain expert, and they expect you keep gazing into the damn thing.
 
The following users thanked this post: peter-h

Offline TheCalligrapher

  • Regular Contributor
  • *
  • Posts: 151
  • Country: us
Re: GCC ARM32 compiler too clever, or not clever enough?
« Reply #42 on: April 23, 2022, 02:39:04 am »
Just don't use VLAs, and sizeof is purely a compile-time operator. ?

A better advice would be: just don't write `sizeof` argument expressions with side effects. There's normally no credible reason to do so.

Or is there any other kind of "variably modified type" in C that I missed

No, there isn't.

However, people sometimes let VLA slip right under their noses. In C

Code: [Select]
const int N = 10;
int a[N];

is actually a VLA, even though it is a regular array in C++. This surprising difference between C and C++ sometimes pops up when porting code between languages or when writing cross-compilable code.
« Last Edit: April 23, 2022, 04:23:28 am by TheCalligrapher »
 

Offline SiliconWizard

  • Super Contributor
  • ***
  • Posts: 14445
  • Country: fr
Re: GCC ARM32 compiler too clever, or not clever enough?
« Reply #43 on: April 23, 2022, 03:30:54 am »
Just don't use VLAs, and sizeof is purely a compile-time operator. ?

A better advice would be: just don't write `sizeof` argument expressions with side effects. There's normally no credible reason to do so.

Well I get the point, but it's a bit strict. For instance, something rather common is to use sizeof on a dereferenced pointer to get the size of the base type. Particularly useful in macros which can apply to any pointer. Example, say 'p' is a pointer to some base type: 'sizeof(*p)'.

'*p' is probably considered having a side-effect (even though in this context, the dereference will never be evaluated.)
 

Offline TheCalligrapher

  • Regular Contributor
  • *
  • Posts: 151
  • Country: us
Re: GCC ARM32 compiler too clever, or not clever enough?
« Reply #44 on: April 23, 2022, 04:25:13 am »
Just don't use VLAs, and sizeof is purely a compile-time operator. ?

A better advice would be: just don't write `sizeof` argument expressions with side effects. There's normally no credible reason to do so.

Well I get the point, but it's a bit strict. For instance, something rather common is to use sizeof on a dereferenced pointer to get the size of the base type. Particularly useful in macros which can apply to any pointer. Example, say 'p' is a pointer to some base type: 'sizeof(*p)'.

'*p' is probably considered having a side-effect (even though in this context, the dereference will never be evaluated.)

Um... "Side effect" is a rather well-defined concept in C and C++ evaluation model. And no, `*p` does not have any side effects. (Unless, `p` is `volatile`, I'd guess...)
 
The following users thanked this post: newbrain

Offline SiliconWizard

  • Super Contributor
  • ***
  • Posts: 14445
  • Country: fr
Re: GCC ARM32 compiler too clever, or not clever enough?
« Reply #45 on: April 23, 2022, 04:45:07 pm »
Just don't use VLAs, and sizeof is purely a compile-time operator. ?

A better advice would be: just don't write `sizeof` argument expressions with side effects. There's normally no credible reason to do so.

Well I get the point, but it's a bit strict. For instance, something rather common is to use sizeof on a dereferenced pointer to get the size of the base type. Particularly useful in macros which can apply to any pointer. Example, say 'p' is a pointer to some base type: 'sizeof(*p)'.

'*p' is probably considered having a side-effect (even though in this context, the dereference will never be evaluated.)

Um... "Side effect" is a rather well-defined concept in C and C++ evaluation model. And no, `*p` does not have any side effects. (Unless, `p` is `volatile`, I'd guess...)

You guess. ::)
 

Offline newbrain

  • Super Contributor
  • ***
  • Posts: 1719
  • Country: se
Re: GCC ARM32 compiler too clever, or not clever enough?
« Reply #46 on: April 24, 2022, 11:52:17 am »
You guess. ::)
No need to guess, ISO/IEC 9899:2011, 5.1.2.3 Program execution, §2:
Quote
Accessing a volatile object, modifying an object, modifying a file, or calling a function that does any of those operations are all side effects[...]
So, *p, if evaluated, yields of course a side effect when p is a volatile pointer (as does the simple statement p;).
As an argument to sizeof, it does not, as it's not evaluated.

Really, there's only one exception to the 'sizeof argument is not evaluated' and it's VLAs, 6.5.3.4 The sizeof and _Alignof operators, §2:
Quote
[...]If the type of the operand is a variable length array type, the operand is evaluated; otherwise, the operand is not evaluated and the result is an
integer constant.

There is a reason I do not put parentheses around the variable argument of sizeof.  [...]  To me, omitting the parentheses here is a style detail that helps me remember this quirk in its behaviour.  I like anything and everything that reduces my cognitive load without any side effects
QFT.
In fact, I've always been doing exactly the same, for the very same reason.



Nandemo wa shiranai wa yo, shitteru koto dake.
 

Offline SiliconWizard

  • Super Contributor
  • ***
  • Posts: 14445
  • Country: fr
Re: GCC ARM32 compiler too clever, or not clever enough?
« Reply #47 on: April 24, 2022, 05:24:30 pm »
Yes, my point. :)

As to the example:
Code: [Select]
const int N = 10;
int a[N];

it can be a trap for many indeed. Yes, here a is a VLA, while it may look stupid to many.
In particular, as a VLA, you can't provide an initilializer.

Code: [Select]
const int N = 10;
int a[N] = { 0 };

is wrong.

But as to sizeof (and stack manipulation), the reasonable optimizing C compilers I've tried all treat 'a' as a regular array when optimizations are enabled, and as a VLA when they aren't (which produces horrible code.)

That "trap" is nasty for those that think constants defined as const variables are "cleaner" than macros. Unfortunately, they are not always a replacement.
« Last Edit: April 24, 2022, 05:31:00 pm by SiliconWizard »
 

Offline newbrain

  • Super Contributor
  • ***
  • Posts: 1719
  • Country: se
Re: GCC ARM32 compiler too clever, or not clever enough?
« Reply #48 on: April 24, 2022, 06:59:43 pm »
That "trap" is nasty for those that think constants defined as const variables are "cleaner" than macros. Unfortunately, they are not always a replacement.
Yes, C is full of traps for C++ programmers - and vice versa.
Nandemo wa shiranai wa yo, shitteru koto dake.
 

Offline peter-hTopic starter

  • Super Contributor
  • ***
  • Posts: 3694
  • Country: gb
  • Doing electronics since the 1960s...
Re: GCC ARM32 compiler too clever, or not clever enough?
« Reply #49 on: April 27, 2022, 04:48:55 am »
Code: [Select]
int a[N] = { 0 };
That's just horrible, because whether the whole array or just the first element get zeroed is compiler dependent, no?

I seem to remember the zeroing works for structures.

That's even if N has been correctly evaluated at compile-time... I didn't think that worked at all anyway.
Z80 Z180 Z280 Z8 S8 8031 8051 H8/300 H8/500 80x86 90S1200 32F417
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf