diff mbox series

[4/9] drivers/iio: Sign extend without triggering implementation-defined behavior

Message ID 20191028200700.213753-5-bvanassche@acm.org (mailing list archive)
State Not Applicable
Headers show
Series Consolidate {get,put}_unaligned_[bl]e24() definitions | expand

Commit Message

Bart Van Assche Oct. 28, 2019, 8:06 p.m. UTC
From the C standard: "The result of E1 >> E2 is E1 right-shifted E2 bit
positions. If E1 has an unsigned type or if E1 has a signed type and a
nonnegative value, the value of the result is the integral part of the
quotient of E1 / 2E2 . If E1 has a signed type and a negative value, the
resulting value is implementation-defined."

Hence use sign_extend_24_to_32() instead of "<< 8 >> 8".

Cc: Jonathan Cameron <jic23@kernel.org>
Cc: Hartmut Knaack <knaack.h@gmx.de>
Cc: Lars-Peter Clausen <lars@metafoo.de>
Cc: Peter Meerwald-Stadler <pmeerw@pmeerw.net>
Signed-off-by: Bart Van Assche <bvanassche@acm.org>
---
 drivers/iio/common/st_sensors/st_sensors_core.c | 7 +------
 1 file changed, 1 insertion(+), 6 deletions(-)

Comments

Jonathan Cameron Oct. 30, 2019, 7:43 p.m. UTC | #1
On Mon, 28 Oct 2019 13:06:55 -0700
Bart Van Assche <bvanassche@acm.org> wrote:

> From the C standard: "The result of E1 >> E2 is E1 right-shifted E2 bit
> positions. If E1 has an unsigned type or if E1 has a signed type and a
> nonnegative value, the value of the result is the integral part of the
> quotient of E1 / 2E2 . If E1 has a signed type and a negative value, the
> resulting value is implementation-defined."
> 
> Hence use sign_extend_24_to_32() instead of "<< 8 >> 8".

+CC linux-iio

> 
> Cc: Jonathan Cameron <jic23@kernel.org>
> Cc: Hartmut Knaack <knaack.h@gmx.de>
> Cc: Lars-Peter Clausen <lars@metafoo.de>
> Cc: Peter Meerwald-Stadler <pmeerw@pmeerw.net>
> Signed-off-by: Bart Van Assche <bvanassche@acm.org>
> ---
>  drivers/iio/common/st_sensors/st_sensors_core.c | 7 +------
>  1 file changed, 1 insertion(+), 6 deletions(-)
> 
> diff --git a/drivers/iio/common/st_sensors/st_sensors_core.c b/drivers/iio/common/st_sensors/st_sensors_core.c
> index 4a3064fb6cd9..94a9cec69cd7 100644
> --- a/drivers/iio/common/st_sensors/st_sensors_core.c
> +++ b/drivers/iio/common/st_sensors/st_sensors_core.c
> @@ -21,11 +21,6 @@
>  
>  #include "st_sensors_core.h"
>  
> -static inline u32 st_sensors_get_unaligned_le24(const u8 *p)
> -{
> -	return (s32)((p[0] | p[1] << 8 | p[2] << 16) << 8) >> 8;
> -}
> -
>  int st_sensors_write_data_with_mask(struct iio_dev *indio_dev,
>  				    u8 reg_addr, u8 mask, u8 data)
>  {
> @@ -556,7 +551,7 @@ static int st_sensors_read_axis_data(struct iio_dev *indio_dev,
>  	else if (byte_for_channel == 2)
>  		*data = (s16)get_unaligned_le16(outdata);
>  	else if (byte_for_channel == 3)
> -		*data = (s32)st_sensors_get_unaligned_le24(outdata);
> +		*data = get_unaligned_signed_le24(outdata);
>  
>  st_sensors_free_memory:
>  	kfree(outdata);
Peter Zijlstra Oct. 30, 2019, 8:02 p.m. UTC | #2
On Mon, Oct 28, 2019 at 01:06:55PM -0700, Bart Van Assche wrote:
> From the C standard: "The result of E1 >> E2 is E1 right-shifted E2 bit
> positions. If E1 has an unsigned type or if E1 has a signed type and a
> nonnegative value, the value of the result is the integral part of the
> quotient of E1 / 2E2 . If E1 has a signed type and a negative value, the
> resulting value is implementation-defined."

FWIW, we actually hard rely on this implementation defined behaviour all
over the kernel. See for example the generic sign_extend{32,64}()
functions.

AFAIR the only reason the C standard says this is implementation defined
is because it wants to support daft things like 1s complement and
saturating integers.

Luckily, Linux doesn't run on any such hardware and we hard rely on
signed being 2s complement and tell the compiler that by using
-fno-strict-overflow (which implies -fwrapv).

And the only sane choice for 2s complement signed shift right is
arithmetic shift right.

(this recently came up in another thread, which I can't remember enough
of to find just now, and I'm not sure we got a GCC person to confirm if
-fwrapv does indeed guarantee arithmetic shift, the GCC documentation
does not mention this)
Douglas Gilbert Oct. 30, 2019, 10:13 p.m. UTC | #3
On 2019-10-30 4:02 p.m., Peter Zijlstra wrote:
> On Mon, Oct 28, 2019 at 01:06:55PM -0700, Bart Van Assche wrote:
>>  From the C standard: "The result of E1 >> E2 is E1 right-shifted E2 bit
>> positions. If E1 has an unsigned type or if E1 has a signed type and a
>> nonnegative value, the value of the result is the integral part of the
>> quotient of E1 / 2E2 . If E1 has a signed type and a negative value, the
>> resulting value is implementation-defined."
> 
> FWIW, we actually hard rely on this implementation defined behaviour all
> over the kernel. See for example the generic sign_extend{32,64}()
> functions.
> 
> AFAIR the only reason the C standard says this is implementation defined
> is because it wants to support daft things like 1s complement and
> saturating integers.

See:
    http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2218.htm

That is in C++20 and on the agenda for C2x:
    https://gustedt.wordpress.com/2018/11/12/c2x/

Doug Gilbert

> Luckily, Linux doesn't run on any such hardware and we hard rely on
> signed being 2s complement and tell the compiler that by using
> -fno-strict-overflow (which implies -fwrapv).
> 
> And the only sane choice for 2s complement signed shift right is
> arithmetic shift right.
> 
> (this recently came up in another thread, which I can't remember enough
> of to find just now, and I'm not sure we got a GCC person to confirm if
> -fwrapv does indeed guarantee arithmetic shift, the GCC documentation
> does not mention this)
>
Bart Van Assche Oct. 31, 2019, 5:55 p.m. UTC | #4
On 10/30/19 3:13 PM, Douglas Gilbert wrote:
> On 2019-10-30 4:02 p.m., Peter Zijlstra wrote:
>> On Mon, Oct 28, 2019 at 01:06:55PM -0700, Bart Van Assche wrote:
>>>  From the C standard: "The result of E1 >> E2 is E1 right-shifted E2 bit
>>> positions. If E1 has an unsigned type or if E1 has a signed type and a
>>> nonnegative value, the value of the result is the integral part of the
>>> quotient of E1 / 2E2 . If E1 has a signed type and a negative value, the
>>> resulting value is implementation-defined."
>>
>> FWIW, we actually hard rely on this implementation defined behaviour all
>> over the kernel. See for example the generic sign_extend{32,64}()
>> functions.
>>
>> AFAIR the only reason the C standard says this is implementation defined
>> is because it wants to support daft things like 1s complement and
>> saturating integers.
> 
> See:
>     http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2218.htm
> 
> That is in C++20 and on the agenda for C2x:
>     https://gustedt.wordpress.com/2018/11/12/c2x/

Thanks Peter and Doug. This is very useful feedback. I will drop the 
sign_extend_24_to_32() function and use sign_extend32() instead.

Bart.
diff mbox series

Patch

diff --git a/drivers/iio/common/st_sensors/st_sensors_core.c b/drivers/iio/common/st_sensors/st_sensors_core.c
index 4a3064fb6cd9..94a9cec69cd7 100644
--- a/drivers/iio/common/st_sensors/st_sensors_core.c
+++ b/drivers/iio/common/st_sensors/st_sensors_core.c
@@ -21,11 +21,6 @@ 
 
 #include "st_sensors_core.h"
 
-static inline u32 st_sensors_get_unaligned_le24(const u8 *p)
-{
-	return (s32)((p[0] | p[1] << 8 | p[2] << 16) << 8) >> 8;
-}
-
 int st_sensors_write_data_with_mask(struct iio_dev *indio_dev,
 				    u8 reg_addr, u8 mask, u8 data)
 {
@@ -556,7 +551,7 @@  static int st_sensors_read_axis_data(struct iio_dev *indio_dev,
 	else if (byte_for_channel == 2)
 		*data = (s16)get_unaligned_le16(outdata);
 	else if (byte_for_channel == 3)
-		*data = (s32)st_sensors_get_unaligned_le24(outdata);
+		*data = get_unaligned_signed_le24(outdata);
 
 st_sensors_free_memory:
 	kfree(outdata);