integer promotion difference between gnu11 and gnu++11

Go To Last Post
20 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I read a few of the previous discussions on integer promotion, and none of them mentioned differences between avr-gcc (5.4.0) in gnu11 and gnu++11 modes (the default for avr-gcc and avr-g++).

When compiled with -Os as a C program, it optimizes the right shift to a single lsr.

#include <avr/io.h>

int main()
{
    uint8_t x = PORTB;
    x >>= 1;
    PORTB = x;
}

However as a C++ program (.cpp instead of .c), x gets promoted to signed int.

      in      r24, 0x18
      ldi     r25, 0x00
      asr     r25
      ror     r24
      out     0x18, r24

I've tried various combinations of casting, anding with 0xFF, and with 0x7F before shifting, all to no avail.  The only thing that worked was resorting to inline asm for the right shift.

asm ("lsr %0" : "+r"(x));

I thought both the C11 and C++11 standards specify the integer promotion, and how a compiler optimizes is implementation defined, but I was quite surprised that avr-gcc only does it the optimal way for C.

For portability and maintainability reasons I'd rather not use inline asm, so if anyone knows how to coerce avr-g++ to compile the left shift to "lsr", I'd like to know how.

 

This topic has a solution.

I have no special talents.  I am only passionately curious. - Albert Einstein

 

Last Edited: Mon. Feb 10, 2020 - 02:18 AM
This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

Yeah, I found this too some time back. The only workaround I could find was using division instead of shift.

#include <avr/io.h>

int main()
{
    uint8_t x = PORTB;
    x /= 2;
    PORTB = x;
}

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

El Tangas wrote:

Yeah, I found this too some time back. The only workaround I could find was using division instead of shift.

#include <avr/io.h>

int main()
{
    uint8_t x = PORTB;
    x /= 2;
    PORTB = x;
}

 

That works perfectly.  I vaguely remember reading a past post you made suggesting division instead of shift, but didn't remember to try it.  I don't think anyone mentioned it's only an issue for C++.

 

I have no special talents.  I am only passionately curious. - Albert Einstein

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I infer that the C++ optimizer has fallen down on the job.

That said, it produces the same result as the C optimizer.

 

The current rule for promoting low-ranked integer types is that types smaller

than int and lower-ranked signed types the same size as int are promoted to int.

Other promotions are to unsigned int.

 

Iluvatar is the better part of Valar.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0


ralphd wrote:

  I don't think anyone mentioned it's only an issue for C++.

 

Is it a coincidence that there are 219 forum thread results for "c++ bloat"?

 

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

skeeve wrote:

I infer that the C++ optimizer has fallen down on the job.

That said, it produces the same result as the C optimizer.

 

The current rule for promoting low-ranked integer types is that types smaller

than int and lower-ranked signed types the same size as int are promoted to int.

Other promotions are to unsigned int.

 

 

I did some more research, and it might not be a difference in the optimizer.  gnu11 mode may be omitting the promotion altogether, as is allowed by the standard (pg 15):

"the ‘‘integer promotions’’ require that the abstract machine promote the value of each variable tointsizeand then add the twoints and truncate the sum. Provided the addition of twochars can be done withoutoverflow, or with overflow wrapping silently to produce the correct result, the actual execution need onlyproduce the same result, possibly omitting the promotions. "

http://www.open-std.org/jtc1/sc2...

 

I've yet to dig through the c++11 standard, but read a couple posts from others that claim the promotion is mandatory in C++.  That still doesn't explain why x /= 2 generates lsr and x >>= 1 does not.

 

 

 

I have no special talents.  I am only passionately curious. - Albert Einstein

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ralphd wrote:
That still doesn't explain why x /= 2 generates lsr and x >>= 1 does not.

 

Be careful, they might decide it's a bug and "fix" /=2 angry

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Is there a mode where some compilers will list "what it's doing"...so as you read the source code it will indicate (with the integrated compiler commentary) what was/was not promoted & other things it has done.  For example, if it eliminates a variable (due to optimizer assuming it never varies, or a conditional that can never be true), it could so indicate.   If some code was unrolled, it could say that too.  Perhaps this would become to too messy, but interesting.

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

theusch wrote:
ralphd wrote:

 

  I don't think anyone mentioned it's only an issue for C++.

 

 

 

Is it a coincidence that there are 219 forum thread results for "c++ bloat"?

More precisely,

it's an issue for the particular C++ compiler.

C++ does not require the complained-of bloat.

Iluvatar is the better part of Valar.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I recently realized that despite starting to program in C more than 30 years ago, I've never read any of the official standards that were released after K&R.  So I'm now picking away a the >500 pages of ISO/IEC 9899:2018 C17.  One of the first things that caught my eye was footnote 82:

"The integer promotions are applied only: as part of the usual arithmetic conversions, to certain argument expressions, to the operands of the unary + , - , and ~ operators, and to both operands of the shift operators, as specified by their respective subclauses."

 

Note that the division operator is not listed.  Therefore one can rely on the fact that a compliant C17 compiler will not promote "x / 2", regardless of optimizations.  The same applies for using "x * 2" instead of "x << 1".

 

I have no special talents.  I am only passionately curious. - Albert Einstein

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I never noticed that either... At last we know the source of this behaviour.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You missed this

ralphd wrote:

I recently realized that despite starting to program in C more than 30 years ago, I've never read any of the official standards that were released after K&R.  So I'm now picking away a the >500 pages of ISO/IEC 9899:2018 C17.  One of the first things that caught my eye was footnote 82:

"The integer promotions are applied only: as part of the usual arithmetic conversions, to certain argument expressions, to the operands of the unary + , - , and ~ operators, and to both operands of the shift operators, as specified by their respective subclauses."

 

Note that the division operator is not listed.  Therefore one can rely on the fact that a compliant C17 compiler will not promote "x / 2", regardless of optimizations.  The same applies for using "x * 2" instead of "x << 1".

 

 

You missed the "as part of the usual arithmetic conversions" part. The "+, -, and ~" are referring to the unary operators. So addition, subtraction, multiplication, and division all invoke integer promotion.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

christop wrote:

You missed this

ralphd wrote:

 

I recently realized that despite starting to program in C more than 30 years ago, I've never read any of the official standards that were released after K&R.  So I'm now picking away a the >500 pages of ISO/IEC 9899:2018 C17.  One of the first things that caught my eye was footnote 82:

"The integer promotions are applied only: as part of the usual arithmetic conversions, to certain argument expressions, to the operands of the unary + , - , and ~ operators, and to both operands of the shift operators, as specified by their respective subclauses."

 

Note that the division operator is not listed.  Therefore one can rely on the fact that a compliant C17 compiler will not promote "x / 2", regardless of optimizations.  The same applies for using "x * 2" instead of "x << 1".

 

 

You missed the "as part of the usual arithmetic conversions" part. The "+, -, and ~" are referring to the unary operators. So addition, subtraction, multiplication, and division all invoke integer promotion.

 

 

Darn.  It seems you are correct.  "Usual arithmetic conversions" is defined in 6.3.1.8.

"Many operators that expect operands of arithmetic type cause conversions and yield result types in a similar way."

It doesn't enumerate those operators, but 6.5.5 defines "multiplicative operators" and says:

"The usual arithmetic conversions are performed on the operands"

 

Maybe I should just stick to assembler...

 

I have no special talents.  I am only passionately curious. - Albert Einstein

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

"Usual arithmetic conversions" is defined in 6.3.1.8.

There's a reason that "people who can interpret Language Standards Documents and come up with either "your code is wrong" or "the compiler is wrong"" get put into a separate class.  Sometimes highly valued. sometimes cursed at (frequently both.)

 

 :-)

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ralphd wrote:
Maybe I should just stick to assembler...
Not for then I wouldn't generate enlightenment due to threads of this kind.

 

"Dare to be naïve." - Buckminster Fuller

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

westfw wrote:
Sometimes highly valued. sometimes cursed at (frequently both.)
Valued and disappointed.

A compiler creator said I misinterpreted the computer language standard; still, a win-win as I corrected the application's source code and the compiler gained another test.

I, who find difficult, languages (natural or otherwise)

 

"Dare to be naïve." - Buckminster Fuller

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

avrfreaks does not support Opera. Profile inactive.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

theusch wrote:
Is it a coincidence that there are 219 forum thread results for "c++ bloat"?
C++ has more ways for a clueless programmer to write bad code, I suppose.  It also has ways for programmers to write better code.  The advantages of C++ increase as the complexity of the program increases. 

 

I just ran an experiment.  I found a simple C program on this site that blinks a LED.  It's all written in main.  I converted it to C++ and the resulting machine code is almost identical. The C++ program uses one more program memory byte than the C program.  The .hex files are identical except for the last 4 lines.

 

// C code

// #define F_CPU 2000000
#define F_CPU 2000    // for simulator cuz it's 1000 times slower.

#include <avr/io.h>
#include <util/delay.h>

#define BLINK_DELAY_MS 1000

 

int main(void)
{
PORTC.DIRSET = 0b10000000 ; // Set pin 7 to be output.

while(1){ // loop forever
    PORTC.OUTSET = 0b10000000 ; // set the output high.
    _delay_ms( BLINK_DELAY_MS ) ; // wait.
    PORTC.OUTCLR = 0b10000000 ; // set the output low.
    _delay_ms( BLINK_DELAY_MS ) ; // wait.
    }
}

 

 

// C++ code

// #define F_CPU 2000000
   #define F_CPU 2000   // for simulator because it's 1000 times slower

#include <util/delay.h>
#include "port_c_device.h"

#define BLINK_DELAY_MS 1000

 

int main(void)
{
(new Port_c_device)->Set_pins_direction_out(Port_device::Pin7);

while(1)  { // loop forever
    (new Port_c_device)->Set_out_pins_high(Port_device::Pin7);
    _delay_ms( BLINK_DELAY_MS ) ; // wait.
    (new Port_c_device)->Set_out_pins_low(Port_device::Pin7);
    _delay_ms( BLINK_DELAY_MS ) ; // wait.
    }
}

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

steve17 wrote:

theusch wrote:

Is it a coincidence that there are 219 forum thread results for "c++ bloat"?

 

C++ has more ways for a clueless programmer to write bad code, I suppose.  It also has ways for programmers to write better code.  The advantages of C++ increase as the complexity of the program increases. 

 

C++ also has many more ways for a clueful programmmer to write bad code too.  There are numerous blog posts and papers that document the problems C++ has, particularly for embedded systems.  Even worse is trying to mix C and C++ (à la Aruino) since the two languages have diverged so much over the past 30 years.  You can find a description of many of those differences in the first 20 pages of the draft proposal for a core C/C++ spec.

http://www.open-std.org/jtc1/sc2...

 

For non-embedded development I do prefer C++, but try to limit it to use cases where C++ makes the code simpler to write, understand, and maintain.  That means avoiding things like complex template classes, and "cool" constexpr hacks.

 

 

 

I have no special talents.  I am only passionately curious. - Albert Einstein

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I disagree.  C++ is just plain better.  If Atmel had a clue, they would supply the I/O device classes.  They'd probably be too cryptic, so I'd stick to mine anyway.  It's a bit ironic that classes were invented in Oslo.

 

I've only used templates once, and that was for PC code.  I don't know what constexpr is, and I guess I don't want to know.  smiley