Discussion:
Unexpected behaviour with intermediate results
(too old to reply)
J. Gareth Moreton
2018-06-12 00:07:37 UTC
Permalink
Raw Message
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
J. Gareth Moreton
2018-06-12 09:21:37 UTC
Permalink
Raw Message
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" ***@moreton-family.com
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
_______________________________________________
fpc-devel maillist - fpc-***@lists.freepascal.org [1]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-devel
[2]">http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-devel



Links:
------
[1] mailto:fpc-***@lists.freepascal.org
[2] http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-devel
Giuliano Colla
2018-06-12 11:42:49 UTC
Permalink
Raw Message
Post by J. Gareth Moreton
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 code at all. Register 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

_______________________________________________
fpc-devel maillist - fpc-***@lists.freepascal.org
http://lists.freepascal.org/cgi-bin/mailman/listin
J. Gareth Moreton
2018-06-12 10:59:07 UTC
Permalink
Raw Message
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
Post by J. Gareth Moreton
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 code at all. Register 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

_______________________________________________
fpc-devel maillist - fpc-***@lists.freepascal.org [1]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-devel
[2]">http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-devel



Links:
------
[1] mailto:fpc-***@lists.freepascal.org
[2] http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-devel
Stefan Glienke
2018-06-12 13:10:54 UTC
Permalink
Raw Message
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)
Post by 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.
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
_______________________________________________
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-devel
_______________________________________________
fpc-devel maillist - fpc-***@lists.freepascal.org
http://lists.freepascal.org/cgi-bin/ma
J. Gareth Moreton
2018-06-12 16:48:34 UTC
Permalink
Raw Message
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

Loading...