I use SES import my keil MDK project, and my keil project work well. With SES, i have a problem, that is, i have a global variable like this:
uint32_t g_ulMainLoopCounter = 0;
and i decrease it in timmer isr:
if (g_ulMainLoopCounter > 0)
{
g_ulMainLoopCounter--;
}
in my main function, i wait until 'g_ulMainLoopCounter' decrease to 0:
int main(void)
{
system_init(); //init timer etc...
g_ulMainLoopCounter = 500;
while (g_ulMainLoopCounter)
{
}
....
}
now the problem is , my code after 'while' will never be executioned, even 'g_ulMainLoopCounter' decrease to 0. This work well in keil.
Then, if i use 'volatile' qualifier for 'g_ulMainLoopCounter', it works, the code then like this:
volatile uint32_t g_ulMainLoopCounter = 0;
My optimization level set to none, means do not optimize my code.
I know use 'volatile' qualifier is a better way, but 'g_ulMainLoopCounter' is just only a example, there are lots of variables which used like "g_ulMainLoopCounter" (means multi-access variable, change the value in a function or isr and comparison in other function)in my program, must i check every variable in my program and determine if the variable is needed use 'volatile' qualifier? If so, i think that's too difficult.Is there any easy solutions?
You seem to know the meaning of volatile.
Your question is mainly whether you should consciously consider it for all your variables.
The answer is: Yes.
If that seems too difficult, or accidentally, just leave the volatile away and only start thinking about it when something does not work as desired.
You might however finally find out that this solution is actually very hard.
Maybe your question was caused by this experience.
The good news is that it will become easier, with experience.
At some point, you will read code, or write it, and feel a shiver going down the back. With more experience you will be able to tell the difference between the "volatile is missing"-shiver and e.g. the "variable is not initialised"-shiver.
Related
Say you have (for reasons that are not important here) the following code:
int k = 0;
... /* no change to k can happen here */
if (k) {
do_something();
}
Using the -O2 flag, GCC will not generate any code for it, recognizing that the if test is always false.
I'm wondering if this is a pretty common behaviour across compilers or it is something I should not rely on.
Does anybody knows?
Dead code elimination in this case is trivial to do for any modern optimizing compiler. I would definitely rely on it, given that optimizations are turned on and you are absolutely sure that the compiler can prove that the value is zero at the moment of check.
However, you should be aware that sometimes your code has more potential side effects than you think.
The first source of problems is calling non-inlined functions. Whenever you call a function which is not inlined (i.e. because its definition is located in another translation unit), compiler assumes that all global variables and the whole contents of the heap may change inside this call. Local variables are the lucky exception, because compiler knows that it is illegal to modify them indirectly... unless you save the address of a local variable somewhere. For instance, in this case dead code won't be eliminated:
int function_with_unpredictable_side_effects(const int &x);
void doit() {
int k = 0;
function_with_unpredictable_side_effects(k);
if (k)
printf("Never reached\n");
}
So compiler has to do some work and may fail even for local variables. By the way, I believe the problem which is solved in this case is called escape analysis.
The second source of problems is pointer aliasing: compiler has to take into account that all sort of pointers and references in your code may be equal, so changing something via one pointer may change the contents at the other one. Here is one example:
struct MyArray {
int num;
int arr[100];
};
void doit(int idx) {
MyArray x;
x.num = 0;
x.arr[idx] = 7;
if (x.num)
printf("Never reached\n");
}
Visual C++ compiler does not eliminate the dead code, because it thinks that you may access x.num as x.arr[-1]. It may sound like an awful thing to do to you, but this compiler has been used in gamedev area for years, and such hacks are not uncommon there, so the compiler stays on the safe side. On the other hand, GCC removes the dead code. Maybe it is related to its exploitation of strict pointer aliasing rule.
P.S. The const keywork is never used by optimizer, it is only present in C/C++ language for programmers' convenience.
There is no pretty common behaviour across compilers. But there is a way to explore how different compilers acts with specific part of code.
Compiler explorer will help you to answer on every question about code generation, but of course you must be familiar with assembler language.
Does gcc have an option to disable read/write optimizations for global variables not explicitly defined as volatile?
My team is running out of program memory in our embedded C project, built using gcc. When I enable optimizations to reduce code size, the code no longer works as expected because we have not been using the volatile keyword where we ought to have been. That is, I was able to resolve the presenting problem by declaring a few variables accessed in ISRs volatile. However, I don't have any level of certainty that those are the only variables I need to declare volatile and I just haven't noticed the other bugs yet.
I have heard that "some compilers" have a flag to implicitly declare everything volatile, but that I should resist the temptation because it is a "substitute for thought" (see https://barrgroup.com/Embedded-Systems/How-To/C-Volatile-Keyword).
Yes, but thought is expensive. Feel free to try to talk me out of it in the comments section, but I'm hoping for a quick fix to get the code size down without breaking the application.
You mean something besides -O0?
I expect that it's not too difficult to hack GCC for a quick experiment. This place in grokdeclarator in gcc/c/c-decl.c is probably a good place to unconditionally inject a volatile qualifier for the interesting storage_class values (probably csc_none, csc_extern, csc_static in your case).
/* It's a variable. */
/* An uninitialized decl with `extern' is a reference. */
int extern_ref = !initialized && storage_class == csc_extern;
type = c_build_qualified_type (type, type_quals, orig_qual_type,
orig_qual_indirect);
The experiment should tell you whether this is feasible from a performance/code size point of view and if it is, you might want to submit this as a proper upstream patch.
It's possible to do it by for example redefining all basic types as the same but with volatile specifier. Anyway if all variables in your code will be volatile I expect that size of your application will be larger than before optimizations.
My solution is: enable optimizations for part of the code. If your application got some functional architecture you can start enabling optimizations and test if this functionality works properly. It's much easier than optimize everything and analyze why nothing works.
I'm a little bit new to C so I'm not familiar with how I would approach a solution to this issue. As you read on, you will notice its not critical that I find a solution, but it sure would be nice for this application and future reference. :)
I have a parameter int hello and I wan't to make a synonomous copy of not it.
f(int hello, structType* otherParam){
// I would like to have a synonom for (!hello)
}
My first thought was to make a local constant, but I'm not sure if there will be additional memory consumption. I'm building with GCC and I really don't know if it would recognize a constant of a parameter (before any modifications) as just a synonymous variable. I don't think so because the parameter could (even though it wont be) changed later on in that function, which would not effect the constant.
I then thought about making a local typedef, but I'm not sure exactly the syntax for doing so. I attempted the following:
typedef (!hello) hi;
However I get the following error.
D:/src-dir/file.c: In function 'f':
D:/src-dir/file.c: 00: error: expected identifier or '(' before '!' token
Any help is appreciated.
In general, in C, you want to write the code that most clearly expresses your intentions, and allow the optimiser to figure out the most efficient way to implement that.
In your example of a frequently-reused calculation, storing the result in a const-qualified variable is the most appropriate way to do this - something like the following:
void f(int hello)
{
const int non_hello = !hello;
/* code that uses non_hello frequently */
}
or more likely:
void x(structType *otherParam)
{
char * const d_name = otherParam->b->c->d->name;
/* code that uses d_name frequently */}
}
Note that such a const variable does not necessarily have to be allocated any memory (unless you take its address with & somewhere) - the optimiser might simply place it in a register (and bear in mind that even if it does get allocated memory, it will likely be stack memory).
Typedef defines an alias for a type, it's not what you want. So..
Just use !hello where you need it
Why would you need a "synonym" for a !hello ? Any programmer would instantly recognize !hello instead of looking for your clever trick for defining a "synonym".
Given:
f(int hello, structType* otherParam){
// I would like to have a synonom for (!hello)
}
The obvious, direct answer to what you have here would be:
f(int hello, structType *otherParam) {
int hi = !hello;
// ...
}
I would not expect to see any major (or probably even minor) effect on execution speed from this. Realistically, there probably isn't a lot of room for improvement in the execution speed.
There are certainly times something like this can make the code more readable. Also note, however, that when/if you modify the value of hello, the value of hi will not be modified to match (unless you add code to update it). It's rarely an issue, but something to remain aware of nonetheless.
I'm writing firmware for a PIC32MX, using HiTech PICC32. One of the problems I want to avoid is that since most of the pins have multiple names (eg. AN0 = RB0 = CN2 = PGED1), I or someone else might accidentally use RB0 without realising that AN0 is already used. (This can actually be catastrophic, since incorrectly configuring an analogue/digital pin can lead to excessive current draw and release of essential smoke.)
As well as comprehensively documenting every pin used, I was wondering if there was a quick way to head this issue off at the level of coding. I want a macro that people (mainly myself) can use, say CLAIM_PIN(58), that will issue a warning or error if it is run twice.
(I don't want this at all costs, if the only possible solution is too horrendous or unmaintainable then I'll forget about it and just develop a reputation for bursting into tears or setting myself on fire or something. I also saw this question about macro producing macros, which rules out that.)
I should clarify: the code IS written in multiple compilation units (at least, I think this is what the phrase means). I have a .h/.c file for my A2D code, similarly for SPI, and similarly for various peripherals that just use certain I/O ports. Space is not really a problem, my code leaves plenty of room on the PIC32MX; also I can use another __DEBUG flag to remove the pin checking code for final use.
#define CLAIM_PIN(n) char claimed_pin_##n;
Now when two pieces of code try to claim a pin, the symbol will be doubly defined and either the compiler or the linker will generate an error.
Edit: Based on comments, this might turn out better:
#define CLAIM_PIN(n) void claimed_pin_#nn(void) {}
Ok, here. No runtime cost.
#define CLAIM(n) struct busy##n {}
CLAIM(58);
CLAIM(58);
If run twice it will error out:
z.c:4: error: redefinition of ‘struct busy58’
To extend the check to multiple compilation units you will want to wrap the macro in #if DEBUG because we would be using the linker to detect the clash and hence would have a runtime footprint.
#define CLAIM(n) char busy##n = 1;
#define CLAIM(n) void busy##n() {} // bdonlan
If you can afford the runtime overhead or if this is just for debugging, I'd just create something like an IOPinOpen() function that kept track of pins in use instead of dealing with macro trickery.
On the other hand, Mark Ransom's updated answer was worth a +1.
Consider the following:
volatile uint32_t i;
How do I know if gcc did or did not treat i as volatile? It would be declared as such because no nearby code is going to modify it, and modification of it is likely due to some interrupt.
I am not the world's worst assembly programmer, but I play one on TV. Can someone help me to understand how it would differ?
If you take the following stupid code:
#include <stdio.h>
#include <inttypes.h>
volatile uint32_t i;
int main(void)
{
if (i == 64738)
return 0;
else
return 1;
}
Compile it to object format and disassemble it via objdump, then do the same after removing 'volatile', there is no difference (according to diff). Is the volatile declaration just too close to where its checked or modified or should I just always use some atomic type when declaring something volatile? Do some optimization flags influence this?
Note, my stupid sample does not fully match my question, I realize this. I'm only trying to find out if gcc did or did not treat the variable as volatile, so I'm studying small dumps to try to find the difference.
Many compilers in some situations don't treat volatile the way they should. See this paper if you deal much with volatiles to avoid nasty surprises: Volatiles are Miscompiled, and What to Do about It. It also contains the pretty good description of the volatile backed with the quotations from the standard.
To be 100% sure, and for such a simple example check out the assembly output.
Try setting the variable outside a loop and reading it inside the loop. In a non-volatile case, the compiler might (or might not) shove it into a register or make it a compile time constant or something before the loop, since it "knows" it's not going to change, whereas if it's volatile it will read it from the variable space every time through the loop.
Basically, when you declare something as volatile, you're telling the compiler not to make certain optimizations. If it decided not to make those optimizations, you don't know that it didn't do them because it was declared volatile, or just that it decided it needed those registers for something else, or it didn't notice that it could turn it into a compile time constant.
As far as I know, volatile helps the optimizer. For example, if your code looked like this:
int foo() {
int x = 0;
while (x);
return 42;
}
The "while" loop would be optimized out of the binary.
But if you define 'x' as being volatile (ie, volatile int x;), then the compiler will leave the loop alone.
Your little sample is inadequate to show anything. The difference between a volatile variable and one that isn't is that each load or store in the code has to generate precisely one load or store in the executable for a volatile variable, whereas the compiler is free to optimize away loads or stores of non-volatile variables. If you're getting one load of i in your sample, that's what I'd expect for volatile and non-volatile.
To show a difference, you're going to have to have redundant loads and/or stores. Try something like
int i = 5;
int j = i + 2;
i = 5;
i = 5;
printf("%d %d\n", i, j);
changing i between non-volatile and volatile. You may have to enable some level of optimization to see the difference.
The code there has three stores and two loads of i, which can be optimized away to one store and probably one load if i is not volatile. If i is declared volatile, all stores and loads should show up in the object code in order, no matter what the optimization. If they don't, you've got a compiler bug.
It should always treat it as volatile.
The reason the code is the same is that volatile just instructs the compiler to load the variable from memory each time it accesses it. Even with optimization on, the compiler still needs to load i from memory once in the code you've written, because it can't infer the value of i at compile time. If you access it repeatedly, you'll see a difference.
Any modern compiler has multiple stages. One of the fairly easy yet interesting questions is whether the declaration of the variable itself was parsed correctly. This is easy because the C++ name mangling should differ depending on the volatile-ness. Hence, if you compile twice, once with volatile defined away, the symbol tables should differ slightly.
Read the standard before you misquote or downvote. Here's a quote from n2798:
7.1.6.1 The cv-qualifiers
7 Note: volatile is a hint to the implementation to avoid aggressive optimization involving the object because the value of the object might be changed by means undetectable by an implementation. See 1.9 for detailed semantics. In general, the semantics of volatile are intended to be the same in C++ as they are in C.
The keyword volatile acts as a hint. Much like the register keyword. However, volatile asks the compiler to keep all its optimizations at bay. This way, it won't keep a copy of the variable in a register or a cache (to optimize speed of access) but rather fetch it from the memory everytime you request for it.
Since there is so much of confusion: some more. The C99 standard does in fact say that a volatile qualified object must be looked up every time it is read and so on as others have noted. But, there is also another section that says that what constitutes a volatile access is implementation defined. So, a compiler, which knows the hardware inside out, will know, for example, when you have an automatic volatile qualified variable and whose address is never taken, that it will not be put in a sensitive region of memory and will almost certainly ignore the hint and optimize it away.
This keyword finds usage in setjmp and longjmp type of error handling. The only thing you have to bear in mind is that: You supply the volatile keyword when you think the variable may change. That is, you could take an ordinary object and manage with a few casts.
Another thing to keep in mind is the definition of what constitutes a volatile access is left by standard to the implementation.
If you really wanted different assembly compile with optimization