Recent

Author Topic: likely bug in FPC v3.2.2 handling of booleans  (Read 8358 times)

Thaddy

  • Hero Member
  • *****
  • Posts: 18729
  • To Europe: simply sell USA bonds: dollar collapses
Re: likely bug in FPC v3.2.2 handling of booleans
« Reply #15 on: January 28, 2025, 11:01:45 am »
No, not here. which version are you using?
Also note he uses the stack... and the stack is dirty.
It is simply not a bug, but a perception.
You can go on forever, but it does not make sense. >:D >:D >:D

There, you have it, didn't do that for months. O:-)
If Europe sells their USA bonds the USD will collapse. Europe can affort that given average state debts. The USA can't affort that. Just an advice...

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 12641
  • FPC developer.
Re: likely bug in FPC v3.2.2 handling of booleans
« Reply #16 on: January 28, 2025, 11:34:35 am »
Basically putting an undefined value in b8 an b16 makes anything that follows uncertain.

It seems the code paths for b8 and b16 are slightly different since b16 tests the value with
orw   %di,%di
setneb   %al

So that is a not equal test, not a =1 test. That might indicate a bug, but might also be considered an optimization.

tetrastes

  • Hero Member
  • *****
  • Posts: 737
Re: likely bug in FPC v3.2.2 handling of booleans
« Reply #17 on: January 28, 2025, 11:58:29 am »
I don't say that it's a bug.
I say that this is because boolean is bitpacked to one bit (simply truncated):
Code: Pascal  [Select][+][-]
  1. BooleanArray : array[0..1] of Boolean = (FALSE, FALSE);
  2. . . .
  3. ord(b8)  := 4;
  4. . . .
b8  = TRUE ord(b8)  = 4 BooleanArray[0] = TRUE
b16 = TRUE ord(b16) = 8 BooleanArray[1] = TRUE
SizeOf(BooleanArray) = 2
ord(BooleanArray[0]) = 4
ord(BooleanArray[1]) = 1

Code: Pascal  [Select][+][-]
  1. BooleanArray : bitpacked array[0..1] of Boolean = (FALSE, FALSE);
  2. . . .
  3. ord(b8)  := 4;
  4. . . .
b8  = TRUE ord(b8)  = 4 BooleanArray[0] = FALSE
b16 = TRUE ord(b16) = 8 BooleanArray[1] = TRUE
SizeOf(BooleanArray) = 1
ord(BooleanArray[0]) = 0
ord(BooleanArray[1]) = 1

Code: Pascal  [Select][+][-]
  1. BooleanArray : bitpacked array[0..1] of Boolean = (FALSE, FALSE);
  2. . . .
  3. ord(b8)  := 5;
  4. . . .
b8  = TRUE ord(b8)  = 5 BooleanArray[0] = TRUE
b16 = TRUE ord(b16) = 8 BooleanArray[1] = TRUE
SizeOf(BooleanArray) = 1
ord(BooleanArray[0]) = 1
ord(BooleanArray[1]) = 1

You may also play with bitpacked array of Boolean16:
Code: Pascal  [Select][+][-]
  1. BooleanArray : array[0..1] of Boolean16 = (FALSE, FALSE);
  2. . . .
  3. ord(b8)  := 4;
  4. ord(b16)  := 8;
  5. . . .
b8  = TRUE ord(b8)  = 4 BooleanArray[0] = TRUE
b16 = TRUE ord(b16) = 8 BooleanArray[1] = TRUE
SizeOf(BooleanArray) = 4
ord(BooleanArray[0]) = 1 ord(BooleanArray[1]) = 8

Code: Pascal  [Select][+][-]
  1. BooleanArray : bitpacked array[0..1] of Boolean16 = (FALSE, FALSE);
  2. . . .
  3. ord(b8)  := 4;
  4. ord(b16)  := 8;
  5. . . .
b8  = TRUE ord(b8)  = 4 BooleanArray[0] = TRUE
b16 = TRUE ord(b16) = 8 BooleanArray[1] = FALSE
SizeOf(BooleanArray) = 1
ord(BooleanArray[0]) = 1 ord(BooleanArray[1]) = 0

FPC versions 3.2.2, 3.2.3-1400-g1bcaaacfe4 [2024/09/23], 3.3.1-16887-g7ac4e38b71 [2024/11/12].

440bx

  • Hero Member
  • *****
  • Posts: 6073
Re: likely bug in FPC v3.2.2 handling of booleans
« Reply #18 on: January 28, 2025, 01:34:18 pm »
Let's look at the code again (added a few lines to output truth values of the variables):
Code: Pascal  [Select][+][-]
  1. {$MODE OBJFPC }
  2.  
  3. program BooleanBug;
  4.  
  5. function TestBoolean() : boolean;
  6. var
  7.   b8     : Boolean   = FALSE;
  8.   b16    : Boolean16 = FALSE;
  9.  
  10.   BooleanArray : bitpacked array[0..1] of boolean = (FALSE, FALSE);
  11.  
  12. begin
  13.   ord(b8)  := 4;                // true
  14.   ord(b16) := 8;                // true
  15.  
  16.   writeln('ord(b8)  = ', ord(b8));
  17.   writeln('ord(b16) = ', ord(b16));
  18.   writeln;
  19.  
  20.   { ------------------------------------------------------------------------- }
  21.   { have the compiler output how it sees the values of b8 and b16             }
  22.  
  23.   { in both cases it compares the value against 0 to determine if the value   }
  24.   { is TRUE or FALSE.  This means that, the compiler is NOT considering 1 as  }
  25.   { the value TRUE but, any non-zero value which is NOT what the documentation}
  26.   { says the compiler does with Boolean, Boolean16, Boolean32 and Boolean64   }
  27.  
  28.   if b8  then writeln('b8  is TRUE') else writeln('b8  is FALSE');
  29.   if b16 then writeln('b16 is TRUE') else writeln('b16 is FALSE');
  30.   writeln;
  31.  
  32.   { ------------------------------------------------------------------------- }
  33.  
  34.   // assign the variables to the bitpacked elements
  35.   // both, b8 and b16 are supposedly true but, when assigned to a bitpacked
  36.   // boolean, one of them is no longer true.
  37.  
  38.   BooleanArray[0] := b8;
  39.   writeln('b8  = ', b8,  ' ord(b8)  = ', ord(b8),  ' BooleanArray[0] = ', BooleanArray[0]);
  40.   BooleanArray[1] := b16;
  41.   writeln('b16 = ', b16, ' ord(b16) = ', ord(b16), ' BooleanArray[1] = ', BooleanArray[1]);
  42.   writeln;
  43.  
  44.   result := true;
  45. end;
  46.  
  47. begin
  48.   TestBoolean();
  49. end.
  50.  
The writeln statements in line 28 and 29 establish that the compiler chooses to interpret the values of b8 and b16 as TRUE.  This is a choice made by the compiler.  This choice can be clearly seen in the assembly code where the compiler tests both values against zero (0) to determine whether their value is TRUE or FALSE.  Refer to the attachment.

Therefore, there can be no doubt that, as far as the compiler is concerned, b8 and b16 are both TRUE.  Fact established by comparing against zero (0) which means that, contrary to the documentation, it is NOT the value 1 that represents TRUE but any non-zero value.

The bug is simple, what the compiler considers a TRUE value is being assigned to BooleanArray[0] and to BooleanArray[1], therefore they should both be TRUE but, they are not both TRUE, one is and the other one isn't.  Refer to the output part of the attachment.

Based on the documentation, b8 and b16 should both be FALSE since their least significant bit is not set and the value 1 is supposedly the only value that yields TRUE. See "Table 3.4 Boolean types" (which by the way is missing the Boolean8 type.)

Essentially, the compiler is _sometimes_ treating b8 and b16 as if they were ByteBool and WordBool respectively and sometimes not.  This can be observed in the assembly code where the value b8 is compared against the value 1 to determine its truth value while the value of b16 is determined by whether or not b16 is non-zero.

The inconsistent treatment which is exposed when assigning the values to a bitpacked array is the bug.
« Last Edit: January 28, 2025, 01:35:50 pm by 440bx »
FPC v3.2.2 and Lazarus v4.0rc3 on Windows 7 SP1 64bit.

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 12097
  • Debugger - SynEdit - and more
    • wiki
Re: likely bug in FPC v3.2.2 handling of booleans
« Reply #19 on: January 28, 2025, 02:36:29 pm »
Basically putting an undefined value in b8 an b16 makes anything that follows uncertain.

It seems the code paths for b8 and b16 are slightly different since b16 tests the value with
orw   %di,%di
setneb   %al

So that is a not equal test, not a =1 test. That might indicate a bug, but might also be considered an optimization.

I would consider it optimization. But basically it is a "not zero" test => which is correct to. zero is one of the two allowed values.


The interesting difference in asm code happens if you do
Code: Pascal  [Select][+][-]
  1. if mybool1 = bybool2 then

If those are "boolean", then they may be binary compared. Meaning that "boolean(2) <> boolean(3)".

But if they are e.g. ByteBool then they will be tested as  (or similar)
Code: Pascal  [Select][+][-]
  1.   ( ord(mybool1)<>0 ) =  ( ord(mybool2)<>0 )

Meaning a lot more generated asm code.

Thaddy

  • Hero Member
  • *****
  • Posts: 18729
  • To Europe: simply sell USA bonds: dollar collapses
Re: likely bug in FPC v3.2.2 handling of booleans
« Reply #20 on: January 28, 2025, 03:28:29 pm »
Based on the documentation, b8 and b16 should both be FALSE since their least significant bit is not set and the value 1 is supposedly the only value that yields TRUE.
Which is what I consistently get on trunk/main, as I already stated. On ARM architectures, I did not test intel/amd.
There has been quite some work regarding boolean types in main as opposed to 3.2.2.

It may be that the misunderstanding comes from there.

Another thing is that platform may be involved:
The adagium true is not false may have crept in after all and that is indeed not correct: false-true is 0-1 for Pascal booleans of any type. So the low bit must always be set to be true.
If the optimization is such that the value 1 for true is not correctly interpreted then indeed this is a bug, because as Martin wrote, both 4 and 8 should be false since the first bit is false for both.
If Europe sells their USA bonds the USD will collapse. Europe can affort that given average state debts. The USA can't affort that. Just an advice...

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 12097
  • Debugger - SynEdit - and more
    • wiki
Re: likely bug in FPC v3.2.2 handling of booleans
« Reply #21 on: January 28, 2025, 06:21:36 pm »
Code: Pascal  [Select][+][-]
  1. var
  2.   b8     : Boolean   = FALSE;
  3.   b16    : Boolean16 = FALSE;
  4.  

Based on the documentation, b8 and b16 should both be FALSE since their least significant bit is not set and the value 1 is supposedly the only value that yields TRUE.

"One is the only one that yields true" is a half truth.

Any value that is not one or zero is an undefined value, and can yield (at random) yield either true or false.

Hence "One is the only one that can be trusted to yields true"

Fred vS

  • Hero Member
  • *****
  • Posts: 3778
    • StrumPract is the musicians best friend
Re: likely bug in FPC v3.2.2 handling of booleans
« Reply #22 on: January 28, 2025, 06:57:34 pm »
Sorry to be a troublemaker but why use the problematic boolean?
It's easier to use byte and if result < 1 it's false and if result > 0 it's true.
I use Lazarus 2.2.0 32/64 and FPC 3.2.2 32/64 on Debian 11 64 bit, Windows 10, Windows 7 32/64, Windows XP 32,  FreeBSD 64.
Widgetset: fpGUI, MSEgui, Win32, GTK2, Qt.

https://github.com/fredvs
https://gitlab.com/fredvs
https://codeberg.org/fredvs

TRon

  • Hero Member
  • *****
  • Posts: 4377
Re: likely bug in FPC v3.2.2 handling of booleans
« Reply #23 on: January 28, 2025, 07:43:35 pm »
.. why use the problematic boolean?
Sometimes it is not about whether or not the code makes actual sense or a problem could be solved by using other code but how the compiler behaves (under certain conditions).

Sometimes the compiler does things that doesn't make sense (at least to me) while it should. Most of the times it is due to a bad interpretation of the documentation or lack thereof but sometimes it is valid to question the behaviour or at least try to get a better understanding of it.

In this particular case TS expects the compiler to behave in a certain way while it doesn't seem to do so. Whether or not it should behave the way it seem to do for the example turns into a debate (as living proof this thread) that usually follows when questioning behaviour of the compiler.
Today is tomorrow's yesterday.

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 12641
  • FPC developer.
Re: likely bug in FPC v3.2.2 handling of booleans
« Reply #24 on: January 28, 2025, 07:58:43 pm »
Sorry to be a troublemaker but why use the problematic boolean?
It's easier to use byte and if result < 1 it's false and if result > 0 it's true.

Unless you accidentally use a signed expression. So use <>0.

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 12641
  • FPC developer.
Re: likely bug in FPC v3.2.2 handling of booleans
« Reply #25 on: January 28, 2025, 08:07:35 pm »
It seems the code paths for b8 and b16 are slightly different since b16 tests the value with
orw   %di,%di
setneb   %al

So that is a not equal test, not a =1 test. That might indicate a bug, but might also be considered an optimization.

I would consider it optimization. But basically it is a "not zero" test => which is correct to. zero is one of the two allowed values.

I think so that the optimisation bit is defensible too. The question is if that optimisation was intended :)

So it might be worth a bugreport and some tests on other architectures.

Zoran

  • Hero Member
  • *****
  • Posts: 1980
    • http://wiki.lazarus.freepascal.org/User:Zoran
Re: likely bug in FPC v3.2.2 handling of booleans
« Reply #26 on: January 28, 2025, 08:43:07 pm »
but the issue is simply that he seems to think Ord() is a cast, whereas it is of course a function.
Then why
Code: Pascal  [Select][+][-]
  1. ord(b8)  := 4;
is compiled at all?

You cannot do that with a function:
Code: Pascal  [Select][+][-]
  1. function BoolToInt(b: boolean): longint;
  2. begin
  3.     Result := ord(b);
  4. end;
  5. . . .
  6.  
  7. BoolToInt(b8) := 4;
  8.  
is not compiled with "Error: Argument cannot be assigned to".

Does anyone knows the answer to this question?
Ord() is a function, it is documented to be a function, it is not a cast, however it might be implemented internally. The compiler must not allow direct assignment to a function result, right?
« Last Edit: January 28, 2025, 08:46:06 pm by Zoran »
Swan, ZX Spectrum emulator https://github.com/zoran-vucenovic/swan

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 12641
  • FPC developer.
Re: likely bug in FPC v3.2.2 handling of booleans
« Reply #27 on: January 28, 2025, 08:45:25 pm »
Well, ord() is not a function

Zoran

  • Hero Member
  • *****
  • Posts: 1980
    • http://wiki.lazarus.freepascal.org/User:Zoran
Re: likely bug in FPC v3.2.2 handling of booleans
« Reply #28 on: January 28, 2025, 08:51:43 pm »
Well, ord() is not a function

The documentation says that it is a function. Thaddy is right there.
It might be implemented as a cast, but that should be just an implementation detail.
This is a bug. At least a documentation bug, but I don't think so.
Swan, ZX Spectrum emulator https://github.com/zoran-vucenovic/swan

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 12097
  • Debugger - SynEdit - and more
    • wiki
Re: likely bug in FPC v3.2.2 handling of booleans
« Reply #29 on: January 28, 2025, 08:57:14 pm »
It seems the code paths for b8 and b16 are slightly different since b16 tests the value with
orw   %di,%di
setneb   %al

So that is a not equal test, not a =1 test. That might indicate a bug, but might also be considered an optimization.

I would consider it optimization. But basically it is a "not zero" test => which is correct to. zero is one of the two allowed values.

I think so that the optimisation bit is defensible too. The question is if that optimisation was intended :)

So it might be worth a bugreport and some tests on other architectures.

Is there a case were it does not work as documented?

Btw, I was to fast (but still stand by, its ok, and even looks indented)

It is not a test for zero.

Well, the "orw" does set the zero flag (if di is zero)
Then the "setneb"  NE  => Not Equal => zero flag not set.

But the "setneb" is why I say its indented. Its a conditional set, it avoids a branch. So the CPU can much faster execute it (no branch prediction needed)

 

TinyPortal © 2005-2018