Re: odd optimization differences of shift between C andC++

Jochen Barth <[email protected]> Thu, 31 Dec 2020 18:18:08 +0100
Newsgroups gmane.comp.hardware.avr.gcc
Message-ID <[email protected]>
Thanks. Did forget to mention that *16/256 instead of >>4 does insert 
some code to add ((2^n) -1) on negative values (because /16 and >>4 is 
not the same).

So a direct optimization on shr would be better.

Kind regards, Jochen


Am 30.12.20 um 12:19 schrieb David Brown:
> Hi,
>
> As a general point, never use "char" like this - use either uint8_t or
> int8_t depending on whether you want signed or unsigned.  Usually if you
> are doing shifts, it's uint8_t you want.
>
> The AVR compiler has trouble with some of these kinds of expressions.  I
> think it has something to do with the way gcc handles them (most cpus
> have instructions for multiple shifts or rotates) combined with the
> integer promotion.  The AVR gcc port has 16-bit int (it's the smallest
> size allowed by C and C++), which means everything has to be done with
> double registers, then there are peepholes and other optimisation passes
> that try to remove the redundant code.  A lot of that is manual work
> added to the compiler, and so there are bound to be combinations that
> have not been included.  Each suboptimal combination has to be spotted,
> then added to the compiler.
>
> The different rearrangements done earlier in the compile process can
> also mean that the internal code passed to the backend optimiser can
> vary substantially.  For example, with most gcc ports, you can split
> expressions into multiple local variables without it affecting the
> (optimised) results.  For the AVR, that does not always apply.
>
> This all means it is not easy to guess the source code to use to get the
> optimal results.  These three versions all give the same code for both C
> and C++, with shorter and faster code than 5 shifts (it has one
> nibble-swap, one shift and one mask).
>
> uint8_t u;
>
> void fooa(void) {
>      u = ((u & 0xe0) >> 5);
> }
>
> void foob(void) {
>      u = u / (1u << 5);
> }
>
> void fooc(void) {
>      u = (u >> 5) & 0x07;
> }
>
>
> You can also consider using the "-mint8" flag.  That breaks C
> compatibility but it can be useful for small programs, and can often
> result in better code.  Be very careful to stick to <stdint.h> types
> rather than "int" and "long" if you are trying this!
>
> David
>
>
>
> On 29/12/2020 21:00, Jochen Barth wrote:
>> I'm using Arduino 1.8.13 with avr-g++ (GCC) 7.3.0.
>>
>> char x;
>> x >>= 5 generates asm (promoted to 16 bit signed) with 5* asr+ror;
>> x = x * (1<<(8-5)) / 256
>> generates 5* asr (without type promotion).
>>
>> Kind regards,
>> Jochen
>>
>>
>> Weddington, Eric Mon, 08 Nov 2010 04:25:48 -0800
>>> -----Original Message-----
>>> From:
>>> [email protected]
>>> [mailto:avr-gcc-list-bounces+eric.weddington=atmel....@nongnu.
>>> org] On Behalf Of William "Chops" Westfield
>>> Sent: Sunday, November 07, 2010 9:58 PM
>>> To: [email protected]
>>> Subject: [avr-gcc-list] odd optimization differences of shift
>>> between C andC++
>>>
>>>
>>>
>>> Is this expected? Is it bug-worthy ?
>>>
>> I would say that it's bug-worthy, as a missed optimization bug. FYI, there are
>> more bugs with the C++ compiler for the AVR than for the C compiler. So I'm not
>> totally surprised that this is happening.
>>
>> BTW, could you please subscribe to the mailing list? That way your posts do not
>> have to be approved by hand.
>>
>> _______________________________________________
>> AVR-GCC-list mailing list
>> [email protected]
>> http://lists.nongnu.org/mailman/listinfo/avr-gcc-list
>>