diff mbox

[U-Boot,RFC,v2] ARM: Avoid compiler optimization for usages of readb, writeb and friends.

Message ID 20101222080206.A0CA3EA652A@gemini.denx.de
State RFC
Headers show

Commit Message

Wolfgang Denk Dec. 22, 2010, 8:02 a.m. UTC
Dear Alexander Holler,

In message <1292711230-3234-1-git-send-email-holler@ahsoftware.de> you wrote:
> gcc 4.5.1 seems to ignore (at least some) volatile definitions,
> avoid that as done in the kernel.
...
> +#define writeb(v,c)			({ __iowmb(); __arch_putb(v,c); })
> +#define writew(v,c)			({ __iowmb(); __arch_putw(v,c); })
> +#define writel(v,c)			({ __iowmb(); __arch_putl(v,c); })

http://www.codesourcery.com/archives/arm-gnu/msg03990.html  explains
why this construct is causing errors in cases where an additional read
from the address is unsupported.

Can you please try the following patch instead?

-------------------------------------------------------------------------

From 4672bbddaf8ce7e17a99ba737782cc527d46e5eb Mon Sep 17 00:00:00 2001
From: Alexander Holler <holler@ahsoftware.de>
Date: Sat, 18 Dec 2010 23:27:10 +0100
Subject: [PATCH] ARM: Avoid compiler optimization for readb, writeb and friends.

gcc 4.5.1 seems to ignore (at least some) volatile definitions,
avoid that as done in the kernel.

Reading C99 6.7.3 8 and the comment 114) there, I think it is a bug of that
gcc version to ignore the volatile type qualifier used e.g. in __arch_getl().
Anyway, using a definition as in the kernel headers avoids such optimizations when
gcc 4.5.1 is used.

Maybe the headers as used in the current linux-kernel should be used,
but to avoid large changes, I've just added a small change to the current headers.

Signed-off-by: Alexander Holler <holler@ahsoftware.de>
Signed-off-by: Wolfgang Denk <wd@denx.de>
---
 arch/arm/include/asm/io.h |   32 ++++++++++++++++++++------------
 1 files changed, 20 insertions(+), 12 deletions(-)

Comments

Alexander Holler Dec. 22, 2010, 11:23 a.m. UTC | #1
Hello,

Am 22.12.2010 09:02, schrieb Wolfgang Denk:

> In message<1292711230-3234-1-git-send-email-holler@ahsoftware.de>  you wrote:
>> gcc 4.5.1 seems to ignore (at least some) volatile definitions,
>> avoid that as done in the kernel.
> ...
>> +#define writeb(v,c)			({ __iowmb(); __arch_putb(v,c); })
>> +#define writew(v,c)			({ __iowmb(); __arch_putw(v,c); })
>> +#define writel(v,c)			({ __iowmb(); __arch_putl(v,c); })
>
> http://www.codesourcery.com/archives/arm-gnu/msg03990.html  explains
> why this construct is causing errors in cases where an additional read
> from the address is unsupported.
>
> Can you please try the following patch instead?

Indeed. Using do {} while (0) instead of that "GCC statement-expression" 
fixes the problem with the read after write.

I didn't know about that "GCC statement-expression" and my usage of 
({...}) was based on the writeb in the kernel-headers. But they (seem 
to) expand to something returning (void) which might avoid the problem.

Good that I've added the sentence that using the kernel-headers instead 
of that patch might be a better solution. But this might bring in much 
more changes, including real memory barriers. ;)

Anyway, now the master (including the GLOBAL...-patch) + patch v3 for 
read*/write* is good here. Just tested with both gcc 4.3.5 and gcc 4.5.1 
using binutils 2.20.1.

Regards,

Alexander
Dirk Behme Dec. 29, 2010, 9:40 a.m. UTC | #2
On 22.12.2010 09:02, Wolfgang Denk wrote:
> Dear Alexander Holler,
>
> In message<1292711230-3234-1-git-send-email-holler@ahsoftware.de>  you wrote:
>> gcc 4.5.1 seems to ignore (at least some) volatile definitions,
>> avoid that as done in the kernel.
> ...
>> +#define writeb(v,c)			({ __iowmb(); __arch_putb(v,c); })
>> +#define writew(v,c)			({ __iowmb(); __arch_putw(v,c); })
>> +#define writel(v,c)			({ __iowmb(); __arch_putl(v,c); })
>
> http://www.codesourcery.com/archives/arm-gnu/msg03990.html  explains
> why this construct is causing errors in cases where an additional read
> from the address is unsupported.

Just for the record:

The trick is to ensure that the __arch_putx() containing the volatile 
is not the last statement in the GCC statement-expression. So, using 
something like

#define writeb(v,c)         ({ __iowmb(); __arch_putb(v,c); v;})

(note the additional 'v;') should result in correct code, too.

The patches sent by Wolfgang and Alexander using

#define writeb(v,c)	do { __iowmb(); __arch_putb(v,c); } while (0)

do the same with a slightly different syntax, so these patches are 
fine, too.

Thanks

Dirk

> Can you please try the following patch instead?
>
> -------------------------------------------------------------------------
>
>> From 4672bbddaf8ce7e17a99ba737782cc527d46e5eb Mon Sep 17 00:00:00 2001
> From: Alexander Holler<holler@ahsoftware.de>
> Date: Sat, 18 Dec 2010 23:27:10 +0100
> Subject: [PATCH] ARM: Avoid compiler optimization for readb, writeb and friends.
>
> gcc 4.5.1 seems to ignore (at least some) volatile definitions,
> avoid that as done in the kernel.
>
> Reading C99 6.7.3 8 and the comment 114) there, I think it is a bug of that
> gcc version to ignore the volatile type qualifier used e.g. in __arch_getl().
> Anyway, using a definition as in the kernel headers avoids such optimizations when
> gcc 4.5.1 is used.
>
> Maybe the headers as used in the current linux-kernel should be used,
> but to avoid large changes, I've just added a small change to the current headers.
>
> Signed-off-by: Alexander Holler<holler@ahsoftware.de>
> Signed-off-by: Wolfgang Denk<wd@denx.de>
> ---
>   arch/arm/include/asm/io.h |   32 ++++++++++++++++++++------------
>   1 files changed, 20 insertions(+), 12 deletions(-)
>
> diff --git a/arch/arm/include/asm/io.h b/arch/arm/include/asm/io.h
> index ff1518e..647503a 100644
> --- a/arch/arm/include/asm/io.h
> +++ b/arch/arm/include/asm/io.h
> @@ -117,21 +117,29 @@ extern inline void __raw_readsl(unsigned int addr, void *data, int longlen)
>   		*buf++ = __arch_getl(addr);
>   }
>
> -#define __raw_writeb(v,a)		__arch_putb(v,a)
> -#define __raw_writew(v,a)		__arch_putw(v,a)
> -#define __raw_writel(v,a)		__arch_putl(v,a)
> +#define __raw_writeb(v,a)	__arch_putb(v,a)
> +#define __raw_writew(v,a)	__arch_putw(v,a)
> +#define __raw_writel(v,a)	__arch_putl(v,a)
>
> -#define __raw_readb(a)			__arch_getb(a)
> -#define __raw_readw(a)			__arch_getw(a)
> -#define __raw_readl(a)			__arch_getl(a)
> +#define __raw_readb(a)		__arch_getb(a)
> +#define __raw_readw(a)		__arch_getw(a)
> +#define __raw_readl(a)		__arch_getl(a)
>
> -#define writeb(v,a)			__arch_putb(v,a)
> -#define writew(v,a)			__arch_putw(v,a)
> -#define writel(v,a)			__arch_putl(v,a)
> +/*
> + * TODO: The kernel offers some more advanced versions of barriers, it might
> + * have some advantages to use them instead of the simple one here.
> + */
> +#define dmb()		__asm__ __volatile__ ("" : : : "memory")
> +#define __iormb()	dmb()
> +#define __iowmb()	dmb()
> +
> +#define writeb(v,c)	do { __iowmb(); __arch_putb(v,c); } while (0)
> +#define writew(v,c)	do { __iowmb(); __arch_putw(v,c); } while (0)
> +#define writel(v,c)	do { __iowmb(); __arch_putl(v,c); } while (0)
>
> -#define readb(a)			__arch_getb(a)
> -#define readw(a)			__arch_getw(a)
> -#define readl(a)			__arch_getl(a)
> +#define readb(c)	({ u8  __v = __arch_getb(c); __iormb(); __v; })
> +#define readw(c)	({ u16 __v = __arch_getw(c); __iormb(); __v; })
> +#define readl(c)	({ u32 __v = __arch_getl(c); __iormb(); __v; })
>
>   /*
>    * The compiler seems to be incapable of optimising constants
Alessandro Rubini Dec. 29, 2010, 11:10 p.m. UTC | #3
Dirk Behme:
> Just for the record:
> 
> The trick is to ensure that the __arch_putx() containing the volatile 
> is not the last statement in the GCC statement-expression. So, using 
> something like
> 
> #define writeb(v,c)         ({ __iowmb(); __arch_putb(v,c); v;})
> 
> (note the additional 'v;') should result in correct code, too.

Yes, that's good. Also "0" may work, and may be more readable, (or not,
according to who reads it).

> The patches sent by Wolfgang and Alexander using
> 
> #define writeb(v,c)	do { __iowmb(); __arch_putb(v,c); } while (0)
> 
> do the same with a slightly different syntax, so these patches are 
> fine, too.

It's not just different syntax, it's different semantics.

The ({...})  trick turns statements into expressions, while the "do
{...} while(0)" does not.  I'd better not forbid statements like

	while (reg = readb(addr), reg != VALUE) { .... }

or

	if (readb(addr) == VALUE) { ... } 

or 
	swtich (readb(addr)) { ... }
 
While I agree they may in general not be clean, I can forsee
meaningful uses. Moreover, I'd better allow expression-looking macros
to really behave like expressions -- otherwise error messages are quite
hard to understand for the unaquainted.

IMHO, the only reason to use "do {} while(0)" over statemente
expressions is being portable but in u-boot we are gcc-specific
anyways.

/alessandro
Dirk Behme Dec. 30, 2010, 10:39 a.m. UTC | #4
On 30.12.2010 00:10, Alessandro Rubini wrote:
> Dirk Behme:
>> Just for the record:
>>
>> The trick is to ensure that the __arch_putx() containing the volatile
>> is not the last statement in the GCC statement-expression. So, using
>> something like
>>
>> #define writeb(v,c)         ({ __iowmb(); __arch_putb(v,c); v;})
>>
>> (note the additional 'v;') should result in correct code, too.
>
> Yes, that's good. Also "0" may work, and may be more readable, (or not,
> according to who reads it).

Yes, indeed,

#define writeb(v,c)         ({ __iowmb(); __arch_putb(v,c); 0;})

seems to work, too :)

Thanks

Dirk

>> The patches sent by Wolfgang and Alexander using
>>
>> #define writeb(v,c)	do { __iowmb(); __arch_putb(v,c); } while (0)
>>
>> do the same with a slightly different syntax, so these patches are
>> fine, too.
>
> It's not just different syntax, it's different semantics.
>
> The ({...})  trick turns statements into expressions, while the "do
> {...} while(0)" does not.  I'd better not forbid statements like
>
> 	while (reg = readb(addr), reg != VALUE) { .... }
>
> or
>
> 	if (readb(addr) == VALUE) { ... }
>
> or
> 	swtich (readb(addr)) { ... }
>
> While I agree they may in general not be clean, I can forsee
> meaningful uses. Moreover, I'd better allow expression-looking macros
> to really behave like expressions -- otherwise error messages are quite
> hard to understand for the unaquainted.
>
> IMHO, the only reason to use "do {} while(0)" over statemente
> expressions is being portable but in u-boot we are gcc-specific
> anyways.
>
> /alessandro
>
Wolfgang Denk Jan. 9, 2011, 10:03 p.m. UTC | #5
Dear Alessandro Rubini,

In message <20101229231004.GA17999@mail.gnudd.com> you wrote:
>
> > #define writeb(v,c)         ({ __iowmb(); __arch_putb(v,c); v;})
> > 
> > (note the additional 'v;') should result in correct code, too.
> 
> Yes, that's good. Also "0" may work, and may be more readable, (or not,
> according to who reads it).

'0' looks wrong to me, as it changes behaviour.  Keep in mind that the
previous implemention usually looks like this:

	#define writeb(val,addr) (*(volatile unsigned char *)(addr)) = (val)

The value of this should be "val", not 0.


> > #define writeb(v,c)	do { __iowmb(); __arch_putb(v,c); } while (0)
> > 
> > do the same with a slightly different syntax, so these patches are 
> > fine, too.
> 
> It's not just different syntax, it's different semantics.

This is true.  Thanks for pointing out.

> The ({...})  trick turns statements into expressions, while the "do
> {...} while(0)" does not.  I'd better not forbid statements like
> 
> 	while (reg = readb(addr), reg != VALUE) { .... }
> 
> or
> 
> 	if (readb(addr) == VALUE) { ... } 
> 
> or 
> 	swtich (readb(addr)) { ... }

Here you are mixing things up.  There is no issue with the read*()
code, only the write*() code is affected.

And while no person ion a sane mind of state should use write*() in
such a context, I agree that for the sake of backward compatibility we
should change the code.  Patch follows.

> While I agree they may in general not be clean, I can forsee
> meaningful uses. Moreover, I'd better allow expression-looking macros
> to really behave like expressions -- otherwise error messages are quite
> hard to understand for the unaquainted.

Agreed.  But the write*() "functions" are considered to return void,
so there should not be any such issues.

> IMHO, the only reason to use "do {} while(0)" over statemente
> expressions is being portable but in u-boot we are gcc-specific
> anyways.

This is wrong interpretation, too; nothing in this construct is GCC
specific.

Best regards,

Wolfgang Denk
diff mbox

Patch

diff --git a/arch/arm/include/asm/io.h b/arch/arm/include/asm/io.h
index ff1518e..647503a 100644
--- a/arch/arm/include/asm/io.h
+++ b/arch/arm/include/asm/io.h
@@ -117,21 +117,29 @@  extern inline void __raw_readsl(unsigned int addr, void *data, int longlen)
 		*buf++ = __arch_getl(addr);
 }
 
-#define __raw_writeb(v,a)		__arch_putb(v,a)
-#define __raw_writew(v,a)		__arch_putw(v,a)
-#define __raw_writel(v,a)		__arch_putl(v,a)
+#define __raw_writeb(v,a)	__arch_putb(v,a)
+#define __raw_writew(v,a)	__arch_putw(v,a)
+#define __raw_writel(v,a)	__arch_putl(v,a)
 
-#define __raw_readb(a)			__arch_getb(a)
-#define __raw_readw(a)			__arch_getw(a)
-#define __raw_readl(a)			__arch_getl(a)
+#define __raw_readb(a)		__arch_getb(a)
+#define __raw_readw(a)		__arch_getw(a)
+#define __raw_readl(a)		__arch_getl(a)
 
-#define writeb(v,a)			__arch_putb(v,a)
-#define writew(v,a)			__arch_putw(v,a)
-#define writel(v,a)			__arch_putl(v,a)
+/*
+ * TODO: The kernel offers some more advanced versions of barriers, it might
+ * have some advantages to use them instead of the simple one here.
+ */
+#define dmb()		__asm__ __volatile__ ("" : : : "memory")
+#define __iormb()	dmb()
+#define __iowmb()	dmb()
+
+#define writeb(v,c)	do { __iowmb(); __arch_putb(v,c); } while (0)
+#define writew(v,c)	do { __iowmb(); __arch_putw(v,c); } while (0)
+#define writel(v,c)	do { __iowmb(); __arch_putl(v,c); } while (0)
 
-#define readb(a)			__arch_getb(a)
-#define readw(a)			__arch_getw(a)
-#define readl(a)			__arch_getl(a)
+#define readb(c)	({ u8  __v = __arch_getb(c); __iormb(); __v; })
+#define readw(c)	({ u16 __v = __arch_getw(c); __iormb(); __v; })
+#define readl(c)	({ u32 __v = __arch_getl(c); __iormb(); __v; })
 
 /*
  * The compiler seems to be incapable of optimising constants