CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to CCS Technical Support

CCS - duplicate global variables allowed?

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
allenhuffman



Joined: 17 Jun 2019
Posts: 552
Location: Des Moines, Iowa, USA

View user's profile Send private message Visit poster's website

CCS - duplicate global variables allowed?
PostPosted: Thu Dec 19, 2019 10:21 am     Reply with quote

We stumbled into a previous issue where duplicate function names did not generate a compiler warning or error. Even using the keyword "static" did not help.

I did a quick test with global variables, and found that duplicates are also allowed with no warning or error.

If there are duplicates, the code will use the one from the earliest C file specified in the Multi Unit Compilation file.

Code:
// main.c

#include <main.h>

//int g_value = 0;
extern int g_value;

int main()
{
   printf ("main()\r\n");

   printf ("main - g_value = %d\r\n", g_value);
   
   foo ();
   
   bar ();

   printf ("main - g_value = %d\r\n", g_value);
 
   while(TRUE)
   {
      //TODO: User Code
   }
}


Code:
#include <main.h>

int S_value = 100;

void foo()
{
   printf ("foo - g_value = %d\r\n", g_value);
}


Code:
#include <main.h>

int g_value = 200;

void bar()
{
   printf ("bar - g_value = %d\r\n", g_value);
}



foo() and bar() both have a global called g_value, each initialized to different values (100 and 200).

If I add them the multi-unit compile in the order of main, foo and bar, I get this:

main, foo, bar:

main()
main - g_value = 100
foo - g_value = 100
bar - g_value = 200
main - g_value = 100


main, bar, foo:

main()
main - g_value = 200
foo - g_value = 100
bar - g_value = 200
main - g_value = 200


Fun, eh?
_________________
Allen C. Huffman, Sub-Etha Software (est. 1990) http://www.subethasoftware.com
Embedded C, Arduino, MSP430, ESP8266/32, BASIC Stamp and PIC24 programmer.
http://www.whywouldyouwanttodothat.com ?
temtronic



Joined: 01 Jul 2010
Posts: 9221
Location: Greensville,Ontario

View user's profile Send private message

PostPosted: Thu Dec 19, 2019 12:29 pm     Reply with quote

this...
If there are duplicates, the code will use the one from the earliest C file specified in the Multi Unit Compilation file.

makes sense to me, though I don't know the 'ins and outs' of C or compilers...

I look at it this way...
The compiier assigns or relates the variable 'g_value' to a section of RAM within the PIC memory map, say to RAM location or register 0x0123. All later or subsequent references to 'g_value' will be redirected or assigned to that location 0x0123.

That is no different than having in main()...

g_value=00;
....code
g_value=01;
...more code
g_value=02;

There aren't 3 different variables called g_value, just one where the data is changed In my case from 00 to 01 to 02.


What's disturbing to me is that apparently in C++ you can have 3 different functions named the same, where passing the same data( say 0x55) will result in 3 different answers ! How it could keep track of what type of data you're passing, surely has to increase the 'overhead' and program size ??
jeremiah



Joined: 20 Jul 2010
Posts: 1345

View user's profile Send private message

PostPosted: Thu Dec 19, 2019 12:42 pm     Reply with quote

allenhuffman wrote:

Fun, eh?

That's actually somewhat normal. The C language allows for multiple variables in different compilation units to be named the same as long as their symbols don't overlap. The ANSI language spec doesn't define HOW to do that, and leaves that up to the implementation. In ANSI-C, it doesn't define anything past what it calls the "abstract machine", and this is an implementation detail below that level.

In GCC, you do that by declaring either foo or bar as static and leaving the other non static. In that case, the extern will follow whichever g_value is NOT static (and thus has a public symbol).

CCS doesn't use the same type of linker (if you can even call it a true linker), so it is free to implement that how it likes. In its case, it chooses to pick the first one that it finds, and it is allowed to do so. That said, it could still be a bug if they didn't intend for their implementation to act that way. I would recommend an email to them asking about it.

Note that even GCC uses ordered linking for some things. I've been able to supply certain .o files to it that already exist and if mine are first in the linking order, it takes those. I'm assuming the supplied ones are defined with a linker alias and weak attributes, but don't know.


Last edited by jeremiah on Thu Dec 19, 2019 1:07 pm; edited 1 time in total
jeremiah



Joined: 20 Jul 2010
Posts: 1345

View user's profile Send private message

PostPosted: Thu Dec 19, 2019 12:53 pm     Reply with quote

temtronic wrote:

What's disturbing to me is that apparently in C++ you can have 3 different functions named the same, where passing the same data( say 0x55) will result in 3 different answers ! How it could keep track of what type of data you're passing, surely has to increase the 'overhead' and program size ??


they generally handle it at the linking stage so it doesn't take any more code than if you used 3 differently named functions. The spec doesn't specify how to do this (again, implementation detail...makes C++ ABI's very interesting to deal with when there is no spec on this). But most compilers do some form of "name mangling" at the linker level. When they translate it to object code, in reality it's just addresses (not actual names). But they also generate a "symbol table" which pairs addresses with symbol names. This doesn't take up any code space...it's only used to generate the EXE. In the symbol table, they name each function based on the declaration parameters and name, so you might see 3 separate symbols in the table: fooXyzz1, fooZyzz1, fooMyzz1, where the Xyzz1, Myzz1, and Zyzz1 are all symbols for separate types of parameter lists (I made those up, but the actual symbols look like that kind of stuff).

The patterns they generate for the names are all deterministic and are not too dissimilar from creating a "hash" of the function name and parameter list. So the separate object files keep track of the physical address of each function, and the symbol table keeps track of the symbol name and which address is associated with it. When they go to generate code for main, it will reference "symbol names" for any external symbols, look them up in the symbol table, and then get the address and replace the call in main to use that instead of the symbol.

Take the following code compiled with GCC 9.1.0:
Code:

typedef struct{
        int value;
} t1;

typedef struct{
        int value;
} t2;

typedef struct{
        int value;
} t3;

void foo(t1 v, int i){}
void foo(t2 v, int i){}
void foo(t3 v, int i){}

int main(void){
   t1 v1;
   t2 v2;
   t3 v3;

   foo(v1,2);
   foo(v2,2);
   foo(v3,2);
   return 0;
}



if I dump the symbol table for that program, I can see all 3 foos there:
Code:

0000000000401510 T _Z3foo2t1i
000000000040151d T _Z3foo2t2i
000000000040152a T _Z3foo2t3i


I forget what the Z3 specifically stands for, but it has to do with the void return type, foo is the name, 2 is the number of paramters, i is for integer, then there is t1/t2/t3
Ttelmah



Joined: 11 Mar 2010
Posts: 19499

View user's profile Send private message

PostPosted: Thu Dec 19, 2019 1:07 pm     Reply with quote

CCS does support overloaded functions, and they work. BUT. The variables
used have to be different. So use an int8 and an int16, and two different
functions of the same name can exist and be used. What it doesn't do
is distinguish aliases for the same physical variable type....
allenhuffman



Joined: 17 Jun 2019
Posts: 552
Location: Des Moines, Iowa, USA

View user's profile Send private message Visit poster's website

PostPosted: Thu Dec 19, 2019 2:03 pm     Reply with quote

Since we accidentally stumbled upon this, it is very good to be aware of since there are (sadly) a ton of global variables being used in existing code.

And yea, C++ generates tons of overhead compare to C. I never learned C++ for that reason, since non of the embedded jobs I have ever had would use it due to code bloat.
_________________
Allen C. Huffman, Sub-Etha Software (est. 1990) http://www.subethasoftware.com
Embedded C, Arduino, MSP430, ESP8266/32, BASIC Stamp and PIC24 programmer.
http://www.whywouldyouwanttodothat.com ?
jeremiah



Joined: 20 Jul 2010
Posts: 1345

View user's profile Send private message

PostPosted: Thu Dec 19, 2019 7:06 pm     Reply with quote

allenhuffman wrote:
And yea, C++ generates tons of overhead compare to C. I never learned C++ for that reason, since non of the embedded jobs I have ever had would use it due to coat bloat.


Not necessarily. It can if you use certain features in a way not tailored towards embedded, but all of the embedded C++ applications I have done on my arm chips have not generated any bloat and are comparable to C programs (I verified with the assembly). Essentially, you want to avoid exceptions and the standard library completely and if you plan on using templates or lambdas, you need to use some design patterns. All the remaining C features can be used with no real bloat or cost. Scott Meyers (a well know C++ author) has done quite a few books and presentations on the subject.

And now as of C++ 11/14/17, C++ code is often times smaller than equivalent C due to constexpr and some other features that offload processing to the compiler instead of your code. Heck, I've done code using virtual functions that was smaller than the equivalent in C using case statements.

Now if you program C++ on an OS, it can bloat up, especially since applications for OS'es (.exe's for example) tend to put a lot more stuff in the binary than just the actual code, but you generally don't do that for embedded bare metal binaries (and honestly most compilers do give tools to strip out that stuff if you don't want it).

EDIT: here's an example: I created a templated static class that represents the EIC (External Interrupt Controller) on my ATSAML21 board and used it to clear the EIC interrupt flag and increment a global counter variable:

Code:

// clear the interrupt handler
 408:   2204         movs   r2, #4
 40a:   4b03         ldr   r3, [pc, #12]   ; (418 <EIC_Handler+0x10>)
 40c:   60da         str   r2, [r3, #12]

// post increment the global
 40e:   4a03         ldr   r2, [pc, #12]   ; (41c <EIC_Handler+0x14>)
 410:   6813         ldr   r3, [r2, #0]
 412:   3301         adds   r3, #1
 414:   6013         str   r3, [r2, #0]

// leave the interrupt
 416:   4770         bx   lr

// data
 418:   40002408    .word   0x40002408  ; Register address
 41c:   2000044c    .word   0x2000044c  ; global variable address


I rewrote it in straight C and it was identical. It's more verbose than PIC24 assembly, but the cortex M0+ doesn't have a large instruction set. But both C and C++ generated the same code. A lot of the magic comes from things like constexpr and using static polymorphism (though now that you can write constexpr constructors, standard polymorphism is also pretty versatile).

I will say it takes more background in C++ to know how to write embedded code, but using C++ doesn't have to mean bloat.
allenhuffman



Joined: 17 Jun 2019
Posts: 552
Location: Des Moines, Iowa, USA

View user's profile Send private message Visit poster's website

PostPosted: Fri Dec 20, 2019 8:26 am     Reply with quote

jeremiah wrote:
Not necessarily. It can if you use certain features in a way not tailored towards embedded, but all of the embedded C++ applications I have done on my arm chips have not generated any bloat and are comparable to C programs (I verified with the assembly). Essentially, you want to avoid exceptions and the standard library completely and if you plan on using templates or lambdas, you need to use some design patterns. All the remaining C features can be used with no real bloat or cost. Scott Meyers (a well know C++ author) has done quite a few books and presentations on the subject.


I'd like to find a book on embedded programming in C++. It's used an awful lot in job postings around here, but "embedded" these days can mean an ARM processors with megs of RAM ;-)

The main thing I'd like to use it for is doing basic object oriented stuff. How bad is the overhead for that? (Does the CCS compiler go that far with C++ stuff, or just allow function overloads?)

Like, it sure would be handy to have the object contain all its private variables, and get to it with:

obj.Open()
obj.Update()
obj.Close()

...instead of having to use static globals in that C file.
_________________
Allen C. Huffman, Sub-Etha Software (est. 1990) http://www.subethasoftware.com
Embedded C, Arduino, MSP430, ESP8266/32, BASIC Stamp and PIC24 programmer.
http://www.whywouldyouwanttodothat.com ?
jeremiah



Joined: 20 Jul 2010
Posts: 1345

View user's profile Send private message

PostPosted: Fri Dec 20, 2019 3:42 pm     Reply with quote

allenhuffman wrote:
jeremiah wrote:
Not necessarily. It can if you use certain features in a way not tailored towards embedded, but all of the embedded C++ applications I have done on my arm chips have not generated any bloat and are comparable to C programs (I verified with the assembly). Essentially, you want to avoid exceptions and the standard library completely and if you plan on using templates or lambdas, you need to use some design patterns. All the remaining C features can be used with no real bloat or cost. Scott Meyers (a well know C++ author) has done quite a few books and presentations on the subject.


I'd like to find a book on embedded programming in C++. It's used an awful lot in job postings around here, but "embedded" these days can mean an ARM processors with megs of RAM ;-)

The main thing I'd like to use it for is doing basic object oriented stuff. How bad is the overhead for that? (Does the CCS compiler go that far with C++ stuff, or just allow function overloads?)

Like, it sure would be handy to have the object contain all its private variables, and get to it with:

obj.Open()
obj.Update()
obj.Close()

...instead of having to use static globals in that C file.


Scott Meyers retired, so he no longer does the class I was talking about, but he does sell his presentation materials from the class:
https://www.artima.com/shop/effective_cpp_in_an_embedded_environment

And for abstractions where you want the organization of classes but don't need actual objects (like peripherals for example), also consider static polymorphism:
https://www.youtube.com/watch?v=k8sRQMx2qUw

Note that you can still use objects coupled with constexpr and good optimizing C++ compilers. These are just options.

Side note, my arm processor that I program both C++ and Ada on has about 32k RAM. I only tend to use 2-8k of that for most programs.

Just for kicks, here is some mixed C and C++ compiled on GCC 9.2
Code:

// Type your code here, or load an example.
class Test{
public:
    constexpr Test();
    constexpr void Set(int a);
    constexpr int Get() const;
private:
    int m_value;
};

constexpr Test::Test() : m_value(0) {}
constexpr void Test::Set(int a){m_value = a;}
constexpr int Test::Get() const {return m_value;};

extern "C" void Set(int a);
extern "C" int Get();
static int s_value = 0;

void Set(int a){ s_value = a; }
int Get() {return s_value;}

void something(void) {
    Test v1;
    v1.Set(32);

    Set(v1.Get());

}


You can see the use of the class for variable v1, calling the setter for it and then calling the getter. Using the -O2 flag (for max optimization), the assembly generated by the code inside the something() function is merely:

Code:

something():
        mov     DWORD PTR s_value[rip], 32
        ret


All of that C++ code got replaced with just "32". I don't know how long godbolt stores links, but here is a link to it compiled on godbolt

https://godbolt.org/z/FyNwmt
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group