----- Original Message ----- From: "Roberto Padovani" <padovan...@gmail.com> To: "GCC for MSP430 - http://mspgcc.sf.net" <mspgcc-users@lists.sourceforge.net> Sent: Monday, July 03, 2006 11:57 AM Subject: Re: [Mspgcc-users] Code generation question
> Hi David, > > criticism is fine....I'll explain my view. > > 2006/7/3, David Brown <da...@westcontrol.com>: > > I don't mean to sound patronising, but I don't think you are aware of what > > compilers do, how they work, how "volatile" works, or how to write good C > > code for embedded systems. I'm afraid your post is almost completely > > incorrect - I hope you take my reply here as constructive critisism to aid > > your better understanding. Thinking about these things is good, and > > discussing them will refine that thinking. > > > > > > > knowing how a compiler works in the most unusual situations is always > > > interesting...and this discussion is no different. > > > > That's not only true, but essential to embedded programming. Knowing your > > compiler is vital, including knowing its shortcomings. > > up to now, we agreee.... > > > > > > however, a compiler is just a machine with a lot of rules, so you > > > cannot expect it to be "clever" anytime, even if correct code is a > > > necessity and efficiency is only a plus; > > > > You can certainly expect the compiler to be "clever" - the people who wrote > > it were clever people, and I have often looked at generated assembly code > > and admired neat tricks I hadn't thought of myself. The aim of the > > compiler's optimiser is to give you the best generated assembly that has the > > same effect as the code you wrote, regardless of the style in which you > > wrote it. > [omissis...] > > in that sense, compilers are nowadays definitely clever, especially gcc. > What I meant is that you can always find a way to complicate the input > to any compiler, so that you'll need still another rule to have it > take the right decision....do you get the difference? English is not > my mother language, I hope I'm writing what I'm thinking. > Your English is fine - your spelling is probably better than mine. Your understanding of how compilers work is not quite right, however. A compiler does not pattern match on the input, and use that to determine the output (unless it is a very simplistic compiler). The input is analysed into an abstract tree of some sort, which may then be modified by front-end optimisations (this part is source-language specific). Then there are a number of passes and re-arrangements in the middle (independant of both the source language and the target). Then comes the back-end, with the code generation and target-specific optimisations. While there is some communication and co-operation between the parts, in general the abstract internal tree seen by the backend is not changed by making small semantically equivilent changes to the source code. The back-end code generator does work, to some extent, by pattern matching and generating appropriate code. But it matches on the internal structures, not the source code. So changes like adding extra temporary variables have no effect on the data seen by the backend (assuming you have at least basic optimisations enabled - as you normally would). In this particular case, it seems that these back-end match rules are not quite right, and an optimisation rule aimed at generating tight code for volatile accesses is accidently triggered. There could, of course, be mistakes in the front-end analysis parts of the compiler that get triggered by particularly complicated source code. However, that's not the case here. Grant's code is not complicated in any way, and other gcc ports (with the same front end, but different back ends) have no problems. > > > > but in this case, reading the C source code, I honestly can't > > > understand for sure what it meant....if I were to translate manually > > > that code into asm, I would have asked a clarification to the > > > programmer: did you want to sample twice the port and then add these > > > two samples ? (say for making an average) or did you want two sample > > > the port once and double that reading ? > > > IMHO the answer wasn't clear, even if I considered all the volatile > > qualifiers; > > > > > In that case, you simply don't have the experiance to understand volatiles > > properly. The code was perfectly clear - the port should be sampled twice, > > and it's sum taken. If Grant had wanted to sample it once and double it, he > > would have writen "two = 2 * P6IN;". Of course, in real code the function > > and variable names would have given an indication of *why* he was doing > > this, but the code itself is perfectly clear. > > > > I do know what volatile means and my first thought was that he wanted > to sample twice; nevertheless, my experience is that way of writing > code saves 1 line in the source file, but may potentially cause > headaches in debugging...it's not a good practice to access two > devices in one line...there's no drawback in splitting it a little > more and sometimes compilers have more chances to apply the > optimization rules > There are certainly reasons why you might want to seperate the statement into two lines - for example, if the code had been "two = P6IN + P5IN" and you wanted to be sure which port was read first, or if you want to single-step with a debugger but don't fancy assembly-level stepping. There are also compilers that can benefit from re-writing your code in different equivilent ways, because they are not as smart as gcc at optimising. Typical examples are writing pointer code manually instead of using arrays, or changing incrementing for loops into decrementing while loops. A big advantage of using gcc is that it does a lot of that sort of thing for you, letting you write your code in the most logical way. > Oh, except when you're taking part in the IOCCC :-) > > > > so I wouldn't try to add another complicated rule to gcc, but I would > > > rather write a better C code...an unambiguous one that the "simple" > > > rules of any compiler can understand. > > > > > > > A C compiler is required to generate correct code for all legal input - it > > would be a poor compiler indeed if it required specific simplistic styles to > > generate correct code. What has been uncovered here are two issues - one is > > inefficient code (which will be fixed if it can easily be done), and the > > other is incorrect code (which people are already working on to find a fix). > > It may be that in the meantime, people will need to use a workaround to > > avoid the bug - but that's an exceptional circumstance, not a solution. > > I agree > > > > for sampling twice: x = P6IN; x += P6IN; > > > > Why do you thing that is better than "x = P6IN + P6IN" ? To the compiler, > > this is exactly the same (assuming "x" is a local variable), and it > > generates exactly the same output. To the writer (and reader) of the C > > code, it is more verbose without adding any useful information - and > > therefore worse code. > > > > in the first post, x was not a local variable > If x is a global variable, then splitting the access will normally produce larger slower code. It is possible that this would be a helpful workaround until the actual compiler bug is fixed, but it does not improve the code in any other way. Additionally, the compiler is free to do the two reads and the add in registers, and only write out the data to x at the end of the function. In fact, with the improvements in optimisation in gcc 4.1, it could quite well eliminate x altogether and generate the same code as if x were local. > > > > By the way, I think it's also better to change your initial example: > > > > > > unsigned two; > > > void foo(void) > > > { > > > two = P6IN + P6IN; > > > } > > > > > > into: > > > > > > unsigned two; > > > > > > void foo(void) > > > unsigned int tmp; > > > { I'm assuming the above two lines are swapped, so that tmp is a local variable rather than a syntax error. > > > tmp = P6IN + P6IN; // <----- tmp = 2 * P6IN; // <----- tmp = > > > P6IN; tmp+= P6IN; > > > two = tmp; > > > } > > > > > > > You may think that, but no one will agree with you. There is no benifit in > > introducing an extra temporary variable here - it simply makes the code more > > verbose and thus less readable. You are writing in C, using a compiler - > > you are not writing everything out explicitly in assembler. It is up to the > > compiler to figure out what extra temporary variables it needs - in fact, > > the compiler will probably internally use exactly the code you have written > > out explicitly. And unless you are specifically compiling with no > > optimisation, the code generated is identical with or without the "tmp" > > variable. > > > > Apart from the fact that the compiler generates two completely > different codes if you use the temp variable or not (try it!), I have tried it, and the code is the same. Unless, of course, you use no optimisation flags, which would be a very strange way to compile real code for a real application (even during testing and debugging). > I don't know what others would say...but I don't think that is > "verbose"; on the contrary, it's more readable because you may have > that memory variable defined far away (in terms of code lines) and you > may forget the type....and....and......it's error prone. There is no excuse for not knowing the type or purpose of the variables you use - you simply should not be writing code like that. There are certainly occasions when extra temporary variables is a good thing. For example, it can make type changes much clearer (such as if a 16-bit local was used as temporary storage before the sum was divided by two and written out to an eight bit variable). Adding them for no reason to a one line function does not make it less error prone. > And it's no more efficient then using explicitely the temp var. Omitting the temp variable is either more efficient (if you have forgot the optimisation flag), as you found out yourself, or it generates the same code. > > > > > or even better a function. it's more efficient (and easier to compile) > > > if you make the calculations through local variables. > > > > > > > It is hard to split up a single-line function into smaller functions. > > first of all, if in that function there was really just that > instruction (and I don't believe so), you would be wasting stack, > making calls, and so on.....which is the opposite of you should do > with embedded systems. In PC your CPU has the pipelines doing nops for > the most part of the time, so you wouldn't affect the performances > significantly, but with an MSP430 you should keep an eye on not > wasting anything. On a PC, avoiding such extra functions can be at least as important - the change of flow plays havoc with your pipelines. The reason for using an optimising compiler, is that it optimises the generated code for you so that you can choose yourself when to divide the code up into smaller or bigger functions. The compiler will automatically inline small functions - I use "static inline" functions all the time rather than old-fashioned macros. The reason for not dividing a small function into even smaller functions is nothing to do with the generated code overhead - it has everything to do with the programmer being able to see what the code is doing without having to jump around in the source files. > Note also that every extra instruction means extra current that you're > drawing from the battery.... > Indeed - that's why I let the compiler generate the best code it can, and check that it is doing so (and occasionally change my source code if necessary). > if you really want that single instruction as a function by itself and > you want it to write directly in the global variable, then you should > at least declare it naked. > Perhaps this is a case where we have misunderstood each other - as far as I can tell, you said that Grant's code should be split with an extra function, and I disagreed. Did I misunderstand you? > > > And as for being easier to compile - I try to be nice to my computer, but I > > refuse to structure my code differently just to save the computer a couple > > of microsecond's effort during compilation. > > I didn't care about the compilation time....if you got there, it means > my English is really bad...sorry everyone! I was being a little sarcastic - I should have added a smiley, as I did understand what you meant. > "easier" meant that you help the compiler to understand when to use a > register instead of a direct memory access > > > Local variables are, in general, more efficient than global variables - but > > they are not more efficient than calculations carried out internally by the > > compiler using registers. Add variables when they make sense in your code, > > and make the logic of your code clearer - don't do it for the compiler's > > sake. > > when you are writing firmware, using explicitely a local variable > means that you _prefer_ it to be a register !!! And if I happen to see > that I used too many local variables (which is quite difficult with 16 > registers), so that the compiler is forced to allocate some of them in > memory, then I try to rearrange my code. The less specific you are in your code, the easier it is for the compiler to re-arrange things for the greatest efficiency. If you make a local "tmp" variable at the start of a function, and use it in several places in the function, and you have debugging enabled, then the compiler is going to give it a specific register so that the debugger can track it (newer versions of gcc, gdb, and dwarf are more flexible, but I don't thing the msp430-gcc toolchain is there yet). That means you have used up a specific register. If you make new "tmp" variables inside smaller block scopes, the compiler can use a different register each time. If you can omit the "tmp" variable altogether, the compiler has even greater freedom. In other words, let the compiler do its job, and don't try and do it yourself. > Efficiency comes also at the cost of coding time, if efficiency is your aim. > Sometimes that's true - but sometimes you can try too hard, ending up being inefficient in your code, and inefficient in your coding time. > Are you sure you know everything about embedded systems ? If I knew everything, then I'd start looking for a new career - learning is part of what makes this stuff fun! But I have worked with a wide range of systems (both compilers and targets), and I've learned a lot about the myths and truths of efficient embedded programming over the years. mvh., David > > R. > > [omissis] >