Poor code generation for conditional statements

Go To Last Post
67 posts / 0 new

Pages

Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Another annoyance:

1)

f(b ? x : y);

2)

if (b)
{
    f(x);
}
else
{
    f(y);
}

1) generates bigger code than 2).

b is a bool, x and y are enum values.

AS6, avrgcc 4.6.2.

EDIT: Improve subject title

Sid

Life... is a state of mind

Last Edited: Wed. Sep 19, 2012 - 02:16 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

In other cases, the opposite is true.

(In the case at hand now, b is an expression testing an enum variable for equality with an enum value.)

This is pretty darn lame.

Sid

Life... is a state of mind

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

Some GCC compiler options and comparison disassembly might help some of us figure out what is going wrong in your specific case, as would an idea of just how different the compile sizes are.

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

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

Also what happens if you compile the same code for i386 using gcc?

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

I use -Os. My code is C++ but that probably doesn't matter to the code generation in this case.

I will try to recreate these problems in a form I can post here, although it's not clear to me what benefit there is in doing that.

I posted this as a warning to people concerned about the size of their code. Surely anybody that cares about code size can try out both forms themselves ?

I also question the relevance of "how different the compile sizes are". Why should there be any difference ?

Sid

Life... is a state of mind

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

Quote:

I also question the relevance of "how different the compile sizes are". Why should there be any difference ?

If it were 3 bytes I doubt anyone would care and they'd just stick with the syntax they think is more readable. If it was 300 bytes then clearly there's an argument for using the smaller version.
Quote:

code is C++ but that probably doesn't matter to the code generation in this case

I think SprinterSB has said that g++ is quite different from gcc so I don't think it's necessarily the case that the code generation models will always be identical for straight C syntax. There's been a lot more work on the C than the C++ compiler.

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

Quote:

I will try to recreate these problems in a form I can post here, although it's not clear to me what benefit there is in doing that.

Without it, there's no way for it to us to verify it and determine what is the reason for the size increase. You'd done the equivalent of "I press a button and my computer stops working" without telling anyone what button, what computer, what stops working or how you're pressing it.

As a test, I just compiled these:

#include 

void f(uint8_t a)
{
	PORTA = a;
}

int main(void)
{
	bool b = PORTC;
	
	if (b)
	{
		f(42);
	}
	else
	{
		f(24);
	}
}
#include 

void f(uint8_t a)
{
	PORTA = a;
}

int main(void)
{
	bool b = PORTC;
	
	f(b ? 42 : 24);
}

And the second one was two bytes shorter, using AVR-G++ and -Os optimizations.

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

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

Adding code to existing program.

// Add to global scope:
#include 

volatile enum Toggle { Off, On, Unknown } toggle;

volatile bool b;

char xxx[99];

void f(int i)
{
    sprintf(xxx, "% d", i);
}

// Add one of these to main() :

// 1)
    f(toggle == Off ? On : Off);

// 2)
    if (toggle == Off)
    {
        f(On);
    }
    else
    {
        f(Off);
    }

// 3)
    f(b ? On : Off);

// 4)
    if (b)
    {
        f(On);
    }
    else
    {
        f(Off);
    }

2) costs 2 bytes more than 1)
4) costs 12 bytes more than 3)
I saw bigger differences in my real code, but this illustrates the point.

I also see that in both these example cases, the ternary operator "wins". In my other code, the opposite was true. But I don't want to waste any more time on trying to recreate it.

sprintf(), volatile declarations and the other stuff in the global scope is not a part of the problem.

Sid

Life... is a state of mind

Last Edited: Tue. Sep 18, 2012 - 09:16 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

It would be interesting to see just what ASM is generated by Mr Gardener. Even better would be some G++ test code.

I get exactly the same code for both foo() and bar() with avr-gcc -Os

extern void f(unsigned char c);
extern volatile unsigned char b;

void foo(void)
{
	if (b != 0)
		f(1);
	else 
		f(2);
}

void bar(void)
{
	f( (b != 0) ? (1) : (2));
}
.global	bar
	.type	bar, @function
bar:
.LFB3:
.LM1:
/* prologue: function */
/* frame size = 0 */
.LM2:
	lds r24,b
	tst r24
	brne .L2
	ldi r24,lo8(2)
	rjmp .L3
.L2:
	ldi r24,lo8(1)
.L3:
	call f
/* epilogue start */
.LM3:
	ret

Now, I am sure that you could shave a cycle off this. I doubt that most people would care. Nor would they be calling their distributors for bigger chips.

David.

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

abcminiuser wrote:
You'd done the equivalent of "I press a button and my computer stops working" without telling anyone what button, what computer, what stops working or how you're pressing it.

I disagree. With the information I posted before you said that, it should be possible for many people to try out if statements instead of ternary operators and vv with their existing code.

While I agree that it is useful to have specific steps to go through - and I did say that I would waste time on finding them - you go too far with that accusation.

Sid

Life... is a state of mind

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

OK a complete program that can be used to test:

#include 
#include 

#define VARIANT 1

volatile enum Toggle { Off, On, Unknown } toggle;

volatile bool b;

char xxx[99];

void f(int i)
{
    sprintf(xxx, "%d", i);
}

	
int main(void) {
	// Add one of these to main() :

#if VARIANT == 1
	f(toggle == Off ? On : Off);

#elif VARIANT == 2
	if (toggle == Off)
	{
		f(On);
	}
	else
	{
		f(Off);
	}

#elif VARIANT == 3
	f(b ? On : Off);

#else
	if (b)
	{
		f(On);
	}
	else
	{
		f(Off);
	}
#endif	
	while(1) {
	}		
}

VARIANT == 1

	f(toggle == Off ? On : Off);
  d2:	20 91 c8 00 	lds	r18, 0x00C8
  d6:	81 e0       	ldi	r24, 0x01	; 1
  d8:	90 e0       	ldi	r25, 0x00	; 0
  da:	22 23       	and	r18, r18
  dc:	11 f0       	breq	.+4      	; 0xe2 
  de:	80 e0       	ldi	r24, 0x00	; 0
  e0:	90 e0       	ldi	r25, 0x00	; 0
  e2:	0e 94 4a 00 	call	0x94	; 0x94 
  e6:	ff cf       	rjmp	.-2      	; 0xe6 

VARIANT == 2

	if (toggle == Off)
  d2:	80 91 c8 00 	lds	r24, 0x00C8
  d6:	88 23       	and	r24, r24
  d8:	29 f4       	brne	.+10     	; 0xe4 
	{
		f(On);
  da:	81 e0       	ldi	r24, 0x01	; 1
  dc:	90 e0       	ldi	r25, 0x00	; 0
  de:	0e 94 4a 00 	call	0x94	; 0x94 
  e2:	04 c0       	rjmp	.+8      	; 0xec 
	}
	else
	{
		f(Off);
  e4:	80 e0       	ldi	r24, 0x00	; 0
  e6:	90 e0       	ldi	r25, 0x00	; 0
  e8:	0e 94 4a 00 	call	0x94	; 0x94 
  ec:	ff cf       	rjmp	.-2      	; 0xec 

VARIANT == 3

	f(b ? On : Off);
  d2:	80 91 64 00 	lds	r24, 0x0064
  d6:	90 e0       	ldi	r25, 0x00	; 0
  d8:	0e 94 4a 00 	call	0x94	; 0x94 
  dc:	ff cf       	rjmp	.-2      	; 0xdc 

VARIANT == 4

	if (b)
  d2:	80 91 64 00 	lds	r24, 0x0064
  d6:	88 23       	and	r24, r24
  d8:	29 f0       	breq	.+10     	; 0xe4 
	{
		f(On);
  da:	81 e0       	ldi	r24, 0x01	; 1
  dc:	90 e0       	ldi	r25, 0x00	; 0
  de:	0e 94 4a 00 	call	0x94	; 0x94 
  e2:	04 c0       	rjmp	.+8      	; 0xec 
	}
	else
	{
		f(Off);
  e4:	80 e0       	ldi	r24, 0x00	; 0
  e6:	90 e0       	ldi	r25, 0x00	; 0
  e8:	0e 94 4a 00 	call	0x94	; 0x94 
  ec:	ff cf       	rjmp	.-2      	; 0xec 

And apologies I built those with the C compiler not the C++ compiler but I imagine that in this case it IS going to be a similar situation.

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

PS I'm not going to edit that as it has % signs but I love what the compiler did with VARIANT == 3!

Oh and isn't it a shame the compiler uses uint16_t for "bool". Personally I've be casting some bitfields onto GPIOR0 and getting the CBI/SBI benefit!

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

clawson wrote:
Quote:

I also question the relevance of "how different the compile sizes are". Why should there be any difference ?

If it were 3 bytes I doubt anyone would care and they'd just stick with the syntax they think is more readable.

That would be the way I would do it on richer processors. It may not matter for hobby users either - what difference does it make whether the chip costs 50 cents or two dollars ?

But if you work to mass produce something it may end up costing a lot, and it's a bad habit.

If you work on creating a library, it's even more important.

And either way the compiler should take care of that sort of thing - but it doesn't.

Sid

Life... is a state of mind

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

Thankyou for posting the test code.

I compiled it, and looked at the ASM. It looks horrendous for each option. Edit. I was using WinAvr-2010.

The reason being that you are using a variadic function (I think).
If you simply have:

extern void f(int i);

the compiler treats it with common sense.
avr-gcc tries to inline local functions. This works fine for most regular simple functions.

If you were to use a regular function, things look better:

#include 
void f(int i)
{
    itoa(i, xxx, 10);
//    sprintf(xxx, "% d", i);
}

David.

Last Edited: Tue. Sep 18, 2012 - 09:45 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

That would be the way I would do it on richer processors. It may not matter for hobby users either - what difference does it make whether the chip costs 50 cents or two dollars ?

I worked for a company buying two million mega16 per annum so I know about the economies of scale. But the point about code optimisation is that it's only necessary when you get close to a code boundary. Now if we'd been using 8,400 bytes of that mega16 I can see a strong argument for going on a byte hunt to find the 208 bytes to get us down into 8K device territory and save $0.20 * 2,000,000. But if the app is at 14-15K I don't see the point until MCU vendors start making devices in fractional binary multiples! ;-)
Quote:

The reason being that you are using a variadic function (I think)

Forget about the sprintf() bloat - that's incidental to the test program. One might as well just write 'i' to a volatile destination such as an SFR. The key difference in the four variants are shown in the four segments I posted above.

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

clawson wrote:
isn't it a shame the compiler uses uint16_t for "bool".

I have noticed the code size penalty for using bool and I am actually contemplating using uint8_t and zero/nonzero instead. You're saying that there is actually a data size penalty as well.

Yes, that is a darn shame.

Sid

Life... is a state of mind

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

Quote:

I disagree. With the information I posted before you said that, it should be possible for many people to try out if statements instead of ternary operators and vv with their existing code.

But without a concrete test case, we will all get wildly different results. I guess it depends on what you want to do: if it's make vague statements then you've achieved you goal, if it's to find out the source of the issue and hopefully get it corrected, you haven't.

Image calling up a car mechanic, and saying "My car makes a funny noise when I turn the wheel. It's a Ford." What would you expect as a response?

Quote:

Oh and isn't it a shame the compiler uses uint16_t for "bool". Personally I've be casting some bitfields onto GPIOR0 and getting the CBI/SBI benefit!

Is it? The compiler is required to store the bool value as a 16-bit when you pass it to your function (as the parameter is of type int, which is 16-bits) but the actual testing is done as an 8-bit value.

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

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

Quote:

using uint8_t and zero/nonzero instead.

That still uses 8 bits to hold a 1 bit state. Atmel thoughtfully put GPIR0 in range of SBI/CBI/SBIS/CBIS so it can be used to hold 8 of your most time sensitive "bool"s. Sadly most modern AVRs have GPIOR1 and GPIOR2 out of the 0x00.0x1F range so they aren't as useful. Atmel redeemed themselves with the Xmega by putting SIXTEEN 8bit general purpose flag registers in range of SBI/CBI - more bit variables that you can possibly know what to do with!

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

abcminiuser wrote:
But without a concrete test case, we will all get wildly different results. I guess it depends on what you want to do: if it's make vague statements then you've achieved you goal, if it's to find out the source of the issue and hopefully get it corrected, you haven't.

I already explained why I posted this. I don't believe for one second that the compiler is going to be changed, no matter how well I describe it.

And even you were able to find a size difference in no time with the information you deem useless.

Sid

Life... is a state of mind

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

Quote:
Image calling up a car mechanic, and saying "My car makes a funny noise when I turn the wheel. It's a Ford." What would you expect as a response?

It's a Ford.

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

Based on Dean's responses here, I want to ask a question or two about this forum:

If you come across a compiler "feature" that others may benefit from knowing about, is it not desirable that you post instructions for what others should look for in their source code ?

Is everybody so caught up in the "I have to fix the specific instance of that specific guy's specific problem" that the general case is not interesting ?

I don't mind wasting time on trying different things - that's how I found this problem in the first place - but if it is a general demand in here that everything must be specified to the smallest detail I will not share findings like this again.

Sid

Life... is a state of mind

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

david.prentice wrote:
The reason being that you are using a variadic function (I think).
If you were to use a regular function, things look better

That is why I used sprintf() for the example. The same goes for the volatile declarations. I wanted to make sure the compiler didn't throw away what I was demonstrating.

Sid

Life... is a state of mind

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

abcminiuser wrote:
Quote:

Oh and isn't it a shame the compiler uses uint16_t for "bool".

Is it? The compiler is required to store the bool value as a 16-bit when you pass it to your function (as the parameter is of type int, which is 16-bits) but the actual testing is done as an 8-bit value.


The bool isn't passed to the function. An enum value is.

Sid

Life... is a state of mind

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

Quote:

The bool isn't passed to the function. An enum value is.

Right you are, Cliff's test code passes an enum value into the function. That said, the disassembly for it still shows the compiler testing the boolean as an 8-bit value, and passing the enum to the function as 16-bit, expected due to the int parameter type.

Quote:

If you come across a compiler "feature" that others may benefit from knowing about, is it not desirable that you post instructions for what others should look for in their source code ?

For me personally I prefer to understand why something is happening. For example, it could be an issue with side-effects forcing the compiler to generate code on way, or a restriction in the standard, or a mistake in the test case, or compiler options, or any other number of things. Sweeping generalizations with no concrete example don't work well, in my experience, for compilers due to the complexities of all the things that can influence the output.

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

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

abcminiuser wrote:
Quote:
If you come across a compiler "feature" that others may benefit from knowing about, is it not desirable that you post instructions for what others should look for in their source code ?

For me personally I prefer to understand why something is happening.


So you are saying that if I don't supply you with enough details that you can understand why it happens then I should not post if for the benefit of others that may be interested ?

Sid

Life... is a state of mind

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

If you see a problem, please post a compilable program that shows the problem. You've failed on both points so far. People had to pull a few lines out of you, and make it compilable them selves. And still no problem has been seen. Of course you can see a couple of cycles/bytes difference but what's the problem with that? If you really need full control over every byte/cycle then write your program in assembler (or parts of it).

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

Although Chauncey usually comes over as being arrogant I have to agree with him on this occasion. It's a very useful observation and other compiler users could benefit from knowing that there is this potential difference in the code generation. Nothing wrong with pointing that out.

I think folks here (and I include myself) often see claims of "poor code generation" as some kind of insult to our beloved compiler and strive to defend its reputation and to do so we need to see code samples and compare the code generation to see if there is a "problem" at all (we're all skeptics too). But I don't think it hurts to recognize that it's not always perfect and in the absence of any likelihood of a problem actually being fixed (only 2-3 people here have the skill to do that and I'm not one of them) it maybe is as well to know what could be suspect or best avoided.

If nothing else the example shows that it's a bit of a shame that both bool and enum are held in 16 bits on a micro where packing them into 8 (or less) could be a distinct advantage.

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

Yes. It is an interesting subject. No, I doubt if it makes much difference in practice.

However, Cliff's examples showed you how the compiler behaved with a certain data set.

You won't always have a boolean { 0, 1} set with an enum set {0, 1}. Personally, I was rather impressed that the Optimiser had taken advantage in (3).
You could argue that it should have made the same decision with input sequences 1, 2, 4.

I doubt if you will always use the same enum sets.

It should not be too difficult for you to publish test suites. Then you would get a lot more enthusiasm.

So far, I would be perfectly happy with any of the translations shown by Cliff.

If you can devise some test sets that create large or inefficient code, please show them.

Meanwhile, most people, most of the time just obey the maxim "use straightforward code, use appropriate variable types". It works pretty well for most embedded programming.

David.

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

snigelen wrote:
If you see a problem, please post a compilable program that shows the problem.

Okay. Now two high profile users are saying that it is not interesting to know that swapping if statements for ternary operators and vv may affect your code size, unless a complete compilable reproduction is made available.

That is a clear message.

snigelen wrote:
You've failed on both points so far.

If I have not yet posted enough code, I give up.

I will keep this sort of findings to myself in the future.

snigelen wrote:
People had to pull a few lines out of you, and make it compilable them selves.

That is utter bullcrap. As soon as I was asked for code, I said that I would work on creating it. Less than three hours later - in the middle of the night here - I posted it, with clear instructions on how to go about compiling it.

I take it you're another one of those who like to pretend you don't know what uint8_t is ?

snigelen wrote:
And still no problem has been seen.

I am pretty sure I'm not the only one that sees the clearly demonstrated anomalies as a problem.

snigelen wrote:
Of course you can see a couple of cycles/bytes difference but what's the problem with that? If you really need full control over every byte/cycle then write your program in assembler (or parts of it).

It doesn't bother you that the compiler generates plenty of unneeded code for a functionally identical test ? That sort of thing calls for using assembly ?

Sid

Life... is a state of mind

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

My sincerest apologies Chauncy, I didn't see your test code in the flurry of posts made shortly after my own - I thought Cliff was the one to cook that one up. Disregard my later complainings (although I would personally also like to see the actual resulting disassembly).

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

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

Quote:

That sort of thing calls for using assembly ?

Sadly the answer is Yes if you want complete control over code generation. Otherwise you are putting yourself in the hands of the compiler and optimiser authors. Further, unlike an AVR targetted compiler such as CodeVision they are in part up against the way in which GCC is actually implemented by a high level front end that converts the C into a kind of "meta language" and then a target specific code generator that then interprets that meta language and generates target code. This is not optimal in all cases. What's more it would appear that although the high level stuff is supposedly agnostic about the target you can bet that the i386 and ARM developers get more say in changes that may produce them more optimal output than some low-use 8bit micro compiler even if it actually has a detrimental affect for them. You can even see this of the development of the compiler from 3.x version up to the now 4.8, the AVR code generator actually seems to be getting less efficient than some old version (though the other side of that may be that it's more accurate/fault free).

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

Thank you for clearing that up, Dean. I feel much better about it now.

clawson wrote:
Quote:

That sort of thing calls for using assembly ?

Sadly the answer is Yes if you want complete control over code generation. Otherwise you are putting yourself in the hands of the compiler and optimiser authors.

Yes, I understand that. But this thread illustrates that you can actually improve code generation by taking deliberate steps with your C/C++ source code, instead of paying the high price associated with creating and maintaining programs in assembly.

I find it frustrating that it is possible to improve the generated code this way, but it sure beats resorting to assembly.

Sid

Life... is a state of mind

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

Quote:

I find it frustrating that it is possible to improve the generated code this way

Hasn't it forever been thus?

As Johan Ekdahl is often keen to point out it's only really necessary to "optimise" and start looking at code generation when you are facing some kind of barrier you need to cross (like the code must be smaller or faster to achieve the job or fit) but when it becomes necessary you can almost always find some way to "massage" the compiler into producing more optimal code sequences for some sections, especially those that look "bloaty" in the .lss

BTW just for giggles I built the four variants using MS VS 2008:

   f(toggle == Off ? On : Off);
0041143E  xor         eax,eax 
00411440  cmp         dword ptr [toggle (4171A4h)],0 
00411447  sete        al   
0041144A  push        eax  
0041144B  call        f (4110FFh) 
00411450  add         esp,4 
   if (toggle == Off)
0041143E  cmp         dword ptr [toggle (4171A4h)],0 
00411445  jne         main+33h (411453h) 
   {
      f(On);
00411447  push        1    
00411449  call        f (4110FFh) 
0041144E  add         esp,4 
   }
   else
00411451  jmp         main+3Dh (41145Dh) 
   {
      f(Off);
00411453  push        0    
00411455  call        f (4110FFh) 
0041145A  add         esp,4 
   }
   if (toggle == Off)
0041143E  cmp         dword ptr [toggle (4171A4h)],0 
00411445  jne         main+33h (411453h) 
   {
      f(On);
00411447  push        1    
00411449  call        f (4110FFh) 
0041144E  add         esp,4 
   }
   else
00411451  jmp         main+3Dh (41145Dh) 
   {
      f(Off);
00411453  push        0    
00411455  call        f (4110FFh) 
0041145A  add         esp,4 
   }
   if (b)
0041143E  movzx       eax,byte ptr [b (4171A3h)] 
00411445  test        eax,eax 
00411447  je          main+35h (411455h) 
   {
      f(On);
00411449  push        1    
0041144B  call        f (4110FFh) 
00411450  add         esp,4 
   }
   else
00411453  jmp         main+3Fh (41145Fh) 
   {
      f(Off);
00411455  push        0    
00411457  call        f (4110FFh) 
0041145C  add         esp,4 
   }

So even a "Professional" compiler has various solutions for this.

BTW look what VS2008 does with this while(1):

   while(1) {
0041145F  mov         eax,1 
00411464  test        eax,eax 
00411466  je          main+4Ah (41146Ah) 
   }      

Talk about a missed optimisation - why on earth is it testing a constant 1 register? I suppose at 3GHz, 1TB of disk and 6GB of DRAM it doesn't matter?

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

Quote:
I saw bigger differences in my real code, but this illustrates the point.
No it doesn't. Your original point was that ?: created bigger code, but in both your examples it produced smaller code.

Regards,
Steve A.

The Board helps those that help themselves.

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

ChaunceyGardiner wrote:
It may not matter for hobby users either - what difference does it make whether the chip costs 50 cents or two dollars ?

But if you work to mass produce something it may end up costing a lot, and it's a bad habit.

If you work on creating a library, it's even more important.

And either way the compiler should take care of that sort of thing - but it doesn't.

If you use a compiler in a professional environment and it's crucial to your application that 2 bytes are saved, then hire a professional contractor that offers professional compiler support.

Using a software in a professional setup without caring for professional support if professional support is essential, that is not professional.

avrfreaks does not support Opera. Profile inactive.

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

Koshchi wrote:
Quote:
I saw bigger differences in my real code, but this illustrates the point.
No it doesn't. Your original point was that ?: created bigger code, but in both your examples it produced smaller code.

My original point was that the ternary operator generates bigger code in some cases that I was facing in what I am working on.

Soon after posting that, I found that the opposite is also true in other cases. That is what I wrote an hour later, before anyone had responded.

In working to produce code to demonstrate this - per request - I came up with two examples that generates smaller code for the ternary operator. That should be enough to illustrate the problem: the compiler produces differently sized code for what should be identical.

Sid

Life... is a state of mind

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

SprinterSB wrote:
ChaunceyGardiner wrote:
But if you work to mass produce something it may end up costing a lot, and it's a bad habit.

If you work on creating a library, it's even more important.

And either way the compiler should take care of that sort of thing - but it doesn't.

If you use a compiler in a professional environment and it's crucial to your application that 2 bytes are saved,

I have already demonstrated in this topic that the difference is 12 bytes for a simple test case. I have also stated that the difference is even bigger in other cases.

A compiler that potentially wastes space every time you test an expression is something that matters in many settings, and that needs to be addressed by developers. This is particularly important when it comes to creating libraries.

SprinterSB wrote:
then hire a professional contractor that offers professional compiler support.

I am a professional. I offer "compiler support" in that I work to find out how the compiler performs and choose the better ways to implement solutions. This topic illustrates that - I posted my findings to help fellow developers, something that clearly did not make it through the noise blanket here.

Sid

Life... is a state of mind

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

The thing I like about freaks is that it is a nice friendly forum :D

regards
Greg

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

ChaunceyGardiner wrote:
I offer "compiler support" in that I work to find out how the compiler performs and choose the better ways to implement solutions. This topic illustrates that - I posted my findings to help fellow developers, something that clearly did not make it through the noise blanket here.
Since there was indeed quite a lot of noise - can you maybe summarize what exactly were your findings?

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

My findings were that the compiler generates differently sized code for if statements and equivalent ternary operators.

The code size should be the same, but it isn't. In some cases, the if statement results in bigger code, in other cases the ternary operator results in bigger code.

Sid

Life... is a state of mind

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

clawson wrote:
BTW look what VS2008 does with this while(1):

   while(1) {
0041145F  mov         eax,1 
00411464  test        eax,eax 
00411466  je          main+4Ah (41146Ah) 
   }      

Talk about a missed optimisation - why on earth is it testing a constant 1 register? I suppose at 3GHz, 1TB of disk and 6GB of DRAM it doesn't matter?


MS has been headed downhill for quite some time when it comes to the C/C++ compiler and the IDE. It's still a good compiler, but it's not on the cutting edge like it used to be.

My guess is that they have been focusing most of their efforts on C# and managed code as that has been the "cool" thing in recent years.

They also seem to have hired a new generation of IDE developers. There are a lot of things you could do with a few keystrokes in VS6 that you now have to use the mouse for. What used to be half a second of work may take half a minute now. Not to mention what it does to your physiology.

Sid

Life... is a state of mind

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

ChaunceyGardiner wrote:
SprinterSB wrote:
If you use a compiler in a professional environment and it's crucial to your application that 2 bytes are saved,
I have already demonstrated in this topic that the difference is 12 bytes for a simple test case.
You didn't even post a valid test case.
  • Your "code" in the initial post from Sep 18, 2012 - 07:45 AM is not a valid test case — neither is it a valid C module nor a valid C++ module.
  • Same is true for the text you posted Sep 18, 2012 - 11:14 AM.
The noise you are complaining about is caused because others tried to help you and tried to reproduce the claimed issue, and in lack of a valid test case that does not stop the compilation with a syntax error, started guessing around.

The first valid C code (still not valid for a GCC problem report) was posted by Dean at Sep 18, 2012 - 10:58 AM, and he reports a code size difference of 2 bytes.

ChaunceyGardiner wrote:
I have also stated that the difference is even bigger in other cases.
You can state anything. Without a valid piece of C code, your statements are just noise that will trigger even more noise. That's exactly what happened above.

ChaunceyGardiner wrote:
A compiler that potentially wastes space every time you test an expression is something [...] that needs to be addressed by developers.
It is simportant that you are specific. Compiler take valid C or C++ or whatever code, but they don't compile prose. Dean already explained it to you:
abcminiuser wrote:
You'd done the equivalent of "I press a button and my computer stops working" without telling anyone what button, what computer, what stops working or how you're pressing it.
But you
ChaunceyGardiner, Sep 18, 2012 - 11:14 AM wrote:
I don't want to waste any more time on trying to recreate it.
You waste the times of all the guys that start digging around and start guessing and come with whatever code that does *not* back up your claim.
ChaunceyGardiner wrote:
SprinterSB wrote:
then hire a professional contractor that offers professional compiler support.
I offer "compiler support" in that I work to find out how the compiler performs and choose the better ways to implement solutions.
Ok, thanks for the insight.

My misunderstanding was that I thought your intention was to report a problem so that it can be fixed in the compiler, rather than working around it by changing the applicaton source code of fiddling with command options.

ChaunceyGardiner, Sep 18, 2012 - 12:07 PM wrote:
I don't believe for one second that the compiler is going to be changed, no matter how well I describe it.

And even you were able to find a size difference in no time with the information you deem useless.

Ok, understood. You have no interests in, for example, filing a proper bug report, which is in turn the basis so that developers can reproduce your issue.

Why should developers start guessing around? Guessing is not a good design pattern!

Let me tell you:

    You will get wrong code from avr-gcc if you use the construct
    if (a >> 7)
    {
       // some code
    }

Are you able to approve the bug? Guess! Start guessing! And tell me how much time you wasted without finding the bug (there is one)!

With almost 100% certainty you will not start guessing and waste days or weeks. So why do you expect others to waste their time by guessing for missing bits of information that you wno't provide or snipped out or refuse to supply for whatever reason?

I still did not get what wour intention is. As you wrote it's obviouly not aimed at improving the tools, for example by filing a proper bug report at the appropriate bug tracker.

As far as I understand, this site if for questions of compiler users. Neither is it a bug tracker, nor is it a GCC development page, not is it likely that GCC developers are hanging around here.

avrfreaks does not support Opera. Profile inactive.

Last Edited: Wed. Sep 19, 2012 - 06:31 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Blah blah blah

Someone tell a joke or something. That would make it more interesting hanging out here.

Sid

Life... is a state of mind

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

*PLONK*

avrfreaks does not support Opera. Profile inactive.

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

Quote:

My findings were that the compiler generates differently sized code for if statements and equivalent ternary operators.

The code size should be the same, but it isn't.


Can you find a compiler where this is actually true? So far we have results from avr-gcc and MS VC++ 2008. I don't believe you are going to find any compiler where those four sequences produce identical code. In fact you even state above:
Quote:

I work to find out how the compiler performs and choose the better ways to implement solutions.

If compilers WERE perfect I guess you'd be unemployed ;-)

BTW here's the four variants from Pelles C:

xor eax,eax
cmp dword [(_toggle)],0
sete al
push dword eax
call _f
pop ecx
cmp dword [(_toggle)],0
jne @8
push dword 1
call _f
pop ecx
jmp @10
@8:
push dword 0
call _f
pop ecx
@10:
xor eax,eax
cmp byte [(_b)],0
setne al
push dword eax
call _f
pop ecx
@11:
cmp byte [(_b)],0
je @8
push dword 1
call _f
pop ecx
jmp @10
@8:
push dword 0
call _f
pop ecx
@10:

Last Edited: Wed. Sep 19, 2012 - 09:30 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
Quote:

My findings were that the compiler generates differently sized code for if statements and equivalent ternary operators.

The code size should be the same, but it isn't.


Can you find a compiler where this is actually true? So far we have results from avr-gcc and MS VC++ 2008. I don't believe you are going to find any compiler where those four sequences produce identical code.

My findings are relevant to developing software in C/C++ on an 8-bit AVR, using the compiler most of us use. MSVC and most other compilers are not relevant in this context.

Also, the four sequences are not necessarily the same. 1) should be the same as 2) and 3) should be the same as 4) though.

clawson wrote:
Quote:

I work to find out how the compiler performs and choose the better ways to implement solutions.

If compilers WERE perfect I guess you'd be unemployed ;-)

I didn't say that that's the only thing I do.

Sid

Life... is a state of mind

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

ChaunceyGardiner wrote:

clawson wrote:
Quote:

That sort of thing calls for using assembly ?

Sadly the answer is Yes if you want complete control over code generation. Otherwise you are putting yourself in the hands of the compiler and optimiser authors.

Yes, I understand that. But this thread illustrates that you can actually improve code generation by taking deliberate steps with your C/C++ source code,

You can, but this thread is the prime illustration of the opposite.

You can improve code generation only if you have a deep enough understanding of *why* the compiler generated suboptimal code.

ChaunceyGardiner wrote:
instead of paying the high price associated with creating and maintaining programs in assembly.
This is pure BS.

ChaunceyGardiner wrote:
I find it frustrating that it is possible to improve the generated code this way, but it sure beats resorting to assembly.
Of course it does not; and of course, to understand why is it so, it requires a deep enough knowledge and skill, too, this time a knowledge and skill in writing asm.

But there is no point in attempting to "improve code" without trying to improve your skills first.

JW

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

SprinterSB wrote:

Let me tell you:
    You will get wrong code from avr-gcc if you use the construct
    if (a >> 7)
    {
       // some code
    }

Huh! Can you please tell us more?

JW

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

It really is quite simple.

I have well over two decades of full-time experience with developing software on many different platforms. Among these platforms are several with very limited resources, as well as some that are so rich most of you probably wouldn't believe it.

I have successfully used a number of different programming languages, including assembly on a few platforms and C/C++ on many.

I have a strong preference for C++. That does not mean that I lack the intelligence or skills needed to use C or assembly, or that I am too lazy to try to learn something. It's the other way around - I knew all about C and assembly on my platforms at the time, but I still took the time and effort to become fluent in C++. As a result, my experience tells me what tools to use and how to use them to maximize my productivity.

Some people are hopefully productive using C - that's what they use - and a handful claim to be productive using assembly. So be it, that's their choice. On a platform as simple as an AVR it may even be true that they are productive, and their code is hopefully easy to understand for others that know the language (and the hardware).

When you find ways to improve the code you generate, using the language you use, that doesn't imply that there is something wrong with the language. The tools you use may be sub-optimal, though. To get around that, you can figure out how your tools perform in different ways and choose to use it the ways that work the best. Or you can do what most developers do - curse, close your eyes and pretend you didn't see it.

Or you can throw it out the window and pretend to know it all so no tools could make you more productive - so you use an assembler.

Sid

Life... is a state of mind

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

Quote:

Huh! Can you please tell us more?

JW

My guess - and it's just a wild guess - is that this will yield incorrect results when used on signed integers. If a is negative the if statement will evaluate as true.

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

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

Quote:

Among these platforms are several with very limited resources,

On those other platforms with limited resources are you saying that those four variants you showed above would have all produced identical C code? Which platforms/compilers were these. Personally I think your expectations exceed reality. No compiler is intelligent enough to have found the absolute optimal sequence (variant 3 I guess) for all of them. It's just not how compilers currently work.

However I still think your thread is valid simply to say "beware and check code generation if you are concerned about size/speed". That'll always be generally true and is of more concern to resource limited embedded programmers than in any other environment.

Pages