Shop OBEX P1 Docs P2 Docs Learn Events
static vs. "normal" global variable — Parallax Forums

static vs. "normal" global variable

maccamacca Posts: 784
edited 2013-04-13 08:56 in Propeller 1
Hello All,

What is the difference (related to Propeller GCC not C) between a global variable declared as static and "normal" (non-static) ?

I'm working on a program that uses a library archive (ar) for common functions. The sources in the archive uses global variables, the problem is that if I declare one of these variables as static the program compiles but won't work anymore, or, better, doesn't work as expected. I tought that the static declaration worked like in standard C and the variables are visible in that source file only (which is my intention, these variables should not be visible by other modules), but I guess there is something else. The variables are used indirectly by others, like settings values, pointers to other data, etc. looks like something gets relocated in the wrong position.

The program is compiled with lmm model.
«1

Comments

  • David BetzDavid Betz Posts: 14,516
    edited 2013-04-11 06:38
    macca wrote: »
    Hello All,

    What is the difference (related to Propeller GCC not C) between a global variable declared as static and "normal" (non-static) ?

    I'm working on a program that uses a library archive (ar) for common functions. The sources in the archive uses global variables, the problem is that if I declare one of these variables as static the program compiles but won't work anymore, or, better, doesn't work as expected. I tought that the static declaration worked like in standard C and the variables are visible in that source file only (which is my intention, these variables should not be visible by other modules), but I guess there is something else. The variables are used indirectly by others, like settings values, pointers to other data, etc. looks like something gets relocated in the wrong position.

    The program is compiled with lmm model.
    Static variables should work the same in PropGCC as in any implementation of ANSI C. Can you give an example that shows the problem you're having?
  • maccamacca Posts: 784
    edited 2013-04-11 07:00
    Wow, answered in one minute, you must be reading while I type. :-)

    It is difficult to explain, I'll attach the source code, it is the C porting of my game engine project.

    If you open the graphics_renderer.c file you'll see two TILEMAP declarations:
    TILEMAP tile_map_1[34 * (VRES / 8) + 2];
    TILEMAP tile_map_2[34 * (VRES / 8) + 2];
    

    With that declaration it works. If you change these two variables to static the video doesn't display the correct image anymore. Looks like it picks the wrong pointers.

    If you want to compile, compile the engine_c folder first, then the demo_c folder.
  • David BetzDavid Betz Posts: 14,516
    edited 2013-04-11 07:16
    macca wrote: »
    Wow, answered in one minute, you must be reading while I type. :-)

    It is difficult to explain, I'll attach the source code, it is the C porting of my game engine project.

    If you open the graphics_renderer.c file you'll see two TILEMAP declarations:
    TILEMAP tile_map_1[34 * (VRES / 8) + 2];
    TILEMAP tile_map_2[34 * (VRES / 8) + 2];
    

    With that declaration it works. If you change these two variables to static the video doesn't display the correct image anymore. Looks like it picks the wrong pointers.

    If you want to compile, compile the engine_c folder first, then the demo_c folder.
    There isn't any reason why adding "static" should cause your program to behave differently except that it might result in the variables being placed differently in memory. Do you have any idea how much of hub memory you're using with this program? Is it possible that you're stack is running into your variables and hence corrupting data and causing the display to fail? If that's the case, adding "static" might change that behavior by shuffling around where things are in memory.
  • maccamacca Posts: 784
    edited 2013-04-11 07:27
    The loader tells it writes 14192 bytes, I'm not sure how much data is there. As far as I can tell, graphics data is about 8k, then there is the COGs code (less than 4k I think), the rest is C code. The stack is not used much, if you look at the main function there is just a loop that calls other functions, not more than 2 or 3 levels deep in total and with very few local variables.
  • David BetzDavid Betz Posts: 14,516
    edited 2013-04-11 07:47
    macca wrote: »
    The loader tells it writes 14192 bytes, I'm not sure how much data is there. As far as I can tell, graphics data is about 8k, then there is the COGs code (less than 4k I think), the rest is C code. The stack is not used much, if you look at the main function there is just a loop that calls other functions, not more than 2 or 3 levels deep in total and with very few local variables.
    I'll try building this later after I'm done with my "day job".
  • David BetzDavid Betz Posts: 14,516
    edited 2013-04-11 08:18
    David Betz wrote: »
    I'll try building this later after I'm done with my "day job".
    Looks like your entire program including uninitialized (.bss) variables is a little over 18k so unless you're using malloc or a lot of stack space it doesn't seem like you're running out of memory.
  • jazzedjazzed Posts: 11,803
    edited 2013-04-11 08:51
    Adding static to a global in a C file (global namespace) makes the symbol private.

    Why should accessing a static global from outside of it's file (or includes) produce a reliable result?
  • David BetzDavid Betz Posts: 14,516
    edited 2013-04-11 08:53
    jazzed wrote: »
    Adding static to a global in a C file (global namespace) makes the symbol private.

    Why should accessing a static global from outside of it's file (or includes) produce a reliable result?
    It wouldn't and shouldn't even be possible. I didn't look closely at his code but I assume the variable is only accessed within the module where it is defined except maybe by reference. I will look at this later tonight. I only had time to compile it to find out if it was overflowing hub memory.
  • jazzedjazzed Posts: 11,803
    edited 2013-04-11 09:10
    Looks like tile_map_1 and tile_map_2 are referenced by the global TILEMAP *tile_map pointer. The tile_map pointer is treated like an array in the main.c demo.
  • David BetzDavid Betz Posts: 14,516
    edited 2013-04-11 09:35
    jazzed wrote: »
    Looks like tile_map_1 and tile_map_2 are referenced by the global TILEMAP *tile_map pointer. The tile_map pointer is treated like an array in the main.c demo.
    That is a bit odd since it looks like tile_map is initially setup to point to tile_map_2.
  • David BetzDavid Betz Posts: 14,516
    edited 2013-04-11 09:37
    David Betz wrote: »
    That is a bit odd since it looks like tile_map is initially setup to point to tile_map_2.
    Never mind. I'm trying to do this too quickly. tile_map_2 is an array so addressing tile_map as an array should be fine.
  • maccamacca Posts: 784
    edited 2013-04-11 11:40
    David Betz wrote: »
    Looks like your entire program including uninitialized (.bss) variables is a little over 18k so unless you're using malloc or a lot of stack space it doesn't seem like you're running out of memory.

    No mallocs and, as I said, there are very few local variables so the stack should not be an issue.
    David Betz wrote: »
    It wouldn't and shouldn't even be possible. I didn't look closely at his code but I assume the variable is only accessed within the module where it is defined except maybe by reference. I will look at this later tonight. I only had time to compile it to find out if it was overflowing hub memory.

    Overflow ? How ? It certainly doesn't use all hub memory. Are we using the same compiler version ? Mine is gcc version 4.6.1 (propellergcc_v0_3_4_1942) compiled from source.
    jazzed wrote: »
    Looks like tile_map_1 and tile_map_2 are referenced by the global TILEMAP *tile_map pointer. The tile_map pointer is treated like an array in the main.c demo.

    The two tilemap arrays builds a double buffering scheme using for scrolling the background. The global tile_map variable points alternatively to tile_map_1 or tile_map_2, the switch is done by the flip function. The tile_map variable is used by external modules to have a reference to the off-screen map and be able to safely update it. The whole source is a work in progress and it is likely that it will be changed in the near future so there won't be any global pointer variable, but I would like to understand what's wrong. I don't remember to have had any problems with static variables like that with normal desktop programs, that's why I asked if there are differences in that specific gcc implementation.
  • David BetzDavid Betz Posts: 14,516
    edited 2013-04-11 11:49
    macca wrote: »
    Overflow ? How ? It certainly doesn't use all hub memory.
    You're right, you're not overflowing hub memory. That is what I checked earlier today. I don't have time to actually look through the source code until this evening.
    Are we using the same compiler version ? Mine is gcc version 4.6.1 (propellergcc_v0_3_4_1942) compiled from source.
    I'm using the P2 branch but the behavior of statics isn't any different than in the version you're using.
    The two tilemap arrays builds a double buffering scheme using for scrolling the background. The global tile_map variable points alternatively to tile_map_1 or tile_map_2, the switch is done by the flip function. The tile_map variable is used by external modules to have a reference to the off-screen map and be able to safely update it. The whole source is a work in progress and it is likely that it will be changed in the near future so there won't be any global pointer variable, but I would like to understand what's wrong. I don't remember to have had any problems with static variables like that with normal desktop programs, that's why I asked if there are differences in that specific gcc implementation.
    Thanks for the overview of how your code works. As I said, I'll look at it later today.
  • David BetzDavid Betz Posts: 14,516
    edited 2013-04-11 14:59
    I just looked over the code a bit and I don't see anything obviously wrong. I still maintain that whether those variables are declared static or not should not make a difference in anything other than maybe their position in memory. What platform does this program run on and what should it do if it is working?
  • Dave HeinDave Hein Posts: 6,347
    edited 2013-04-11 15:43
    One problem is the following statement.
        tile_map = (TILEMAP *) &tile_map_2;
    
    The symbol "tile_map_2" will give you the address of the array. I'm not sure what value you get with "&tile_map_2", but I don't think that's what you want. If you didn't cast it with (TILEMAP *) you would get a warning from the compiler that the types don't match. I think you just want to use:
        tile_map = tile_map_2;
    
    Another issue may be because TILEMAP is a 16-bit value, and you may not be getting 32-bit alignment on the arrays. I don't know if you need 32-bit alignment or not. However, I think your main problem is using the "&" operator when assigning the pointer value.
  • jazzedjazzed Posts: 11,803
    edited 2013-04-11 16:34
    If I make the TILEMAP arrays "static volatile" things work fine.

    This is a very a cool demo BTW.
  • Dave HeinDave Hein Posts: 6,347
    edited 2013-04-11 19:06
    Well I ran a few tests with gcc under Cygwin, and &tile_map_2 and tile_map_2 produce the same address value. However, the compiler gives me a warning with &tile_map_2 if I don't cast it, so it's not a "TILEMAP *" type. And the resulting type is not "TILEMAP **" either, so I don't know what it is. But it does produce the same address, so that's not the problem.

    As I said in my previous post, it might be a 32-bit alignment problem. It may be aligned when a global, but not when a static because of the order of the variables in memory.
  • mindrobotsmindrobots Posts: 6,506
    edited 2013-04-11 19:44
    I thought that the C guru collective (don't remember which individual) had told me that static variables that were not declared volatile could be optimized away. This was causing me problems on some of the multi-cog tutorials.

    Could these variables be getting optimized to something in the end?
  • Heater.Heater. Posts: 21,230
    edited 2013-04-11 23:34
    this may noy be you problem but it seems it could be similar to an issue I had with heater_fft.

    I had two arrays of constants (look up tables) that were expected to be contiguous in memory. That is accesses off the end of the first one would reach into the second one which would happen to have the correct data in it. Bad practice really.

    All was well in the Spin version and C version with no optimization.

    BUT turning on optimization caused the compiler to reverse the order of the arrays in memory which broke everything.

    I happen to notice that your problem here is with two consecutive arrays...
  • maccamacca Posts: 784
    edited 2013-04-11 23:42
    David Betz wrote: »
    What platform does this program run on and what should it do if it is working?

    It should run on any platform with VGA on P16-P24. It expects a NES controller on P2-P4 but it isn't important to see the results.
    Dave Hein wrote: »
    One problem is the following statement.
        tile_map = (TILEMAP *) &tile_map_2;
    
    The symbol "tile_map_2" will give you the address of the array. I'm not sure what value you get with "&tile_map_2", but I don't think that's what you want. If you didn't cast it with (TILEMAP *) you would get a warning from the compiler that the types don't match. I think you just want to use:
        tile_map = tile_map_2;
    
    Another issue may be because TILEMAP is a 16-bit value, and you may not be getting 32-bit alignment on the arrays. I don't know if you need 32-bit alignment or not. However, I think your main problem is using the "&" operator when assigning the pointer value.

    Because the RENDER_PARAMS structure declares the tile_map member as uint32_t, declaring as TILEMAP * should fix the type match.
    The TILEMAP structure is 16 bits but the array has always an even number of elements so it should always be 32-bit aligned, if that's what you mean. The pointer is used by the rendering COGs code so it must be 32 bit aligned. I guess this may be the problem. I don't know if there are structures or variables that may cause a misalignment, but anyway I'm surprised that this makes a difference when the variable is declared static.
    jazzed wrote: »
    If I make the TILEMAP arrays "static volatile" things work fine.

    Yes with volatile it works, so it appears to be an optimization issue.
  • maccamacca Posts: 784
    edited 2013-04-12 00:01
    Heater. wrote: »
    I had two arrays of constants (look up tables) that were expected to be contiguous in memory. That is accesses off the end of the first one would reach into the second one which would happen to have the correct data in it. Bad practice really.

    All was well in the Spin version and C version with no optimization.

    BUT turning on optimization caused the compiler to reverse the order of the arrays in memory which broke everything.

    I happen to notice that your problem here is with two consecutive arrays...

    No, the arrays are declared consecutively just for clarity but they could be declared in any order and with other variables between them.
  • Heater.Heater. Posts: 21,230
    edited 2013-04-12 00:24
    macca,
    The TILEMAP structure is 16 bits but the array has always an even number of elements so it should always be 32-bit aligned
    
    Why?

    I would expect it to be aligned yes. But I would expect an array of 16 bit items to be aligned to two byte boundries not four. Perhaps if TILEMAP is a struct of two bytes I would expect only byte alignment.
  • maccamacca Posts: 784
    edited 2013-04-12 02:52
    Well, after all, I think this was all due to my mistakes and wrong assumptions.
    Heater. wrote: »
    I would expect it to be aligned yes. But I would expect an array of 16 bit items to be aligned to two byte boundries not four. Perhaps if TILEMAP is a struct of two bytes I would expect only byte alignment.

    I think you are right, I assumed that the alignment was made by looking at the variable size but it should be made by looking at the type size, so the arrays may be not be aligned on 32 bit boundaries. The alignment issue may be confirmed, because if I change the order of the objects in the makefile the problem seems fixed. Maybe it is just a coincidence that it works without the static declaration.

    Then I found two other things, first the array size is wrong, it should be [34 * (VRES / 8 + 2)] and not [34 * (VRES / 8) + 2] this alone seems to fix the problem, even if the missing elements are not that important in that specific case (the missing elements should not be read by the COG and are not written by the code). The other thing is that I found the nes_gamepad_ctrl.c source declares a 16 bit static variable, this could explain the different alignments when the arrays are declared static.

    Is there a way to force the 32 bit alignment on a variable ?
  • Heater.Heater. Posts: 21,230
    edited 2013-04-12 05:11
    macca,
    Is there a way to force the 32 bit alignment on a variable ?
    Perhaps you could declare your TILEMAP as a union of a uint32_t and whatever structure it actually has. That should do it.
  • Heater.Heater. Posts: 21,230
    edited 2013-04-12 05:29
    Something like:
    typedef struct tiledata {
        uint8_t a;
        uint8_t b;
        uint8_t c;
        uint8_t d;
    } TILEDATA_T; 
    
    
    typedef union tilemap {
       uint32_t   align;
       TILEDATA_T d;
    } TILEMAP;
    
    And access the tile data with :
    TILEMAP tm;
        uint8_t x = tm.d.a;
    
  • Heater.Heater. Posts: 21,230
    edited 2013-04-12 05:37
    Of course there is probably some ugly attributes you can add like this:

    static TILEMAP tmarray[10] __attribute__((aligned(4)));

    In which case you don't need the union stuff.
  • maccamacca Posts: 784
    edited 2013-04-12 05:44
    Heater. wrote: »
    macca,

    Perhaps you could declare your TILEMAP as a union of a uint32_t and whatever structure it actually has. That should do it.

    No, I can't. It is 16 bits, or 2 bytes, one tile index and one palette index. Probably in future it will become 6 bits for palette index and 10 bits for tile index, but still 16 bits total.
  • Heater.Heater. Posts: 21,230
    edited 2013-04-12 05:54
    Then you could define a union that contains the entire array of whatever it is overlaid against an array of uint32_t.
  • Dave HeinDave Hein Posts: 6,347
    edited 2013-04-12 08:18
    You could look at the addresses of tile_map_1 and tile_map_2 in the symbol map to see if there 32-bit aligned. Or you could just print the addresses out. Also, you can define tile_map_1 and tile_map_2 as an int32_t array, and cast it to (TILEMAP *) when assigning it to the pointer. This would ensure 32-bit alignment.

    As far as making tile_map_1 and tile_map_2 volatile -- that shouldn't make any difference as long as you always access them through the pointer. It might make a difference if you made the pointer volatile.
  • jazzedjazzed Posts: 11,803
    edited 2013-04-12 09:01
    I'm seriously looking forward to a fully developed demo of this code. The graphics are great on VGA !
    Dave Hein wrote: »
    As far as making tile_map_1 and tile_map_2 volatile -- that shouldn't make any difference as long as you always access them through the pointer. It might make a difference if you made the pointer volatile.

    It certainly made a difference for me (on the default branch). Addresses must have gotten shuffled.

    However, I don't think optimization is the problem since -O0 without volatile still had the same issues.

    The thing is that without static, the display is "close" to being correct. If the arrays were totally lost, the screen would have garbage.
Sign In or Register to comment.