Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-devel / 2018-06

Unexpected behaviour with intermediate results

6 messages · 3 people ·

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

J. Gareth Moreton

https://bugs.freepascal.org/view.php?id=33851
Someone pointed out that if you perform "(x shl 8) shr 8", where x is of type Word, the result is always x, even though logic dictates that the upper 8 bits should be shifted out and hence the result actually be equal to "x and $FF".  After analysing the assembly language that's produced, it turns out that the optimiser works with the full 32-bits of the register.  For example:

 is_this_a_bug:=((counter shl 8) shr 8);

...becomes the following under -O3 (under -O1, %r13w and %si are replaced with references)...

movzwl %r13w,%eax
shl    $0x8,%eax
shr    $0x8,%eax
mox    %al,%si

A similar situation happens if the variables are of type Byte as well - the intermediate values use the full 32 bits of the register.

I'm not certain if this is a bug or intended behaviour though.  Can someone more senior make a decision on this?

Gareth

Reply by email

J. Gareth Moreton

I will add though that Delphi apparently does the same thing, so if any changes are made so precision is lost as expected (even though loss of precision is usually not a desired trait), it shouldn't occur under -mDelphi.

Personally I'm under the opinion that precision should be lost because that is the expected behaviour, as well as noting the fact that the behaviour is different depending on if you're working with 32-bit numbers compared to 8 or 16-bit values.

Gareth

On Tue 12/06/18 01:07 , "J. Gareth Moreton"
sent:
https://bugs.freepascal.org/view.php?id=33851
Someone pointed out that if you perform "(x shl 8) shr 8", where x is of type Word, the result is always x, even though logic dictates that the upper 8 bits should be shifted out and hence the result actually be equal to "x and $FF".  After analysing the assembly language that's produced, it turns out that the optimiser works with the full 32-bits of the register.  For example:

 is_this_a_bug:=((counter shl 8) shr 8);

...becomes the following under -O3 (under -O1, %r13w and %si are replaced with references)...

movzwl %r13w,%eax
shl    $0x8,%eax
shr    $0x8,%eax
mox    %al,%si

A similar situation happens if the variables are of type Byte as well - the intermediate values use the full 32 bits of the register.

I'm not certain if this is a bug or intended behaviour though.  Can someone more senior make a decision on this?

Gareth

Reply by email

J. Gareth Moreton

Hence why I wish to delegate this to someone more senior to decide if this is a bug or a feature.  Seems that logic has more than one path.  Mathematically, yes, shl 8 and shr 8 should cancel out completely and be a null operation.
Gareth

On Tue 12/06/18 12:42 , Giuliano Colla sent:
Il 12/06/2018 02:07, J. Gareth Moreton ha scritto:

Someone pointed out that if you perform "(x shl 8) shr 8", where x is of type Word, the result is always x, even though logic dictates that the upper 8 bits should be shifted out and hence the result actually be equal to "x and $FF".

IMHO logic dictates that (x shl 8) shr 8 should give x, because the shr cancels the effect of shl, and that a further compiler optimization might simply suppress the size when performing a
calculation is not dictated by logic, but by physical constraints, which have nothing to do with logic.

If the programmer wishes to mask out some bits he should explicitly use an *and* instruction, making the code both more readable and more portable. Generating a less efficient code, with and instructions added with the sole purpose of supporting the improper usage of a shl instruction wouldn't be very smart.

I'm in favour of a compiler which generates the most efficient code, and leaves to the programmer the responsibility to mask unwanted bits.

Just my 2 cents.

Giuliano

Reply by email

Giuliano Colla
to J. Gareth Moreton: https://bugs.freepascal.org/view.php?id=33851 Someone pointed out that if you perform "(x…

Il 12/06/2018 02:07, J. Gareth Moreton ha scritto:

Someone pointed out that if you perform "(x shl 8) shr 8", where x is of type Word, the result is always x, even though logic dictates that the upper 8 bits should be shifted out and hence the result actually be equal to "x and $FF".

IMHO logic dictates that (x shl 8) shr 8 should give x, because the shr cancels the effect of shl, and that a further compiler optimization might simply suppress the size when performing a
calculation is not dictated by logic, but by physical constraints, which have nothing to do with logic.

If the programmer wishes to mask out some bits he should explicitly use an *and* instruction, making the code both more readable and more portable. Generating a less efficient code, with and instructions added with the sole purpose of supporting the improper usage of a shl instruction wouldn't be very smart.

I'm in favour of a compiler which generates the most efficient code, and leaves to the programmer the responsibility to mask unwanted bits.

Just my 2 cents.

Giuliano

Reply by email

Stefan Glienke
to J. Gareth Moreton: https://bugs.freepascal.org/view.php?id=33851 Someone pointed out that if you perform "(x…

The real bug imo is that is that it even generates shl and shr instructions.

b := (a shl x) shr x should be just compile to b := a for x in 0..24 and a being 8bit (which is what several C++ compilers do afaik)
Show quoted text 31 lines
On 12 June 2018 at 02:07 "J. Gareth Moreton" wrote: https://bugs.freepascal.org/view.php?id=33851 Someone pointed out that if you perform "(x shl 8) shr 8", where x is of type Word, the result is always x, even though logic dictates that the upper 8 bits should be shifted out and hence the result actually be equal to "x and $FF". After analysing the assembly language that's produced, it turns out that the optimiser works with the full 32-bits of the register. For example: is_this_a_bug:=((counter shl 8) shr 8); ...becomes the following under -O3 (under -O1, %r13w and %si are replaced with references)... movzwl %r13w,%eax shl $0x8,%eax shr $0x8,%eax mox %al,%si A similar situation happens if the variables are of type Byte as well - the intermediate values use the full 32 bits of the register. I'm not certain if this is a bug or intended behaviour though. Can someone more senior make a decision on this? Gareth _______________________________________________ fpc-devel maillist - http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-devel

Reply by email

J. Gareth Moreton

As pointed out in the bug report, it's not a bug.  As listed in the documentation over here:
https://www.freepascal.org/docs-html/current/ref/refsu4.html

"Every integer smaller than the ”native” size is promoted to a signed version of the ”native” size. Integers equal to the ”native” size keep their signedness."

Apologies for not abiding by RTFM - it's hard to find all the little notes sometimes.  Still, this is useful to know when it comes to future optimization work.
Gareth

Reply by email