Jump to content
Sign in to follow this  
Pavel

Why Your Code Got Bigger

Recommended Posts

We got complains from several users that their code/data size increased when compiled with 6.13 (or any post 6.04) compiler vs 6.04. We looked at several such cases and here are the explanations:

 

The short answer is because post 6.04 compilers support 32 bit long data types and what you see is the downside of 32 bit support.

 

 

And here is the long answer based on some sample code. There was a function in the code which had the following lines:

 

ADresult = n * 9;         // put in the 4 of 4.892
ADresult = ADresult + (( n * 7 ) / 10);       // put in the 8 of 4.892
ADresult = ADresult + (( n * 7 ) / 100);    // put in the 9 of 4.892
ADresult = ADresult + (( n * 5 ) / 1000);    // put in the 2 of 4.892

 

Let's try to split these complex expressions into a number of more simple ones:

the first line:

unsigned16 * unsigned8 -> unsigned16

next two lines:

unsigned16 * unsigned8 -> unsigned32

unsigned32 / unsigned8 -> unsigned32

unsigned16 + unsigned32 -> unsigned16

the last line:

unsigned16 * unsigned8 -> unsigned32

unsigned32 / unsigned16 -> unsigned32

unsigned16 + unsigned32 -> unsigned16

 

For the same code 6.04 compiler used:

 

the first line:

unsigned16 * unsigned8 -> unsigned16

next two lines:

unsigned16 * unsigned8 -> unsigned16

unsigned16 / unsigned8 -> unsigned16

unsigned16 + unsigned16 -> unsigned16

the last line:

unsigned16 * unsigned8 -> unsigned16

unsigned16 / unsigned16 -> unsigned16

unsigned16 + unsigned16 -> unsigned16

 

Have you noticed the difference. Because the new compiler supports 32 bit long data types in many cases it now has to assume that result of an expression may be 32 bits long too. For example when you multiply 16 bit long operand by 8 bit long operand compiler assumes that the result is 32 bit long and allocates 4 bytes for it while 6.04 for the same expression assumed the result 16 bit long.

 

Now about a possible solution. If for example you know that n*5 or n*7 will always fit into 16 bits (what was the case for this particulat sample because code did work under 6.04) you can change the code so that 32 bit multiplication or division won't be used. The trick is to make compiler use multiplication and division of 16 bit long operands:

 

unsigned short tmp;
ADresult = n * 9;         // put in the 4 of 4.892
tmp = n * 7;
ADresult += tmp / 10;       // put in the 8 of 4.892
ADresult += tmp / 100;    // put in the 9 of 4.892
tmp = n * 5;
ADresult += tmp / 1000;    // put in the 2 of 4.892

 

This simple change will reduce both code and data size dramatically.

 

Regards,

Pavel

Share this post


Link to post
Share on other sites

Thanks for the feedback Pavel, it really helps us users to understand what's going on...and helps diagnose problems when we're pushing the compiler to the limit. Much appreciated and one of the reasons I like SourceBoost so much.

 

I don't suppose a later release could have a switch to detect these 32 v 16 bit situations and generate appropriate code based on the data size of the result variable?

 

Phil

Share this post


Link to post
Share on other sites

Phil,

 

I don't suppose a later release could have a switch to detect these 32 v 16 bit situations and generate appropriate code based on the data size of the result variable?
How the compiler going to do that?

It doesn't know what numbers you may feed into an expression,

 

 int x,y;
x = 100;
y = 200;

z = x * y; // 100 * 200 = 20000, fits into integer
y = 2000;
z = x * y; // 100 * 2000 = 200000, needs a long integer

If you also imagine that x and y could be computed from another expression, you soon see that the compiler would need a crystal ball to be able to predict the result.

 

Regards

Dave

Share this post


Link to post
Share on other sites

An alternative to splitting the expression up is to cast down the intermediate result, this prevents any 32 bit maths from being performed eg:

 

	ADresult = (long)n * 9;         // put in the 4 of 4.892
ADresult = ADresult + ((int)( n * 7 ) / 10);       // put in the 8 of 4.892
ADresult = ADresult + ((int)( n * 7 ) / 100);    // put in the 9 of 4.892
ADresult = ADresult + ((int)( n * 5 ) / 1000);    // put in the 2 of 4.892

But this does produce 50% more code that Pavels optimal solution.

 

Regards

Dave

Share this post


Link to post
Share on other sites

Sorry....I might not have read the explanation properly.

 

I was thinking that the optimisation switch would force the compiler to use 32 or 16bit (or 8bit etc) maths in all the operations based on the result. So if you define

unsigned long nBig;

unsigned short nSmall;

 

Then (if option enabled)

nBig = X * 1000; // would use 32 bit code

nSmall = X * 1000; // would use 16bit code

It would be up to the user to ensure their code wouldn't do something silly eg set X=80 when calculating nSmall....of course a warning similiar to those already in place should be issued.

 

Phil

Share this post


Link to post
Share on other sites

You are right... partially :) Regardless of optimization:

 

long nBig = X * 1000; // uses 32 bit code

short nBig = X * 1000; // uses 16 bit code

 

But the original code was a bit different. Something like this:

 

short nBig = X * 1000 / Y;

 

can't use 16 bit code in multiplication or the result may be incorrect. For this particular expression 32 bit multiplication is used and produces 32 bit long temporary result that is than devided by Y.

 

Regards,

Pavel

Share this post


Link to post
Share on other sites

Yep I understand that generally you would have to use the highest bit number maths to take into account these possibilities.

What I was wondering was if this could be turned on/off by option or a pragma and that the user would be responsible for any code fragments that they may write, and generate smaller code if they wanted to. In the example below 16 bit code could be generated regardless:

 

...

#pragma USE_16_BIT_MATHS //generate a compiler warning to check code

short nBig = X * 1000 / Y; //uses 16bit maths...it's OK, I've checked my operands!

....and other code in my highly optimised routine

#pragma USE_DEFAULT_MATHS

short nNormal = X * 1000 / Y; //uses default 32 bit maths

 

Probably not very 'C' standard...but would be handy in a microcontroller focussed compiler so we can make code as small as possible. Any example would be using 16bit maths when playing around with timer registers.

 

Phil

 

You are right... partially :) Regardless of optimization:

 

long nBig = X * 1000;  // uses 32 bit code

short nBig = X * 1000;  // uses 16 bit code

 

But the original code was a bit different. Something like this:

 

short nBig = X * 1000 / Y;

 

can't use 16 bit code in multiplication or the result may be incorrect. For this particular expression 32 bit multiplication is used and produces 32 bit long temporary result that is than devided by Y.

 

Regards,

Pavel

Share this post


Link to post
Share on other sites

Join the conversation

You are posting as a guest. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
Sign in to follow this  

×
×
  • Create New...