[dpdk-dev,1/2] eal: add static endianness conversion macros

Message ID 840342851720fc237214aeb30d38565615293b58.1495101988.git.adrien.mazarguil@6wind.com (mailing list archive)
State Superseded, archived
Headers

Checks

Context Check Description
ci/checkpatch success coding style OK
ci/Intel-compilation success Compilation OK

Commit Message

Adrien Mazarguil May 18, 2017, 10:14 a.m. UTC
  These macros resolve to constant expressions that allow developers to
perform endianness conversion on static/const objects, even outside of
function scope as they do not translate to function calls.

This is most useful for static initializers and constant values (whenever
it has to be performed at compilation time). Run-time endianness conversion
of variable values should keep using rte_*_to_*() calls for best
performance.

Signed-off-by: Adrien Mazarguil <adrien.mazarguil@6wind.com>
---
 .../common/include/generic/rte_byteorder.h      | 58 +++++++++++++++-----
 1 file changed, 44 insertions(+), 14 deletions(-)
  

Comments

Thomas Monjalon June 7, 2017, 2:16 p.m. UTC | #1
Hi, some comments below:

18/05/2017 12:14, Adrien Mazarguil:
> These macros resolve to constant expressions that allow developers to
> perform endianness conversion on static/const objects, even outside of
> function scope as they do not translate to function calls.
> 
> This is most useful for static initializers and constant values (whenever
> it has to be performed at compilation time). Run-time endianness conversion
> of variable values should keep using rte_*_to_*() calls for best
> performance.
> 
> Signed-off-by: Adrien Mazarguil <adrien.mazarguil@6wind.com>
[...]
> +#define RTE_STATIC_BSWAP64(v) \
> +	((((uint64_t)(v) & UINT64_C(0x00000000000000ff)) << 56) | \
> +	 (((uint64_t)(v) & UINT64_C(0x000000000000ff00)) << 40) | \
> +	 (((uint64_t)(v) & UINT64_C(0x0000000000ff0000)) << 24) | \
> +	 (((uint64_t)(v) & UINT64_C(0x00000000ff000000)) << 8) | \
> +	 (((uint64_t)(v) & UINT64_C(0x000000ff00000000)) >> 8) | \
> +	 (((uint64_t)(v) & UINT64_C(0x0000ff0000000000)) >> 24) | \
> +	 (((uint64_t)(v) & UINT64_C(0x00ff000000000000)) >> 40) | \
> +	 (((uint64_t)(v) & UINT64_C(0xff00000000000000)) >> 56))

Minor nit: you could align lines by inserting a space before 8.

> +#if RTE_BYTE_ORDER == RTE_BIG_ENDIAN
> +#define RTE_BE16(v) (uint16_t)(v)
> +#define RTE_BE32(v) (uint32_t)(v)
> +#define RTE_BE64(v) (uint64_t)(v)
> +#define RTE_LE16(v) RTE_STATIC_BSWAP16(v)
> +#define RTE_LE32(v) RTE_STATIC_BSWAP32(v)
> +#define RTE_LE64(v) RTE_STATIC_BSWAP64(v)
> +#elif RTE_BYTE_ORDER == RTE_LITTLE_ENDIAN
> +#define RTE_BE16(v) RTE_STATIC_BSWAP16(v)
> +#define RTE_BE32(v) RTE_STATIC_BSWAP32(v)
> +#define RTE_BE64(v) RTE_STATIC_BSWAP64(v)
> +#define RTE_LE16(v) (uint16_t)(v)
> +#define RTE_LE32(v) (uint32_t)(v)
> +#define RTE_LE64(v) (uint64_t)(v)

This naming is confusing.
Let's take RTE_BE16() as example, it does not say wether the input value
is big endian or the output value will be big endian.
I think we should mimic the wording of run-time conversions:
	RTE_BE_TO_CPU_16()

Any other ideas?
  
Adrien Mazarguil June 8, 2017, 9:14 a.m. UTC | #2
On Wed, Jun 07, 2017 at 04:16:58PM +0200, Thomas Monjalon wrote:
> Hi, some comments below:
> 
> 18/05/2017 12:14, Adrien Mazarguil:
> > These macros resolve to constant expressions that allow developers to
> > perform endianness conversion on static/const objects, even outside of
> > function scope as they do not translate to function calls.
> > 
> > This is most useful for static initializers and constant values (whenever
> > it has to be performed at compilation time). Run-time endianness conversion
> > of variable values should keep using rte_*_to_*() calls for best
> > performance.
> > 
> > Signed-off-by: Adrien Mazarguil <adrien.mazarguil@6wind.com>
> [...]
> > +#define RTE_STATIC_BSWAP64(v) \
> > +	((((uint64_t)(v) & UINT64_C(0x00000000000000ff)) << 56) | \
> > +	 (((uint64_t)(v) & UINT64_C(0x000000000000ff00)) << 40) | \
> > +	 (((uint64_t)(v) & UINT64_C(0x0000000000ff0000)) << 24) | \
> > +	 (((uint64_t)(v) & UINT64_C(0x00000000ff000000)) << 8) | \
> > +	 (((uint64_t)(v) & UINT64_C(0x000000ff00000000)) >> 8) | \
> > +	 (((uint64_t)(v) & UINT64_C(0x0000ff0000000000)) >> 24) | \
> > +	 (((uint64_t)(v) & UINT64_C(0x00ff000000000000)) >> 40) | \
> > +	 (((uint64_t)(v) & UINT64_C(0xff00000000000000)) >> 56))
> 
> Minor nit: you could align lines by inserting a space before 8.

I think alignment attempts past the mandatory line indentation often end up
in a failure (e.g. when grouping macros by name, one of them inevitably
happens to be longer than initially envisioned, same for structure fields
and trailing comment blocks, etc.) Since I'm not convinced it improves
readability, I tend to avoid them altogether for consistency.

It's a matter of style but I can change that if you prefer.

> > +#if RTE_BYTE_ORDER == RTE_BIG_ENDIAN
> > +#define RTE_BE16(v) (uint16_t)(v)
> > +#define RTE_BE32(v) (uint32_t)(v)
> > +#define RTE_BE64(v) (uint64_t)(v)
> > +#define RTE_LE16(v) RTE_STATIC_BSWAP16(v)
> > +#define RTE_LE32(v) RTE_STATIC_BSWAP32(v)
> > +#define RTE_LE64(v) RTE_STATIC_BSWAP64(v)
> > +#elif RTE_BYTE_ORDER == RTE_LITTLE_ENDIAN
> > +#define RTE_BE16(v) RTE_STATIC_BSWAP16(v)
> > +#define RTE_BE32(v) RTE_STATIC_BSWAP32(v)
> > +#define RTE_BE64(v) RTE_STATIC_BSWAP64(v)
> > +#define RTE_LE16(v) (uint16_t)(v)
> > +#define RTE_LE32(v) (uint32_t)(v)
> > +#define RTE_LE64(v) (uint64_t)(v)
> 
> This naming is confusing.
> Let's take RTE_BE16() as example, it does not say wether the input value
> is big endian or the output value will be big endian.
> I think we should mimic the wording of run-time conversions:
> 	RTE_BE_TO_CPU_16()
> 
> Any other ideas?

First I'd like to keep those macro names as short as possible, ideally not
much larger than simply casting the provided value to the target type for
usability and readability purposes. Think about files full of static
initializers, while there are not many examples right now, the definition of
static rte_flow rules and capability trees will need to use these macros
extensively.

The fact you suggested RTE_BE_TO_CPU_16() instead of RTE_CPU_TO_BE_16() as a
replacement for RTE_BE16() highlights the misunderstanding. However I find
"CPU_TO" overly verbose, particularly since the reverse macros won't exist,
remember these are made for static conversions of integer constants resolved
at compilation time, not variables. Users may additionally confuse
RTE_CPU_TO_BE_16() with its similarly-named inline function counterpart.

Functions and macros are typically named after their output, not their
input. In that sense and without further precision, RTE_BE16() is fine in my
opinion.

Remember this [1]? I think we could make everything clearer by perhaps
applying it and casting the results of these macros to the proper type,
e.g.:

 #define RTE_BE16(v) (rte_be16_t)(v)

I can probably modify this series to introduce the new types first, use them
in the conversion macro and then later clarify existing structure
fields. How about this?

[1] http://dpdk.org/ml/archives/dev/2016-November/050060.html
  
Thomas Monjalon June 8, 2017, 4:35 p.m. UTC | #3
08/06/2017 11:14, Adrien Mazarguil:
> On Wed, Jun 07, 2017 at 04:16:58PM +0200, Thomas Monjalon wrote:
> > Hi, some comments below:
> > 
> > 18/05/2017 12:14, Adrien Mazarguil:
> > > +#define RTE_STATIC_BSWAP64(v) \
> > > +	((((uint64_t)(v) & UINT64_C(0x00000000000000ff)) << 56) | \
> > > +	 (((uint64_t)(v) & UINT64_C(0x000000000000ff00)) << 40) | \
> > > +	 (((uint64_t)(v) & UINT64_C(0x0000000000ff0000)) << 24) | \
> > > +	 (((uint64_t)(v) & UINT64_C(0x00000000ff000000)) << 8) | \
> > > +	 (((uint64_t)(v) & UINT64_C(0x000000ff00000000)) >> 8) | \
> > > +	 (((uint64_t)(v) & UINT64_C(0x0000ff0000000000)) >> 24) | \
> > > +	 (((uint64_t)(v) & UINT64_C(0x00ff000000000000)) >> 40) | \
> > > +	 (((uint64_t)(v) & UINT64_C(0xff00000000000000)) >> 56))
> > 
> > Minor nit: you could align lines by inserting a space before 8.
> 
> I think alignment attempts past the mandatory line indentation often end up
> in a failure (e.g. when grouping macros by name, one of them inevitably
> happens to be longer than initially envisioned, same for structure fields
> and trailing comment blocks, etc.) Since I'm not convinced it improves
> readability, I tend to avoid them altogether for consistency.

I agree
Here it is just adding a space in front of the single digit to make
bits numbers aligned on 2 digits :)

> It's a matter of style but I can change that if you prefer.
> 
> > > +#if RTE_BYTE_ORDER == RTE_BIG_ENDIAN
> > > +#define RTE_BE16(v) (uint16_t)(v)
> > > +#define RTE_BE32(v) (uint32_t)(v)
> > > +#define RTE_BE64(v) (uint64_t)(v)
> > > +#define RTE_LE16(v) RTE_STATIC_BSWAP16(v)
> > > +#define RTE_LE32(v) RTE_STATIC_BSWAP32(v)
> > > +#define RTE_LE64(v) RTE_STATIC_BSWAP64(v)
> > > +#elif RTE_BYTE_ORDER == RTE_LITTLE_ENDIAN
> > > +#define RTE_BE16(v) RTE_STATIC_BSWAP16(v)
> > > +#define RTE_BE32(v) RTE_STATIC_BSWAP32(v)
> > > +#define RTE_BE64(v) RTE_STATIC_BSWAP64(v)
> > > +#define RTE_LE16(v) (uint16_t)(v)
> > > +#define RTE_LE32(v) (uint32_t)(v)
> > > +#define RTE_LE64(v) (uint64_t)(v)
> > 
> > This naming is confusing.
> > Let's take RTE_BE16() as example, it does not say wether the input value
> > is big endian or the output value will be big endian.
> > I think we should mimic the wording of run-time conversions:
> > 	RTE_BE_TO_CPU_16()
> > 
> > Any other ideas?
> 
> First I'd like to keep those macro names as short as possible, ideally not
> much larger than simply casting the provided value to the target type for
> usability and readability purposes. Think about files full of static
> initializers, while there are not many examples right now, the definition of
> static rte_flow rules and capability trees will need to use these macros
> extensively.
> 
> The fact you suggested RTE_BE_TO_CPU_16() instead of RTE_CPU_TO_BE_16() as a
> replacement for RTE_BE16() highlights the misunderstanding. However I find
> "CPU_TO" overly verbose, particularly since the reverse macros won't exist,
> remember these are made for static conversions of integer constants resolved
> at compilation time, not variables. Users may additionally confuse
> RTE_CPU_TO_BE_16() with its similarly-named inline function counterpart.

You're right.
RTE_BE_TO_CPU_16 does not make sense.
I think you could add a comment like that:
RTE_XE_NN is equivalent to rte_cpu_to_Xe_NN run-time conversion

> Functions and macros are typically named after their output, not their
> input. In that sense and without further precision, RTE_BE16() is fine in my
> opinion.

Good point.

> Remember this [1]? I think we could make everything clearer by perhaps
> applying it and casting the results of these macros to the proper type,
> e.g.:
> 
>  #define RTE_BE16(v) (rte_be16_t)(v)
> 
> I can probably modify this series to introduce the new types first, use them
> in the conversion macro and then later clarify existing structure
> fields. How about this?

Yes good idea.
  

Patch

diff --git a/lib/librte_eal/common/include/generic/rte_byteorder.h b/lib/librte_eal/common/include/generic/rte_byteorder.h
index e00bccb..8408872 100644
--- a/lib/librte_eal/common/include/generic/rte_byteorder.h
+++ b/lib/librte_eal/common/include/generic/rte_byteorder.h
@@ -74,6 +74,47 @@ 
 #elif defined __LITTLE_ENDIAN__
 #define RTE_BYTE_ORDER RTE_LITTLE_ENDIAN
 #endif
+#if !defined(RTE_BYTE_ORDER)
+#error Unknown endianness.
+#endif
+
+#define RTE_STATIC_BSWAP16(v) \
+	((((uint16_t)(v) & UINT16_C(0x00ff)) << 8) | \
+	 (((uint16_t)(v) & UINT16_C(0xff00)) >> 8))
+
+#define RTE_STATIC_BSWAP32(v) \
+	((((uint32_t)(v) & UINT32_C(0x000000ff)) << 24) | \
+	 (((uint32_t)(v) & UINT32_C(0x0000ff00)) << 8) | \
+	 (((uint32_t)(v) & UINT32_C(0x00ff0000)) >> 8) | \
+	 (((uint32_t)(v) & UINT32_C(0xff000000)) >> 24))
+
+#define RTE_STATIC_BSWAP64(v) \
+	((((uint64_t)(v) & UINT64_C(0x00000000000000ff)) << 56) | \
+	 (((uint64_t)(v) & UINT64_C(0x000000000000ff00)) << 40) | \
+	 (((uint64_t)(v) & UINT64_C(0x0000000000ff0000)) << 24) | \
+	 (((uint64_t)(v) & UINT64_C(0x00000000ff000000)) << 8) | \
+	 (((uint64_t)(v) & UINT64_C(0x000000ff00000000)) >> 8) | \
+	 (((uint64_t)(v) & UINT64_C(0x0000ff0000000000)) >> 24) | \
+	 (((uint64_t)(v) & UINT64_C(0x00ff000000000000)) >> 40) | \
+	 (((uint64_t)(v) & UINT64_C(0xff00000000000000)) >> 56))
+
+#if RTE_BYTE_ORDER == RTE_BIG_ENDIAN
+#define RTE_BE16(v) (uint16_t)(v)
+#define RTE_BE32(v) (uint32_t)(v)
+#define RTE_BE64(v) (uint64_t)(v)
+#define RTE_LE16(v) RTE_STATIC_BSWAP16(v)
+#define RTE_LE32(v) RTE_STATIC_BSWAP32(v)
+#define RTE_LE64(v) RTE_STATIC_BSWAP64(v)
+#elif RTE_BYTE_ORDER == RTE_LITTLE_ENDIAN
+#define RTE_BE16(v) RTE_STATIC_BSWAP16(v)
+#define RTE_BE32(v) RTE_STATIC_BSWAP32(v)
+#define RTE_BE64(v) RTE_STATIC_BSWAP64(v)
+#define RTE_LE16(v) (uint16_t)(v)
+#define RTE_LE32(v) (uint32_t)(v)
+#define RTE_LE64(v) (uint64_t)(v)
+#else
+#error Unsupported endianness.
+#endif
 
 /*
  * An internal function to swap bytes in a 16-bit value.
@@ -84,8 +125,7 @@ 
 static inline uint16_t
 rte_constant_bswap16(uint16_t x)
 {
-	return (uint16_t)(((x & 0x00ffU) << 8) |
-		((x & 0xff00U) >> 8));
+	return RTE_STATIC_BSWAP16(x);
 }
 
 /*
@@ -97,10 +137,7 @@  rte_constant_bswap16(uint16_t x)
 static inline uint32_t
 rte_constant_bswap32(uint32_t x)
 {
-	return  ((x & 0x000000ffUL) << 24) |
-		((x & 0x0000ff00UL) << 8) |
-		((x & 0x00ff0000UL) >> 8) |
-		((x & 0xff000000UL) >> 24);
+	return RTE_STATIC_BSWAP32(x);
 }
 
 /*
@@ -112,14 +149,7 @@  rte_constant_bswap32(uint32_t x)
 static inline uint64_t
 rte_constant_bswap64(uint64_t x)
 {
-	return  ((x & 0x00000000000000ffULL) << 56) |
-		((x & 0x000000000000ff00ULL) << 40) |
-		((x & 0x0000000000ff0000ULL) << 24) |
-		((x & 0x00000000ff000000ULL) <<  8) |
-		((x & 0x000000ff00000000ULL) >>  8) |
-		((x & 0x0000ff0000000000ULL) >> 24) |
-		((x & 0x00ff000000000000ULL) >> 40) |
-		((x & 0xff00000000000000ULL) >> 56);
+	return RTE_STATIC_BSWAP64(x);
 }