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
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
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.
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:
Also what happens if you compile the same code for i386 using gcc?
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 ?
I also question the relevance of "how different the compile sizes are". Why should there be any difference ?
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.
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:
#includevoid f(uint8_t a) { PORTA = a; } int main(void) { bool b = PORTC; if (b) { f(42); } else { f(24); } }
#includevoid 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:
Adding code to existing program.
// Add to global scope: #includevolatile 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.
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.
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.
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.
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 ; 0xe2de: 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 ; 0x94dc: 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.
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!
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.
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.
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:
#includevoid f(int i) { itoa(i, xxx, 10); // sprintf(xxx, "% d", i); }
David.
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 ?
The reason being that you are using a variadic function (I think)
isn't it a shame the compiler uses uint16_t for "bool".
Yes, that is a darn shame.
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?
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:
using uint8_t and zero/nonzero instead.
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.
And even you were able to find a size difference in no time with the information you deem useless.
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.
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.
The reason being that you are using a variadic function (I think).
If you were to use a regular function, things look better
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.
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.
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:
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.
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).
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.
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.
If you see a problem, please post a compilable program that shows the problem.
That is a clear message.
You've failed on both points so far.
I will keep this sort of findings to myself in the future.
People had to pull a few lines out of you, and make it compilable them selves.
I take it you're another one of those who like to pretend you don't know what uint8_t is ?
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).
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:
That sort of thing calls for using assembly ?
Thank you for clearing that up, Dean. I feel much better about it now.
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.
I find it frustrating that it is possible to improve the generated code this way, but it sure beats resorting to assembly.
I find it frustrating that it is possible to improve the generated code this way
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?
I saw bigger differences in my real code, but this illustrates the point.
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.
Using a software in a professional setup without caring for professional support if professional support is essential, that is not professional.
Quote:No it doesn't. Your original point was that ?: created bigger code, but in both your examples it produced smaller code.I saw bigger differences in my real code, but this illustrates the point.
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.
ChaunceyGardiner wrote:If you use a compiler in a professional environment and it's crucial to your application that 2 bytes are saved,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.
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.
then hire a professional contractor that offers professional compiler support.
The thing I like about freaks is that it is a nice friendly forum :D
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.
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.
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?
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.
SprinterSB wrote:I have already demonstrated in this topic that the difference is 12 bytes for a simple test case.If you use a compiler in a professional environment and it's crucial to your application that 2 bytes are saved,
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.
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 needs to be addressed by developers.
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 don't want to waste any more time on trying to recreate it.
SprinterSB wrote:I offer "compiler support" in that I work to find out how the compiler performs and choose the better ways to implement solutions.then hire a professional contractor that offers professional compiler support.
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.
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.
Why should developers start guessing around? Guessing is not a good design pattern!
Let me tell you:
if (a >> 7) { // some code }
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.
Blah blah blah
Someone tell a joke or something. That would make it more interesting hanging out here.
*PLONK*
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.
I work to find out how the compiler performs and choose the better ways to implement solutions.
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:
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.
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.
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 ;-)
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 improve code generation only if you have a deep enough understanding of *why* the compiler generated suboptimal 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.
But there is no point in attempting to "improve code" without trying to improve your skills first.
JW
Let me tell you:You will get wrong code from avr-gcc if you use the construct
if (a >> 7) { // some code }
JW
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.
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:
Among these platforms are several with very limited resources,
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.
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.
And thats the point the people here are trying to make: most of the time the speed is not really of concern, then write it in C in a way that is most readable for you, not for the compiler. In the very few other cases use a small piece of assembler.
No compiler is intelligent enough
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.
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.
ChaunceyGardiner wrote:That sounds like an "either or".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).
Often speed is really of concern in only small parts of a program (e.g. a ISR). Writing that small part in assembly is not a maintaining nightmare.
ChaunceyGardiner wrote:The downside is that you have to reinvestigate these improvements with every new compiler version. With a small piece of assembler code you ensure to get the best result "now and forever".When you find ways to improve the code you generate, using the language you use.
Cliff is the only person who has posted the results of Mr Gardiner's test code. 20, 26, 12, 26 bytes for each method.
Personally, I would not be unhappy with any of those translations. Method (3) took particular advantage of a special enum { set }.
I would prefer a better test suite. And yes, it is incredibly important to specify any test conditions.
I am not surprised that the translations were not identical. Perhaps one day, compiler writers may achieve perfection.
I still maintain that: "use straightforward code, use appropriate variable types" is as good advice as any.
Note that uint8_t would have made a significant difference. However this may not be your natural style. (feeling comfortable is important for your well-being)
David.
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:
The result of E1 >> E2 is E1 right-shifted E2 bit positions.[...] If E1 has a signed type and a negative value, the resulting value is implementation-defined.
Cliff is the only person who has posted the results of Mr Gardiner's test code.
2) costs 2 bytes more than 1)
4) costs 12 bytes more than 3)
20, 26, 12, 26 bytes for each method.
abcminiuser wrote:There is no incorrect result in that case: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:
C99, 6.5.7#5 wrote:JWThe result of E1 >> E2 is E1 right-shifted E2 bit positions.[...] If E1 has a signed type and a negative value, the resulting value is implementation-defined.
Indeed, I meant bug as in "bug in the user code due to incorrect assumptions", rather than "bug in the compiler itself". If SprinterSB was talking about compiler bugs then all bets are off, I was just thinking of the puzzle from the "what would the user do wrong" perspective.
- Dean :twisted:
I still maintain that: "use straightforward code, use appropriate variable types" is as good advice as any.Note that uint8_t would have made a significant difference. However this may not be your natural style. (feeling comfortable is important for your well-being)
Sid is apparently not comfortable with the way the compiler performs. Nor is he comfortable with writing asm either. Nor he is not comfortable with making it easy for the gcc developers to understand his complains (which would increase the probability that the compiler could be improved one day).
On the other hand, he appears to be comfortable with inspection of the generated code and tweaking the source to get a desired result, and he appears to be comfortable with all the gotchas this method involves.
Programming is all about tradeoffs. I personally wouldn't recommend Sid's preferences, though.
JW
Those differences are bigger than the ones I find.
Yes. But at least Cliff posted the results.
You just claim things without showing any evidence.
Yes. I may prefer one style which I have found works pretty well across many platforms.
You have to make your own choice of style.
Incidentally, I have altered 'styles' to squeeze into a bootloader section. In all honesty, I have never bothered with an application section. How often are you 20 bytes too big?
David.
he appears to be comfortable with inspection of the generated code and tweaking the source to get a desired result, and he appears to be comfortable with all the gotchas this method involves.
Somehow it is better to accept a definite penalty now in case it turns out to be an advantage with a hypothetical future reversal of problems in the compiler ?
If SprinterSB was talking about compiler bugs [...]
I tried to play a bit with the >>7, and although did not find anything suspicious, the following in context of Cliff's test might be some food for thought:
#includevolatile uint8_t a; volatile uint8_t b; int main(void) { if (a >> 7) { b = 1; } else { b = 2; } b = (a >> 7) ? 3 : 4; }
int main(void) { if (a >> 7) { 112: 80 91 01 02 lds r24, 0x0201 116: 87 ff sbrs r24, 7 118: 02 c0 rjmp .+4 ; 0x11eb = 1; 11a: 81 e0 ldi r24, 0x01 ; 1 11c: 01 c0 rjmp .+2 ; 0x120 } else { b = 2; 11e: 82 e0 ldi r24, 0x02 ; 2 120: 80 93 00 02 sts 0x0200, r24 } b = (a >> 7) ? 3 : 4; 124: 80 91 01 02 lds r24, 0x0201 128: 80 95 com r24 12a: 88 1f adc r24, r24 12c: 88 27 eor r24, r24 12e: 88 1f adc r24, r24 130: 8d 5f subi r24, 0xFD ; 253 132: 80 93 00 02 sts 0x0200, r24 136: 08 95 ret 00000138 <_exit>: 138: ff cf rjmp .-2 ; 0x138 <_exit>
JW
Quote:Those differences are bigger than the ones I find.Yes. But at least Cliff posted the results.
You just claim things without showing any evidence.
The numbers you seem to like are clearly wrong, too.
Oops. I did my sums wrong on the byte counts:
22, 28, 12, 28 bytes.
Huh ? I posted the relevant numbers and I posted the code with instructions about where to put it if you wanted to verify those numbers.
David.
Is there any chance of you posting the generated code?
How often are you 20 bytes too big?
Please post a bug report at the GCC bug database here:
http://gcc.gnu.org/bugzilla/
This page at the GCC project goes into detail as to what is needed in a bug report:
http://gcc.gnu.org/bugs/
Please put my email address (see button at the bottom of my posts) in the CC field.
Note that if there is no compilable test case that shows the misoptimization then the bug cannot be fixed.
We want to make sure that misoptimization bugs get fixed.