Author Topic: GCC ARM32 comparison question  (Read 2546 times)

0 Members and 1 Guest are viewing this topic.

Offline peter-hTopic starter

  • Super Contributor
  • ***
  • Posts: 3694
  • Country: gb
  • Doing electronics since the 1960s...
GCC ARM32 comparison question
« on: June 17, 2022, 03:50:27 pm »
Code: [Select]
volatile uint8_t a=0xaa;
volatile uint8_t b=0x55;
volatile bool fred=false;

if (a != ~b)
{
  fred=true;
}

// now fred=true

b=~b;
fred=false;
if (a != b)
{
  fred=true;
}

// now fred=false

How does this work? Does inverting a byte produce some larger variable? Even this doesn't work:

Code: [Select]
if (a != (~b))
Z80 Z180 Z280 Z8 S8 8031 8051 H8/300 H8/500 80x86 90S1200 32F417
 

Offline Siwastaja

  • Super Contributor
  • ***
  • Posts: 8168
  • Country: fi
Re: GCC ARM32 comparison question
« Reply #1 on: June 17, 2022, 04:10:03 pm »
This is basic C, you can Google it all, or read any tutorial - and of course, the standard itself (any version really). Instead of direct answer, let me help you with keywords:

C operators:
~ operator
!= operator
https://en.wikipedia.org/wiki/Operators_in_C_and_C%2B%2B

C integer promotion

BTW, from the strange way of using volatile I'm assuming you are flashing this on an MCU target and looking at it in debugger, am I right? But IMHO, for simple "how does that language work" tests, I would prefer just having gcc installed on your PC, write a small program and use printf(). This easily extents to be a larger unit test where you can loop through possible values and check the correctness.
« Last Edit: June 17, 2022, 04:14:20 pm by Siwastaja »
 

Online Nominal Animal

  • Super Contributor
  • ***
  • Posts: 6242
  • Country: fi
    • My home page and email address
Re: GCC ARM32 comparison question
« Reply #2 on: June 17, 2022, 04:11:39 pm »
Does inverting a byte produce some larger variable?
You forgot the automatic integer promotions C compilers (have to) do.  Everything smaller than int that can be described by an int, gets converted to an int, and so on.

The correct expression here is
if (a != (uint8_t)(~b))
 
The following users thanked this post: Siwastaja, newbrain, SiliconWizard

Offline peter-hTopic starter

  • Super Contributor
  • ***
  • Posts: 3694
  • Country: gb
  • Doing electronics since the 1960s...
Re: GCC ARM32 comparison question
« Reply #3 on: June 17, 2022, 04:15:27 pm »
Yes; embedded target. I really should set up Borland C v3.1 on my PC :)

I did suspect the uint8_t was being converted to an int, so

~0

becomes

0xffffffff

for the comparison (or some such).

The reason this has not caught me out before is because most of my 32F417 coding has been uint32_t, flipping bits destined for registers, etc.
« Last Edit: June 17, 2022, 04:17:02 pm by peter-h »
Z80 Z180 Z280 Z8 S8 8031 8051 H8/300 H8/500 80x86 90S1200 32F417
 

Online SiliconWizard

  • Super Contributor
  • ***
  • Posts: 14447
  • Country: fr
Re: GCC ARM32 comparison question
« Reply #4 on: June 17, 2022, 05:59:52 pm »
Yep, integer promotion is the key to integer arithmetics in C. And yep, it can be a bit confusing and a bit annoying. As shown above, you'll need explicit conversions.
 

Offline peter-hTopic starter

  • Super Contributor
  • ***
  • Posts: 3694
  • Country: gb
  • Doing electronics since the 1960s...
Re: GCC ARM32 comparison question
« Reply #5 on: June 17, 2022, 07:18:22 pm »
Why is the cast needed?

It is surely obvious what the intention is? Comparing a byte with (say) 32 bits is meaningless, unless one "knows" the int value will always fit within a byte.
Z80 Z180 Z280 Z8 S8 8031 8051 H8/300 H8/500 80x86 90S1200 32F417
 

Online Nominal Animal

  • Super Contributor
  • ***
  • Posts: 6242
  • Country: fi
    • My home page and email address
Re: GCC ARM32 comparison question
« Reply #6 on: June 17, 2022, 07:49:42 pm »
Why is the cast needed?
Because the C standard says that before an expression is evaluated, integer promotions are performed.

We cannot avoid that, but we can fix the comparison logic by casting the promoted value back to uint8_t range and precision.  (Since C99, a cast suffices – regardless of optimization level – to limit the cast expression to the range and precision of the cast type.)
 

Online SiliconWizard

  • Super Contributor
  • ***
  • Posts: 14447
  • Country: fr
Re: GCC ARM32 comparison question
« Reply #7 on: June 17, 2022, 07:53:42 pm »
I set the '-Wconversion' flag in compilers to catch potential implicit conversion issues, and I highly recommend it.
Unfortunately, it doesn't catch anything in this particular comparison case.

As Nominal said, integer promotion is a basic rule in C, and it's just the way it works. It *does* confuse a lot of people.
With your example, what seems the most confusing to many is that a unary operator will trigger integer promotion, while you'd usually expect it to preserve the original type of the operand. I do not think that not preserving the original type is a good idea here, but I didn't design C nor was ever part of the std commitee, so my opinion probably doesn't matter much, and doing otherwise in any new std revision would probably break too much code to be even considered.

To completely avoid this kind of "issues", you'd need a strongly-typed language, such as Ada, which wouldn't even allow comparing objects of a different type (unless you provided the corresponding operator.)

Implicit conversions are, IMHO, the "root of all evil", but in practice, there seems to be way more people hating too strongly-typed languages than people hating implicit conversions (which look convenient at first sight), so... there you have it. Pick your poison.
« Last Edit: June 17, 2022, 08:00:53 pm by SiliconWizard »
 

Offline peter-hTopic starter

  • Super Contributor
  • ***
  • Posts: 3694
  • Country: gb
  • Doing electronics since the 1960s...
Re: GCC ARM32 comparison question
« Reply #8 on: June 17, 2022, 09:26:15 pm »
Quote
Because the C standard says that before an expression is evaluated, integer promotions are performed.

Why would that be a good idea?

I know cases like sscanf which always read integers and there is no way to get them to read directly into a uint8_t for example.
Z80 Z180 Z280 Z8 S8 8031 8051 H8/300 H8/500 80x86 90S1200 32F417
 

Online Nominal Animal

  • Super Contributor
  • ***
  • Posts: 6242
  • Country: fi
    • My home page and email address
Re: GCC ARM32 comparison question
« Reply #9 on: June 17, 2022, 10:22:57 pm »
Quote
Because the C standard says that before an expression is evaluated, integer promotions are performed.
Why would that be a good idea?
:-//

C is an imperfect tool; that's just how it works.
 

Online newbrain

  • Super Contributor
  • ***
  • Posts: 1719
  • Country: se
Re: GCC ARM32 comparison question
« Reply #10 on: June 17, 2022, 11:20:01 pm »
Yes; embedded target. I really should set up Borland C v3.1 on my PC :)
I hope you are joking. I fear you aren't.

Why would that be a good idea?
Mostly, hysterical raisins.
You can read more in an old, but very good document:
"Rationale for International Standard — Programming Languages — C"
Specifically, in chapter 6.3 Conversions.

The whole doc is worth reading though, as a companion to the standard - it was written originally for C89, but was updated to reflect C99.
« Last Edit: June 17, 2022, 11:30:54 pm by newbrain »
Nandemo wa shiranai wa yo, shitteru koto dake.
 

Offline oPossum

  • Super Contributor
  • ***
  • Posts: 1415
  • Country: us
  • Very dangerous - may attack at any time
Re: GCC ARM32 comparison question
« Reply #11 on: June 18, 2022, 04:52:20 am »
Code: [Select]

if((a^b) == 0xFF) { }  // Lower 8 bits are compliment

 

Offline peter-hTopic starter

  • Super Contributor
  • ***
  • Posts: 3694
  • Country: gb
  • Doing electronics since the 1960s...
Re: GCC ARM32 comparison question
« Reply #12 on: June 18, 2022, 05:27:51 am »
Quote
I hope you are joking. I fear you aren't.

I was joking :)

But I still have the manuals on the shelf.

I can very easily test C code on my 32F417 target. I have an RTOS task called APP and just stick it in there.

I did read that C document on "integer promotion" and still don't get why this is needed. They could just treat uint8_t and int8_t as genuine standalone variable types. Currently, it seems, they get promoted to uint and int respectively for any operations, so their only advantage is less storage space.
« Last Edit: June 18, 2022, 05:32:53 am by peter-h »
Z80 Z180 Z280 Z8 S8 8031 8051 H8/300 H8/500 80x86 90S1200 32F417
 

Online ataradov

  • Super Contributor
  • ***
  • Posts: 11238
  • Country: us
    • Personal site
Re: GCC ARM32 comparison question
« Reply #13 on: June 18, 2022, 05:59:39 am »
C is old and its promotion rules are generally considered bad. Modern languages handle this much better. And "uint8_t" and "int8_t" were added to the standard decades after the initial release, and the original type system is much less strict and poorly (platform and implementation) defined.

Also, if there were no promotion, you will be equally surprised by arithmetic operations. I bet you rely on that a lot in your code.
Alex
 

Offline peter-hTopic starter

  • Super Contributor
  • ***
  • Posts: 3694
  • Country: gb
  • Doing electronics since the 1960s...
Re: GCC ARM32 comparison question
« Reply #14 on: June 18, 2022, 06:18:39 am »
Quote
you will be equally surprised by arithmetic operations

Can you give me an example?

Actually I don't do arithmetic on byte variables, other than ++ or --. Also I never use int8_t. On the ARM32 the perf is so high there is no point. I don't use pointers, and use int or uint32_t as an array index. Even float mult is 1 clock. It's a different world.

But in the old days people wrote whole programs in byte variables for speed. For example Honeywell did an autopilot (KFC225) which appears to do its algorithms using signed bytes (with occassional severe underflow problems as it happens). On the other hand, and AFAIK they used C, it would not have gained them much because bytes would be promoted to int which on the CPU they used would be a 16 bit reg so unless one uses asm code (e.g. 8x8=16 multiply) you gain little. In those days, 1980-1995, I used only asm, on all the CPUs listed below except arm32.
« Last Edit: June 18, 2022, 06:31:37 am by peter-h »
Z80 Z180 Z280 Z8 S8 8031 8051 H8/300 H8/500 80x86 90S1200 32F417
 

Online ataradov

  • Super Contributor
  • ***
  • Posts: 11238
  • Country: us
    • Personal site
Re: GCC ARM32 comparison question
« Reply #15 on: June 18, 2022, 06:41:28 am »
I don't know what you man by "in byte variables for speed", but "int" is guaranteed to be faster, since on 16- and 32- bit CPUs compilers have to issue extra instructions to comply with 8-bit semantics. Some people think they are optimizing (like using uint8_t in short "for" loops), but in reality they are just making things worse.

As for the example - any reasonably complicated expression with data in byte arrays. For example, you have an array of 5 bytes and you want to calculate average:

Code: [Select]
uint8_t a[5] = {1, 123, 27, 13, 222 };
uint8_t avg = (a[0] + a[1]+ a[2]+ a[3]+ a[4]) / 5; // Wrong without promotion
uint8_t avg = ((int)a[0] + (int)a[1]+ (int)a[2]+ (int)a[3]+ (int)a[4]) / 5; // You would have to do something like this.
Depending on the new rules, only the first manual promotion may be necessary, but that is error-prone when making modifications.

Stuff like this happens all the time.

And again, C promotion rules are not good, but it is what it is, and if you want to use C, you just need to learn them.
« Last Edit: June 18, 2022, 06:44:10 am by ataradov »
Alex
 
The following users thanked this post: newbrain

Offline peter-hTopic starter

  • Super Contributor
  • ***
  • Posts: 3694
  • Country: gb
  • Doing electronics since the 1960s...
Re: GCC ARM32 comparison question
« Reply #16 on: June 18, 2022, 08:04:51 am »
Quote
I don't know what you man by "in byte variables for speed", but "int" is guaranteed to be faster, since on 16- and 32- bit CPUs compilers have to issue extra instructions to comply with 8-bit semantics. Some people think they are optimizing (like using uint8_t in short "for" loops), but in reality they are just making things worse.

I am sure you know all this, so I am surprised by your question.

Take the Z80. HL holds a uint16_t x and you are doing x << 4.

 ld b, 4
loop: add hl, hl
 djnz loop

The loop counter is a byte.

If you were forced to an "int", which on an IAR C compiler was 16 bits, you would need something like

 ld bc, 4
loop: add hl, hl
 dec bc
 ld a, b
 or c
 jr nz, loop

There is a million cases where 8 bit values is far faster, on those micros. One also had 8x8=16 multiply, 16/8=8 divide, etc. Converting every int8 into 16 bit arithmetic bloats the code 5x to 10x. In fact I remember rewriting a lot of IAR code in asm and wondering why they are doing everything in 16 bits, with the top byte set to 0. 8x8=16 is probably 10x faster than 16x16=32 and then discarding the top 16 bits. No wonder C got a crap name back then for generating crap code :)

In later years, CPUs became "genuinely 16 bit" even though they still had the crippling 64k address space. H8/500 was one such.

On the arm32 I use int or uint32_t for loop counters; it merely wastes RAM :)

Re your example, that is wrong code :) because anybody adding up even two bytes should know the result can be > 255. Classic integer maths overflow. If using integers (rather than floats) one needs to be intimately familiar with the actual range of values i.e. the actual data. This was well known since for ever. The Apollo guidance stuff used int32 heavily and had to deal with this. So avg should be a uint16_t and possibly a uint32_t if adding up lots of them. Auto promoting avg to uint16_t only saves your skin partially, and hides the real problem.

People who can't be bothered with actual data ranges have to use floats - and pay a heavy price it... in the old days, of x100 to x1000 less speed. Not today, with a 168 megaflop 32F417.

AIUI, uint32_t avg = (a[0] + a[1]+ a[2]+ a[3]+ a[4]) would promote each of the five items to a uint32_t before doing the addition. That's probably wrong too :)
« Last Edit: June 18, 2022, 08:19:09 am 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 comparison question
« Reply #17 on: June 18, 2022, 08:09:37 am »
I don't use pointers, and use int or uint32_t as an array index.

Both of those pessimise speed on different machines. The best choice if you're indexing from the actual name of the array is size_t. If you're indexing from a pointer that may be into the middle of an array then ptrdiff_t is better than int.
 

Offline brucehoult

  • Super Contributor
  • ***
  • Posts: 4028
  • Country: nz
Re: GCC ARM32 comparison question
« Reply #18 on: June 18, 2022, 08:30:51 am »
Quote
I don't know what you man by "in byte variables for speed", but "int" is guaranteed to be faster, since on 16- and 32- bit CPUs compilers have to issue extra instructions to comply with 8-bit semantics. Some people think they are optimizing (like using uint8_t in short "for" loops), but in reality they are just making things worse.

I am sure you know all this, so I am surprised by your question.

Take the Z80. HL holds a uint16_t x and you are doing x << 4.

 ld b, 4
loop: add hl, hl
 djnz loop

The loop counter is a byte.

If you were forced to an "int", which on an IAR C compiler was 16 bits, you would need something like

 ld bc, 4
loop: add hl, hl
 dec bc
 ld a, b
 or c
 jr nz, loop

That's kind of dumb because it's no bigger, and much faster, to do as sdcc does:

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

uint16_t shl4(uint16_t n){
  return n<<4;
}

Using "sdcc -mz80 --opt-code-size -S dumb_loop.c":

Code: [Select]
.area _CODE
;dumb_loop.c:3: uint16_t shl4(uint16_t n){
; ---------------------------------
; Function shl4
; ---------------------------------
_shl4::
;dumb_loop.c:4: return n<<4;
add hl, hl
add hl, hl
add hl, hl
add hl, hl
ex de, hl
;dumb_loop.c:5: }
ret

Ok, let's make it a variable shift:

Code: [Select]
uint16_t shl4(uint16_t n, int sh){
  return n<<sh;
}

Code: [Select]
.area _CODE
;dumb_loop.c:3: uint16_t shl4(uint16_t n, int sh){
; ---------------------------------
; Function shl4
; ---------------------------------
_shl4::
;dumb_loop.c:4: return n<<sh;
ld b, e
inc b
jr 00104$
00103$:
add hl, hl
00104$:
djnz 00103$
ex de, hl
;dumb_loop.c:5: }
ret

Hmm. I guess you forgot that any shift amount over 16 is going to give the same result -- 0 -- so you don't have to worry about counts over 255. But the compiler didn't forget!
 

Online newbrain

  • Super Contributor
  • ***
  • Posts: 1719
  • Country: se
Re: GCC ARM32 comparison question
« Reply #19 on: June 18, 2022, 08:48:45 am »
AIUI, uint32_t avg = (a[0] + a[1]+ a[2]+ a[3]+ a[4]) would promote each of the five items to a uint32_t before doing the addition. That's probably wrong too :)
The result of the expression that is the right operand of the '=' operator will be converted to uint32_t.
But the operands of the four '+' operators sub-expressions will be converted to signed int, as discussed above.
The final effect is the same, in this specific case.

I don't use pointers, and use int or uint32_t as an array index.
Liar, liar, pants on fire.
Nandemo wa shiranai wa yo, shitteru koto dake.
 

Offline Siwastaja

  • Super Contributor
  • ***
  • Posts: 8168
  • Country: fi
Re: GCC ARM32 comparison question
« Reply #20 on: June 18, 2022, 10:30:42 am »
Implicit conversions are, IMHO, the "root of all evil", but in practice, there seems to be way more people hating too strongly-typed languages than people hating implicit conversions (which look convenient at first sight), so... there you have it. Pick your poison.

I totally agree, yet most people seem to have gripes with C being too strongly typed, i.e., they want even weaker typing, to the point of automatic implicit conversions from anything to anything, with little or no control over types at all. It seems easy on surface, but such lack of control is a recipe for disaster. C is at least halfway there to strong typing.
 

Offline peter-hTopic starter

  • Super Contributor
  • ***
  • Posts: 3694
  • Country: gb
  • Doing electronics since the 1960s...
Re: GCC ARM32 comparison question
« Reply #21 on: June 18, 2022, 10:35:26 am »
This has been a huge learning experience for me.

So, AIUI, C makes any variable size shorter than int pointless, unless you are trying to save storage space.

In particular (unless very short of stack space) using a variable smaller than int inside a function is pointless because any storage used is chucked away upon exit. And a lot of variables are optimised anyway so never stored in RAM.

This is why I have not been bitten by this (until yesterday) because the bulk of my C programming has been on the arm32 and there I know the arm32 is natively 32 bit so uint8_t int8_t uint16_t int16_t are pointless. The only time I have used int16 was for peripherals registers which are 16 bit, like DMA transfer counters. uint8_t I have used for buffers, obviously, because practically all "data" is byte sized. My previous C coding was a little bit of hacking somebody else's C code about 20 years previously.

I still wonder why C doesn't internally represent bytes as bytes. They are quite common in embedded systems :) Expanding every byte retrieved from a buffer to 32 bits doesn't generate more code, or lose speed, on an arm32, but on most older stuff it does one or the other or both. Of course few people care about old CPUs today...

I do know djnz, btw - I posted it above :)

Strong typing is a PITA. I did tons of Pascal and even implemented a Pascal compiler on an embedded customer-programmable product. The language was near-useless without a lot of extensions.
Z80 Z180 Z280 Z8 S8 8031 8051 H8/300 H8/500 80x86 90S1200 32F417
 

Offline Siwastaja

  • Super Contributor
  • ***
  • Posts: 8168
  • Country: fi
Re: GCC ARM32 comparison question
« Reply #22 on: June 18, 2022, 10:41:26 am »
I did read that C document on "integer promotion" and still don't get why this is needed.

You are missing the fact that a language which actually runs and enables a big part of our computerized world, the language needs to be properly standardized and specified. Compilers just can't work they way which would seem logical or good, because then everyone would disagree about what it exactly is.

In the process of standardization, poor choices happen, it is inevitable; people make mistakes, have bad ideas, or are thinking about some different use case which ends up being irrelevant.

C is a product of such process, it's far from perfect.

C's strengths are:
* It is standardized, proper language, which changes slowly. It's stable.
* It is good enough,
* While it has a dozen of stupid ideas or footguns, it doesn't have hundreds of them. A normal human being with say 110IQ, with programmer mentality, can actually learn the language in a few years!

Languages that fundamentally change all the time, or specification is all over the place, become toy languages like Python or PHP. They are usable in their own domain, of course. But there is a difference: if you ask why something is brain-dead in C or in PHP, for C, the reason is: "it was considered a good idea in 1980's. it is standardized like this, all greybeards know it, and it has to stay that way for compatibility". For PHP, the answer is: "no one knows. It's just a bug. It may or may not randomly change."
 
The following users thanked this post: SiliconWizard

Offline brucehoult

  • Super Contributor
  • ***
  • Posts: 4028
  • Country: nz
Re: GCC ARM32 comparison question
« Reply #23 on: June 18, 2022, 11:09:43 am »
So, AIUI, C makes any variable size shorter than int pointless, unless you are trying to save storage space.

No, not at all.

The C language always specifies things "as if". Integer calculations on values shorter than int must act "as if" they were promoted to int and the calculation done in int. But if the ISA in question has the ability to do operations directly on bytes or shorts AND the compiler can prove that the result will be the same, then it is allowed to use them.

Code: [Select]
char addb(char *b, char *c){
  return *b + *c;
}

Z80 (using sdcc):

Code: [Select]
_addb::
ld c, (hl)
ld a, (de)
add a, c
ret

x86_64: (gcc)

Code: [Select]
addb(char*, char*):
        movzx   eax, BYTE PTR [rsi]
        add     al, BYTE PTR [rdi]
        ret

MSP430:

Code: [Select]
addb(char*, char*):
        MOV.B   @R12, R12
        ADD.B   @R13, R12
        RET

x86: (msvc)

Code: [Select]
char addb(char *,char *) PROC                             ; addb, COMDAT
        mov     eax, DWORD PTR _b$[esp-4]
        mov     ecx, DWORD PTR _c$[esp-4]
        mov     al, BYTE PTR [eax]
        add     al, BYTE PTR [ecx]
        ret     0

All of those are compiling C code to use byte ALU operations.

It's not at all pointless to use char and short.

If the result of the function was int instead of char then the code would be different, with a full 16 bit (or more) add -- or at least with the next byte getting the carry flag (if char is unsigned).
« Last Edit: June 18, 2022, 11:12:42 am by brucehoult »
 

Offline peter-hTopic starter

  • Super Contributor
  • ***
  • Posts: 3694
  • Country: gb
  • Doing electronics since the 1960s...
Re: GCC ARM32 comparison question
« Reply #24 on: June 18, 2022, 11:29:59 am »
OK, sure, but nobody answered my question :)

Except in terms of "it is the C standard, so accept it".

ISTM that this "promotion to int of almost everything shorter" is an attempt to prevent bad coding leading to what has since for ever been known in the embedded world (whether manually blowing fusible link PROMs one byte at a time, or asm, or C or whatever) as "integer overflow". The cost of doing this is

- on CPUs which are mostly not natively int sized (most of the 8/16 bit stuff like the Z80 etc) it bloats a lot of stuff, and slows it down, a lot
- if there is enough oveflow to make MSB=1 then you will likely get real problems with any comparisons, because the promotion is to signed int
- it conceals most integer overflow, which is nearly always loss of real data - unless doing a checksum ;)
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 comparison question
« Reply #25 on: June 18, 2022, 11:45:26 am »
OK, sure, but nobody answered my question :)

Except in terms of "it is the C standard, so accept it".

When the rubber meets the road, yes, that's the specification of the language.

As to why...

Quote
ISTM that this "promotion to int of almost everything shorter" is an attempt to prevent bad coding leading to what has since for ever been known in the embedded world (whether manually blowing fusible link PROMs one byte at a time, or asm, or C or whatever) as "integer overflow". The cost of doing this is

No. It's not to protect against sloppy programmers. It's because very many of the machines that C runs on -- basically all mainframes or RISC that started life as 32 bit -- there are only instructions for full register arithmetic, so if you forced them to mask results down to 8 bits after every operation it would be very inefficient.

At the same time, machines that *do* have byte arithmetic are -- as I showed above -- free to use it IF IT WILL GET THE SAME RESULT.

It's a very pragmatic policy.

Quote
- on CPUs which are mostly not natively int sized (most of the 8/16 bit stuff like the Z80 etc) it bloats a lot of stuff, and slows it down, a lot

Rubbish.

What does it bloat? You showed one calculation that you said would be bloated on z80 if done in C. I showed actual C compiler output for the z80 which was not bloated.

I presented another calculation, and showed that it also was not boated on any of a range of machines.

Want to come up with an example which will actually show bloat?

Quote
- if there is enough oveflow to make MSB=1 then you will likely get real problems with any comparisons, because the promotion is to signed int
- it conceals most integer overflow, which is nearly always loss of real data - unless doing a checksum ;)

The MSB of what? A 1 byte value?  Promotion of an unsigned char is to (signed) int values from 0-255, and promotion of a signed char is to int values from -128 to 127. Arithmetic and comparisons operated perfectly in either case.

If anything, it is *easier* to detect overflow.

Examples in C, please.
« Last Edit: June 18, 2022, 12:14:44 pm by brucehoult »
 

Online Nominal Animal

  • Super Contributor
  • ***
  • Posts: 6242
  • Country: fi
    • My home page and email address
Re: GCC ARM32 comparison question
« Reply #26 on: June 18, 2022, 12:08:14 pm »
OK, sure, but nobody answered my question :)

Except in terms of "it is the C standard, so accept it".
Well, it is one of those choices by language developers that have complicated (and often not that convincing) reasoning and history behind it, and cannot be changed without making the result a different programming language.

You are probably right in that the reasoning did not emphasize use cases (i.e., us developers and programmers using the language), and instead had more to do with the compiler implementation and the abstract machine approach the C standard uses.  There are related things like argument promotion (similar; but float is also promoted to double) applied when there is no prototype or the arguments are passed as variable arguments to a variadic function, see <stdarg.h>) making variadic function calls much easier to support in C, as each passed argument always takes a full native register or native-size word on the stack, depending on the ABI.

Just like many others here, I've done paid work in something like a dozen different programming languages.  It is useful to wonder why language designers made a specific choice, because it can reveal their intent and viewpoint (related to the paradigm underlying the language design).  However, sometimes such choices don't have any deeper meaning for us developers, because their reasoning is elsewhere, or just plain wrong.  I could be wrong, but I've always assumed integer promotions in C belong to this latter category.  It is sometimes useful, sometimes annoying; just something one has to deal with when writing C, without any huge programming paradigm insights in it.  Similar to e.g. how you can swap array names and indices in C, a[b] being equivalent to b[a].  Other than obfuscated code contests, I've never seen a real world use case where that would be actually useful.

So, AIUI, C makes any variable size shorter than int pointless, unless you are trying to save storage space.

In particular (unless very short of stack space) using a variable smaller than int inside a function is pointless because any storage used is chucked away upon exit. And a lot of variables are optimised anyway so never stored in RAM.
No; this is exactly why the int_fastN_t and uint_fastN_t types were introduced (and standardized in C99 in <stdint.h>), for N = 8, 16, 32, and 64.  They provide at least N-bit range, but their actual size depends on what is fast on a particular architecture.

For example, on 32- and 64-bit architectures, uint_fast16_t is usually 32 or 64 bit unsigned integer, whichever is "faster" for that architecture.

I still wonder why C doesn't internally represent bytes as bytes. They are quite common in embedded systems :)
Actually, it does: it just calls them char (implementation-defined signedness), unsigned char, and signed char.  In particular, sizeof (char) = sizeof (unsigned char) = sizeof (signed char) = 1 by definition –– and the size is not explicitly 8 bits.  There are even specific rules about conversion to and from ((un)signed) char buffers in C.

It is only in (arithmetic and logical) expressions that integer promotions are done; and also when calling functions without prototypes or variadic functions.
So, it is not really about internal representation, and more about the definition of how arithmetic and logical expressions are evaluated.
And even then, the compiler does not need to do that, as long as it is proven that the results are the same as if it had done so.  (Kinda annoying, yeah, but the C language is defined in terms of an abstract machine.)
 
The following users thanked this post: peter-h

Offline peter-hTopic starter

  • Super Contributor
  • ***
  • Posts: 3694
  • Country: gb
  • Doing electronics since the 1960s...
Re: GCC ARM32 comparison question
« Reply #27 on: June 18, 2022, 12:23:33 pm »
Thank you. It does clarify it for me, and importantly I can see why my code is working fine, until yesterday ;)

Quote
Examples in C, please.

I can't because I don't have a Z80 C environment and don't have the time to set one up. I probably have the 1980s IAR Z180 compiler somewhere (I don't think it was dongled, but it was ~£1000) and could try with that, but clearly compilers have got a lot more clever since then. I spent months on a particular project coding a lot of its functions in asm. Including runtimes, which were almost certainly written wholly in C too (as distinct from Hitech C which had e.g. printf() in C but a lot of runtime stuff done in asm; Clyde Smith-Stubbs knew his stuff).

« Last Edit: June 18, 2022, 12:26:07 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 comparison question
« Reply #28 on: June 18, 2022, 12:36:14 pm »
Quote
Examples in C, please.

I can't because I don't have a Z80 C environment and don't have the time to set one up.

I only need source code, I have a z80 compiler. It also does 6502, z180, mcs51, r2k, pic14, pic16, hc08, s08, stm8.

Quote
I probably have the 1980s IAR Z180 compiler somewhere (I don't think it was dongled, but it was ~£1000) and could try with that, but clearly compilers have got a lot more clever since then.

Probably. Certainly the open source ones have. And this isn't even gcc or llvm, because they don't handle the weirdness of 8 bit ISAs very well, but a compiler with a lot less work in it.
 

Offline peter-hTopic starter

  • Super Contributor
  • ***
  • Posts: 3694
  • Country: gb
  • Doing electronics since the 1960s...
Re: GCC ARM32 comparison question
« Reply #29 on: June 18, 2022, 12:46:14 pm »
Quote
I only need source code, I have a z80 compiler

Yes; a current one.

I did another project using an H8/323 in 1997. We used the Hitech C compiler then, which was about £350. That worked well. There was also an open source compiler around (GNU?) which Hitachi were giving away on a CD but it produced hugely bloated code; about 2x bigger than the Hitech one.

Further back I used some Zilog-distributed MUFOM tools. Mainly their z280 assembler (I was the first Z280 design-in in Europe, according to Zilog) but there was also a C compiler which was so bloated nobody dared use it for anything real. And a Z8000 compiler, similarly bloated. Reportedly those compilers were generated with YACC.

This discussion applies to current tools, and I accept that.
« Last Edit: June 18, 2022, 12:58:22 pm by peter-h »
Z80 Z180 Z280 Z8 S8 8031 8051 H8/300 H8/500 80x86 90S1200 32F417
 

Online dietert1

  • Super Contributor
  • ***
  • Posts: 2063
  • Country: br
    • CADT Homepage
Re: GCC ARM32 comparison question
« Reply #30 on: June 18, 2022, 01:02:50 pm »
Some days ago i made a quick test for a STM32L476 board using IAR IDE. There were some float variables and it used them as intended, except the link map contained lots of double support functions. I found that for invocation of printf all float values got extended to doubles. Probably caused by some project default setting like MISRA or the like.

Regards, Dieter
 

Online Nominal Animal

  • Super Contributor
  • ***
  • Posts: 6242
  • Country: fi
    • My home page and email address
Re: GCC ARM32 comparison question
« Reply #31 on: June 18, 2022, 01:19:34 pm »
Some days ago i made a quick test for a STM32L476 board using IAR IDE. There were some float variables and it used them as intended, except the link map contained lots of double support functions. I found that for invocation of printf all float values got extended to doubles. Probably caused by some project default setting like MISRA or the like.
It's the argument promotion rules I mentioned above that causes that; printf() being a variadic function.

See if there is a flag to disable printf() float support altogether, and if the C library used in IAR IDE supports strfromf() (standard C since C99).  Since strfromf() is not a variadic function, floats are passed without promoting them to double.  It is not as powerful as printf(), because the format string must begin with a %, followed by optional size and/or precision, followed by one of the conversion specifiers valid for floats (AaEeFfGg).

The idea is that you use a small buffer on stack to convert the float to a string first, say

    char  tempbuf[12];
    if (strfromf(tempbuf, sizeof tempbuf, "%.3f", floatvar) >= (int)sizeof tempbuf) {
        /* Oops, tempbuf[] was not large enough! */
    } else {
        /* Print tempbuf as a string */
    }

It's not optimal for sure; just a workaround.

When using GCC, the -Wdouble-promotion parameter can be very useful: whenever the compiler decides to do this, this warning flag causes the compiler to emit a warning.
 

Offline brucehoult

  • Super Contributor
  • ***
  • Posts: 4028
  • Country: nz
Re: GCC ARM32 comparison question
« Reply #32 on: June 18, 2022, 01:31:14 pm »
Quote
I only need source code, I have a z80 compiler

Yes; a current one.

Naturally.

Quote
I did another project using an H8/323 in 1997. We used the Hitech C compiler then, which was about £350. That worked well. There was also an open source compiler around (GNU?) which Hitachi were giving away on a CD but it produced hugely bloated code; about 2x bigger than the Hitech one.

Further back I used some Zilog-distributed MUFOM tools. Mainly their z280 assembler (I was the first Z280 design-in in Europe, according to Zilog) but there was also a C compiler which was so bloated nobody dared use it for anything real. And a Z8000 compiler, similarly bloated. Reportedly those compilers were generated with YACC.

I used all kinds of awful compilers 40 years ago too. And companies thought they could charge a fortune for rubbish.

My first C programming was on a Z8000 machine running Unix, as it happens, in 1983 if I recall correctly. Prior to that any high level language programming I'd done on a microcomputer was in either an interpreter or a compiler to bytecode and then interpret that, so anything that compiled to native code was a huge speed improvement.

Quote
This discussion applies to current tools, and I accept that.

More to the point, it applies to what the C language permits or doesn't permit, not to the quality of any particular compiler, though I'm certainly prepared to take compilers producing reasonable code as existence proofs that C doesn't mandate bad code, even on such crude machines as a z80 or 6502.
 

Online ataradov

  • Super Contributor
  • ***
  • Posts: 11238
  • Country: us
    • Personal site
Re: GCC ARM32 comparison question
« Reply #33 on: June 18, 2022, 04:43:30 pm »
Take the Z80. HL holds a uint16_t x and you are doing x << 4.

I very explicitly said that it is guaranteed to be faster on 16- and 32- bit CPUs. Z80 is not one of them.

Re your example, that is wrong code :) because anybody adding up even two bytes should know the result can be > 255.
It is just an example. Believe me, you will be bitten by the lack of promotion and you would create similar topic if they were not there.

How to implement promotions is not a simple topic, and in the end you just need to decide something. For concrete examples, you can look at how it is handled in more modern languages like Rust, Go, Swift. They all made their own slightly different decisions.
« Last Edit: June 18, 2022, 04:49:24 pm by ataradov »
Alex
 

Online SiliconWizard

  • Super Contributor
  • ***
  • Posts: 14447
  • Country: fr
Re: GCC ARM32 comparison question
« Reply #34 on: June 18, 2022, 05:58:43 pm »
Have you read the rationale document posted by newbrain?
 

Online ataradov

  • Super Contributor
  • ***
  • Posts: 11238
  • Country: us
    • Personal site
Re: GCC ARM32 comparison question
« Reply #35 on: June 18, 2022, 06:07:18 pm »
Have you read the rationale document posted by newbrain?
Is this for me? If so, yes, I've read this many years ago.

From my point of view, the best way to approach promotions is to allow only non-losing widening promotions. There is a good discussion on that here https://internals.rust-lang.org/t/implicit-widening-polymorphic-indexing-and-similar-ideas/1141

Prohibiting everything results in a lot of pointless castings and doing what C did results in a lot of confusion.
Alex
 

Online SiliconWizard

  • Super Contributor
  • ***
  • Posts: 14447
  • Country: fr
Re: GCC ARM32 comparison question
« Reply #36 on: June 18, 2022, 06:11:40 pm »
Have you read the rationale document posted by newbrain?
Is this for me? If so, yes, I've read this many years ago.

No, sorry for not quoting the OP. That was for the OP.
And I'm again not saying that their decision was the right one, but at least we more or less know why C is defined this way.
 

Offline Siwastaja

  • Super Contributor
  • ***
  • Posts: 8168
  • Country: fi
Re: GCC ARM32 comparison question
« Reply #37 on: June 18, 2022, 07:21:01 pm »
- on CPUs which are mostly not natively int sized (most of the 8/16 bit stuff like the Z80 etc) it bloats a lot of stuff, and slows it down, a lot

Of course not! You still don't get it. C is not a "portable assembler". C is a high level language, with the concept of abstract C machine. Compiler is totally allowed to produce optimum code. Compiler only has to prove the result is correct, according to the standard. The problem is you thinking "optimization" as some separate step, as if programmer writes "portable assembler" first, then compiler translates it 1:1 to bloated code, and then optimizer trying to do something about it. It's not like this. Really, programmer gives high-level description of program, using standard language called C. Then compiler produces machine code, the only requirement being it must produce exactly the correct result. It does not matter how the compiler achieves this. Of course compilers try to do this in optimum way.
 

Online SiliconWizard

  • Super Contributor
  • ***
  • Posts: 14447
  • Country: fr
Re: GCC ARM32 comparison question
« Reply #38 on: June 18, 2022, 07:33:25 pm »
Compilers build an abstract description of the source code before translating that to assembly.
Only in the very old and simple compilers both steps were more or less combined (like say, with the original Turbo Pascal compiler.)

 

Offline nctnico

  • Super Contributor
  • ***
  • Posts: 26896
  • Country: nl
    • NCT Developments
Re: GCC ARM32 comparison question
« Reply #39 on: June 24, 2022, 12:29:10 am »
OK, sure, but nobody answered my question :)

Except in terms of "it is the C standard, so accept it".

ISTM that this "promotion to int of almost everything shorter" is an attempt to prevent bad coding leading to what has since for ever been known in the embedded world (whether manually blowing fusible link PROMs one byte at a time, or asm, or C or whatever) as "integer overflow". The cost of doing this is

- on CPUs which are mostly not natively int sized (most of the 8/16 bit stuff like the Z80 etc) it bloats a lot of stuff, and slows it down, a lot
The designers of the C language already thought of that: the size of an int (integer) isn't fixed in C! On 16 bits CPUs an int is typically 16 bit. On 64 bit platforms an int usually is 32 bit.
There are small lies, big lies and then there is what is on the screen of your oscilloscope.
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf