Optimizer causes result difference

Beta Testing discussion on mikroPascal PRO for PIC.
Post Reply
Author
Message
Dany
Posts: 3854
Joined: 18 Jun 2008 11:43
Location: Nieuwpoort, Belgium
Contact:

Optimizer causes result difference

#1 Post by Dany » 01 May 2010 16:03

p.s. I consider this as no actual problem anymore (certainly not for the runtime behaviour, which is correct), as Janni states: The runtime calculations follow from left to right and the highest type of involved operands is assumed.
It is only a small issue that compile time calculations do follow not the same rules. Perhaps this difference should be mentioned clearly in the manual and the helpfile.

The only thing that bothers me is that one does not know when the optimizer is capable to step over from run time calculation to compile time calculation (which is what happened here: the "Wrd1" variable was never used, the literal value was used directly, see the conclusions in http://www.mikroe.com/forum/viewtopic.php?t=20170 and below).


2010-05-07: I changed my mind, the above reasoning that this is a harmless phenomenon is not valid. This is clearly a difference in result due to an optimisation activity of the compiler. Even if the variable "Wrd1" was not actually used, the outcome of the code should be the same as if it was. Optimisation activities should never influence the result. So, I still consider this a major issue (I did also change the title to reflect the actual issue).


valid for mP v1.50 - v4.10
======================

The problem of http://www.mikroe.com/forum/viewtopic.php?t=20170 is still valid:

Problem:
Janni wrote:No more differences between calculations at compile time and runtime!.
Hi Janni, I think there are still some differences somewhere, the code in http://www.mikroe.com/forum/viewtopic.php?t=20170 still gives 2 different results:

Code: Select all

program Type_Casting_cont;
var Wrd1, Wrd2: word;
    Percentage: byte;

procedure Proc(TotalMemSize: word);
var I: byte;
begin
  // ------------ test ------------- //
  Wrd1     := 3968; 
  Percentage := 60;
  Wrd2    := Wrd1 * Percentage div 100;         // Wrd2 = as expected (2380)
  Wrd2    := TotalMemSize * Percentage div 100; // Wrd2 = faulty (414)
  // ------------ end test ------------- //
end;

begin
  Proc(3968);
end.
I do not know if it has something to to with the left-side rule in expression evaluation. :shock:
----------------------------------
Janni's answer:
Dany wrote: I do not know if it has something to to with the left-side rule in expression evaluation. :shock:
No, it does not. The runtime calculation is correct. You just crossed the the word-type range (3968*100=396 800) as calculations follow from left to right and the highest type of involved operands is assumed. The compilation time calculations are wrong, though, because wrong types were used (type of operands is larger than word). Try this
Code:
Wrd2 := word(Wrd1 * Percentage) div 100;
and you'll get the 'right' result (i.e. reflecting the real operands' types).

You found a bug, but it's on the compile-time side. Apparently, operands are not exactly represented in compile-time calculations :( .
Please report it in the beta-testing forum, so it won't be overlooked.

-------------------

The only actual difference between the two cases is that one calculation used the local variable, and the other one a parameter, both of the same size (word).

In the first link http://www.mikroe.com/forum/viewtopic.php?t=20170 you can see that it is due to ram/code optimisations: the variable "Wrd1" is not used but its value is used directly for further calculation (which makes it a compile time calculation):

Code: Select all

  // ------------ test ------------- //
  Wrd1     := 3968; //<---- this variable was not used by the compiler, its value was used as a literal one
  Percentage := 60;
  Wrd2    := Wrd1 * Percentage div 100;         // Wrd2 = as expected (2380)  <--- compile time calculation
  Wrd2    := TotalMemSize * Percentage div 100; // Wrd2 = faulty (414) <--- run time calculation
  // ------------ end test ------------- //
so the code was in reality:

Code: Select all

  // ------------ test ------------- //
  Percentage := 60;
  Wrd2    := 3968 * Percentage div 100;         // Wrd2 = as expected (2380)  <--- compile time calculation
  Wrd2    := TotalMemSize * Percentage div 100; // Wrd2 = faulty (414) <--- run time calculation
  // ------------ end test ------------- //
Thanks in advance!
Last edited by Dany on 25 Sep 2010 10:11, edited 9 times in total.
Kind regards, Dany.
Forget your perfect offering. There is a crack in everything, that's how the light gets in... (L. Cohen)
Remember when we were young? We shone like the sun. (David Gilmour)

Dany
Posts: 3854
Joined: 18 Jun 2008 11:43
Location: Nieuwpoort, Belgium
Contact:

Re: V3.50 bèta: Compile/run time calculation differences?

#2 Post by Dany » 07 May 2010 16:47

Hi, I changed my mind, the above reasoning that this is a harmless phenomenon is not valid. This is clearly a difference in result due to an optimisation activity of the compiler. Even if the variable "Wrd1" was not actually used, the outcome of the code should be the same as if it was. Optimisation activities should never influence the result.
So, I still consider this a major issue (I did also change the title to reflect the actual issue).
Last edited by Dany on 20 May 2010 16:44, edited 1 time in total.
Kind regards, Dany.
Forget your perfect offering. There is a crack in everything, that's how the light gets in... (L. Cohen)
Remember when we were young? We shone like the sun. (David Gilmour)

User avatar
srdjan
mikroElektronika team
Posts: 1552
Joined: 28 Dec 2005 12:47
Location: Serbia

Re: V3.50 bèta: Compile/run time calculation differences?

#3 Post by srdjan » 17 May 2010 10:04

Hi,
It's not optimizer problem, but compiler's expression evaluation rules.

Code: Select all

  // ------------ test ------------- //
  Wrd2    := TotalMemSize * Percentage div 100; // Wrd2 = faulty (414) <--- run time calculation
  // ------------ end test ------------- //
Current rule states that TotalMemSize and Percentage should be multiplied at word level because left side is word,
so we do not need longword result.
However, division can bring longword multiplication result into a word range and there we have a problem.
I'm going to fix this, but the report was entered too late for the current release :(
In the mean time, as a workaround, use explicit typecasting of operands to longword type in order to get correct result.

Dany
Posts: 3854
Joined: 18 Jun 2008 11:43
Location: Nieuwpoort, Belgium
Contact:

Re: V3.50 bèta: Compile/run time calculation differences?

#4 Post by Dany » 17 May 2010 12:41

srdjan wrote:Hi,
It's not optimizer problem, but compiler's expression evaluation rules.

Code: Select all

  // ------------ test ------------- //
  Wrd2    := TotalMemSize * Percentage div 100; // Wrd2 = faulty (414) <--- run time calculation
  // ------------ end test ------------- //
Current rule states that TotalMemSize and Percentage should be multiplied at word level because left side is word,
so we do not need longword result.
However, division can bring longword multiplication result into a word range and there we have a problem.
I'm going to fix this, but the report was entered too late for the current release :(
In the mean time, as a workaround, use explicit typecasting of operands to longword type in order to get correct result.
Hi srdjan, thanks for your reply.

Please be carefull, I am under the impression you did not read the whole story in the posts above.
If I write in your code example

Code: Select all

// Wrd2 = faulty (414) <--- run time calculation
then this result is OK with respect to the expression evaluation rules of the compiler!
The actual problem here is the previous line:

Code: Select all

Wrd2    := Wrd1 * Percentage div 100;         // Wrd2 = as expected (2380)  <--- compile time
which gives a result of 2380 (correct for a human), but according to the evaluation expression rules the result should be 414!

So, I do think you should not act too fast here! The run-time behaviour is OK (acc to the rules), the compile time behaviour is not ok, the result should be the same as the run-time calculation. The difference is there due to the optimizer who did optimize out the usage of variable "Wrd1". But, in both cases, optimized or not, the result of the calculation should be the same, the optimizer should never influence the result.
srdjan wrote:Current rule states that TotalMemSize and Percentage should be multiplied at word level because left side is word, so we do not need longword result.
Apparently (correct me if I'm wrong), the left side size does not inflence any more the resolution of the calculation in v3.80, it is now the biggest size of the right-hand variables. Here it makes no difference, both are words.

See also the analysis of Janni in http://www.mikroe.com/forum/viewtopic.php?f=93&t=24995

My conclusion: please do not change the run-time evaluation behaviour, try to adapt the compile-time evaluation behaviour (which is probably done at maximum Delphi resolution) the same as the run-time one.
Last edited by Dany on 20 May 2010 16:45, edited 1 time in total.
Kind regards, Dany.
Forget your perfect offering. There is a crack in everything, that's how the light gets in... (L. Cohen)
Remember when we were young? We shone like the sun. (David Gilmour)

janni
Posts: 5373
Joined: 18 Feb 2006 13:17
Contact:

Re: Optimizer causes result difference

#5 Post by janni » 17 May 2010 15:18

Hi Srdjan,
Current rule states that TotalMemSize and Percentage should be multiplied at word level because left side is word, so we do not need longword result.
Ouch :!: . I haven't noticed that the left-side type is sometimes taken into account in expression evaluation :( . This distinguishes mP from mC and I hoped all compilers will have the same expression evaluation rules.
Equivalent of

Code: Select all

dword_var:=byte_var*3;
in mC takes just 11 instruction words, while it needs 20 words plus 32-bit multiplication routine in mP :( (and cannot be optimised to mC level with any explicit typecasting).
However, division can bring longword multiplication result into a word range and there we have a problem.
I'm going to fix this...
Is this really necessary? I think that every programmer (especially of microcontrollers) should be aware of type ranges. Apart form introducing another discrepancy between compilers, the simpler the evaluation rule, the better. Simple: 'arithmetic operations will be performed based on the highest type of the operands according to grouping, associativity and precedence rules' would be perfect. Now, it appears, one has to add the condition that operations may be performed according to the assignment's left-side type (not very clear rule) and, if you 'fix the problem', there will be a new exception and possibly less optimal final code...

User avatar
srdjan
mikroElektronika team
Posts: 1552
Joined: 28 Dec 2005 12:47
Location: Serbia

Re: Optimizer causes result difference

#6 Post by srdjan » 19 May 2010 08:30

janni wrote:Hi Srdjan,
Current rule states that TotalMemSize and Percentage should be multiplied at word level because left side is word, so we do not need longword result.
Ouch :!: . I haven't noticed that the left-side type is sometimes taken into account in expression evaluation :( . This distinguishes mP from mC and I hoped all compilers will have the same expression evaluation rules.
Equivalent of

Code: Select all

dword_var:=byte_var*3;
in mC takes just 11 instruction words, while it needs 20 words plus 32-bit multiplication routine in mP :( (and cannot be optimised to mC level with any explicit typecasting).
- C has it's standard defining it this way.
Pascal (ISO 7185:1990) standard I found says:
The required constant-identifier maxint shall denote an implementation-defined value of integertype
. This value shall satisfy the following conditions.
a) All integral values in the closed interval from -maxint to +maxint shall be values of the
integer-type.
b) Any monadic operation performed on an integer value in this interval shall be correctly
performed according to the mathematical rules for integer arithmetic.
c) Any dyadic integer operation on two integer values in this same interval shall be correctly
performed according to the mathematical rules for integer arithmetic, provided that the result
is also in this interval.

d) Any relational operation on two integer values in this same interval shall be correctly performed
according to the mathematical rules for integer arithmetic.

Which means, if I understood it, all expressions must be evaluated correctly!
janni wrote:
However, division can bring longword multiplication result into a word range and there we have a problem.
I'm going to fix this...
Is this really necessary? I think that every programmer (especially of microcontrollers) should be aware of type ranges.
Most of the users do not want to bother with any of those things. They want their expressions evaluated correctly, as they have calculated them :(
janni wrote: Apart form introducing another discrepancy between compilers, the simpler the evaluation rule, the better. Simple: 'arithmetic operations will be performed based on the highest type of the operands according to grouping, associativity and precedence rules' would be perfect. Now, it appears, one has to add the condition that operations may be performed according to the assignment's left-side type (not very clear rule) and, if you 'fix the problem', there will be a new exception and possibly less optimal final code...
What is the highest type when you have operands of word an integer type?
Is it word or integer, both have 2bytes in size?
How should result be treated signed or unsigned?
If we take word as highest type (result) and if we get negative number as a result, then subsequent calculations with this word will not work properly!!!
Same goes for integer, if we get positive result above integer range...
The only way to get this correct is to use longint as a result...

Dany
Posts: 3854
Joined: 18 Jun 2008 11:43
Location: Nieuwpoort, Belgium
Contact:

Re: Optimizer causes result difference

#7 Post by Dany » 19 May 2010 09:12

srdjan wrote:Which means, if I understood it, all expressions must be evaluated correctly!
Hi SrdJan,
I would like to suggest to give up this rule for mikroPascal, and consider the execption as something specific for PIC (with its limited Ram/Rom sources).
Instead please use (as Janni suggests) the same rules as used in the mC compliler (which is I believe: the calculation is done in the resolution of the variable on the right hand side with the highest one). This is the best choice I think for limited resource devices like the PIC.
If the user wants to do the evaluation in a higher resolution then he/she can always do typecasting of intermediate results.

Acting in this manner (and with good documentation), the maximal flexibility and minimal resources use is guaranteed. :D

Keep up the good work! :D
Kind regards, Dany.
Forget your perfect offering. There is a crack in everything, that's how the light gets in... (L. Cohen)
Remember when we were young? We shone like the sun. (David Gilmour)

janni
Posts: 5373
Joined: 18 Feb 2006 13:17
Contact:

Re: Optimizer causes result difference

#8 Post by janni » 19 May 2010 13:42

srdjan wrote:Most of the users do not want to bother with any of those things. They want their expressions evaluated correctly, as they have calculated them :(
While this is certainly true, it's also true that the same users want to have the final code small :( . It's also obvious that the first wish is not possible to fulfill, even in PCs - only the range limit is higher there. Even in Delphi, the biggest integer type (Int64) is used only if one of the operands is of this type, i.e. treated as language extention. Same may be done in mP with Longint and Dword, for a purist approach.
What is the highest type when you have operands of word an integer type?
Is it word or integer, both have 2bytes in size?
How should result be treated signed or unsigned?
The solution is to use signed types whenever expression result could be signed, and unsigned only when all operands are unsigned (as well as the result - in assignments, or reference - in comparisons).
The only way to get this correct is to use longint as a result...
And here a lot of users will protest - why can't we use whole Dword range? (Happily enough, this is not true - yet) The maximum usefull integral range in small microcontrollers is simply too low due to RAM limitations. Same is true for program memory.
As ideal cannot be reached, I'd opt (as Dany) for simple expression evaluation rules and smallest possible ROM use.

joseLB
Posts: 444
Joined: 02 Apr 2006 05:56
Location: Riode Janeiro, Brasil

Re: Optimizer causes result difference

#9 Post by joseLB » 24 May 2010 00:46

janni wrote:As ideal cannot be reached, I'd opt (as Dany) for simple expression evaluation rules and smallest possible ROM use.
Seems a good compromise. Have my vote too!
Thanks
Jose

Dany
Posts: 3854
Joined: 18 Jun 2008 11:43
Location: Nieuwpoort, Belgium
Contact:

Re: Optimizer causes result difference

#10 Post by Dany » 24 May 2010 08:25

joseLB wrote:
janni wrote:As ideal cannot be reached, I'd opt (as Dany) for simple expression evaluation rules and smallest possible ROM use.
Seems a good compromise. Have my vote too!
Thanks
Jose
I think this is indeed, the best solution.

Remains: the original problem: Optimizer cause result difference. If the optimizer decides to use "Constant propagation" (as is the case here), then the compile time calculations should give the same result as if it was a runtime calculation.
Kind regards, Dany.
Forget your perfect offering. There is a crack in everything, that's how the light gets in... (L. Cohen)
Remember when we were young? We shone like the sun. (David Gilmour)

Dany
Posts: 3854
Joined: 18 Jun 2008 11:43
Location: Nieuwpoort, Belgium
Contact:

Re: Optimizer causes result difference

#11 Post by Dany » 25 Sep 2010 10:15

Issue still there in v4.10.
Kind regards, Dany.
Forget your perfect offering. There is a crack in everything, that's how the light gets in... (L. Cohen)
Remember when we were young? We shone like the sun. (David Gilmour)

User avatar
srdjan
mikroElektronika team
Posts: 1552
Joined: 28 Dec 2005 12:47
Location: Serbia

Re: Optimizer causes result difference

#12 Post by srdjan » 27 Sep 2010 13:35

Hi,
Dany wrote:Issue still there in v4.10.
It's in our ToDo list, but considering all the things waiting to be done, this one has a low priority.
We'll fix it as soon as time permits.
Thank you for understanding.

Dany
Posts: 3854
Joined: 18 Jun 2008 11:43
Location: Nieuwpoort, Belgium
Contact:

Re: Optimizer causes result difference

#13 Post by Dany » 27 Sep 2010 19:37

srdjan wrote:Hi,
Dany wrote:Issue still there in v4.10.
It's in our ToDo list, but considering all the things waiting to be done, this one has a low priority.
Thanks, I understand of course. I do not try to make you nervous, I just report the current status because of the new compiler version.
Keep up the good work! :D :D
Kind regards, Dany.
Forget your perfect offering. There is a crack in everything, that's how the light gets in... (L. Cohen)
Remember when we were young? We shone like the sun. (David Gilmour)

shazinaz
Posts: 1
Joined: 29 Sep 2011 11:27

Re: Optimizer causes result difference

#14 Post by shazinaz » 29 Sep 2011 11:32

I have gotten some good information here.

Post Reply

Return to “mikroPascal PRO for PIC Beta Testing”