Hi David,

that is an excellent idea. The attached patch implements that idea (slightly
different) and almost makes clang happy. It know fails with:

> /usr/avr/include/avr/wdt.h:436:5: error: invalid operand for instruction
>                                 "in __tmp_reg__,__SREG__" "\n\t"
>                                 ^
> <inline asm>:1:5: note: instantiated into assembly here
>         in __tmp_reg__,__SREG__
>            ^

So I guess for my application I know only have to teach clang the value of
__tmp_reg__ (and maybe also a few other constants) to get it compiling :-)

Kind regards,
Marian

On Fri, 15 Oct 2021 09:49:30 +0200
David Brown <da...@westcontrol.com> wrote:

> On 13/10/2021 18:05, Marian Buschsieweke wrote:
> > Hi together,
> > 
> > parts of the avrlibc headers are not compatible with clang. (The use case is
> > not to compile avrlibc itself with clang, but rather an application using
> > a vanilla GCC compiled avrlibc.) However, the issues are not always trivial 
> > to
> > fix.
> > 
> > E.g. in avr/wdt.h there are several instances of code like this:
> >   
> >> static __inline__
> >> __attribute__ ((__always_inline__))
> >> void wdt_enable (const uint8_t value)
> >> {
> >>    if (_SFR_IO_REG_P (_WD_CONTROL_REG))
> >>    {
> >>            __asm__ __volatile__ (
> >>                            [...]
> >>                            : /* no outputs */
> >>                            : "I" (_SFR_IO_ADDR(_WD_CONTROL_REG)),
> >>                            [...]
> >>                            : "r0"
> >>            );
> >>    }
> >>    else {
> >>            /* variant not using _WD_CONTROL_REG as immediate */
> >>            [...]       
> >>       
> >> }  
>  [...]  
> 
> It's been a /long/ time since I have done anything with the AVR, and I
> don't have a modern avr gcc compiler (or any clang avr compiler at all)
> handy for testing.  However, I have an idea you can try:
> 
>       "I" (_SFR_IO_ADDR(_WD_CONTROL_REG) || 1)
> 
> If "_SFR_IO_ADDR(_WD_CONTROL_REG)" evaluates to false, then this
> expression would give "1", which clang might be happier with.
> 

diff --git a/trunk/avr-libc/include/avr/interrupt.h b/trunk/avr-libc/include/avr/interrupt.h
index a36e2ba6..844289df 100644
--- a/trunk/avr-libc/include/avr/interrupt.h
+++ b/trunk/avr-libc/include/avr/interrupt.h
@@ -125,9 +125,9 @@
 #  define ISR(vector, [attributes])
 #else  /* real code */
 
-#if (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || (__GNUC__ > 4)
+#if ((__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || (__GNUC__ > 4)) && !defined(__clang__)
 #  define __INTR_ATTRS __used__, __externally_visible__
-#else /* GCC < 4.1 */
+#else /* GCC < 4.1 or clang */
 #  define __INTR_ATTRS __used__
 #endif
 
diff --git a/trunk/avr-libc/include/avr/wdt.h b/trunk/avr-libc/include/avr/wdt.h
index 73517e3e..8d6253bf 100644
--- a/trunk/avr-libc/include/avr/wdt.h
+++ b/trunk/avr-libc/include/avr/wdt.h
@@ -41,6 +41,13 @@
 #include <avr/io.h>
 #include <stdint.h>
 
+/* Workaround for clang: This gives the I/O address of the given register if it can be used
+ * as immediate value, otherwise an incorrect address is returned that does fulfill the constraint
+ * "I" but must not be used. The programmer has to make sure that inline assembly using this macro
+ * is only executed, if _SFR_IO_REG_P(x) is true
+ */
+#define _SFR_IO_ADDR_WORKAROUND(x) (_SFR_IO_ADDR(x) & 0x3f)
+
 /** \file */
 /** \defgroup avr_watchdog <avr/wdt.h>: Watchdog timer handling
     \code #include <avr/wdt.h> \endcode
@@ -266,7 +273,7 @@ void wdt_enable (const uint8_t value)
 			: /* no outputs */
 			: [CCPADDRESS] "n" (_SFR_MEM_ADDR(CCP)),
 			[SIGNATURE] "r" ((uint8_t)0xD8),
-			[WDTREG] "I" (_SFR_IO_ADDR(_WD_CONTROL_REG)),
+			[WDTREG] "I" (_SFR_IO_ADDR_WORKAROUND(_WD_CONTROL_REG)),
 			[WDVALUE] "r" ((uint8_t)((value & 0x08 ? _WD_PS3_MASK : 0x00)
 				| _BV(WDE) | (value & 0x07) ))
 			: "r0"
@@ -282,7 +289,7 @@ void wdt_enable (const uint8_t value)
 			"sts %[WDTREG],%[WDVALUE]" "\n\t"
 			"out __SREG__,__tmp_reg__" "\n\t"
 			: /* no outputs */
-			: [CCPADDRESS] "I" (_SFR_IO_ADDR(CCP)),
+			: [CCPADDRESS] "I" (_SFR_IO_ADDR_WORKAROUND(CCP)),
 			[SIGNATURE] "r" ((uint8_t)0xD8),
 			[WDTREG] "n" (_SFR_MEM_ADDR(_WD_CONTROL_REG)),
 			[WDVALUE] "r" ((uint8_t)((value & 0x08 ? _WD_PS3_MASK : 0x00)
@@ -300,9 +307,9 @@ void wdt_enable (const uint8_t value)
 			"out %[WDTREG],%[WDVALUE]" "\n\t"
 			"out __SREG__,__tmp_reg__" "\n\t"
 			: /* no outputs */
-			: [CCPADDRESS] "I" (_SFR_IO_ADDR(CCP)),
+			: [CCPADDRESS] "I" (_SFR_IO_ADDR_WORKAROUND(CCP)),
 			[SIGNATURE] "r" ((uint8_t)0xD8),
-			[WDTREG] "I" (_SFR_IO_ADDR(_WD_CONTROL_REG)),
+			[WDTREG] "I" (_SFR_IO_ADDR_WORKAROUND(_WD_CONTROL_REG)),
 			[WDVALUE] "r" ((uint8_t)((value & 0x08 ? _WD_PS3_MASK : 0x00)
 				| _BV(WDE) | (value & 0x07) ))
 			: "r0"
@@ -350,7 +357,7 @@ void wdt_disable (void)
 				: /*no output */
 				: [CCPADDRESS] "n" (_SFR_MEM_ADDR(CCP)),
 				[SIGNATURE] "r" ((uint8_t)0xD8),
-				[WDTREG] "I" (_SFR_IO_ADDR(_WD_CONTROL_REG)),
+				[WDTREG] "I" (_SFR_IO_ADDR_WORKAROUND(_WD_CONTROL_REG)),
 				[TEMP_WD] "d" (temp_wd),
 				[WDVALUE] "n" (1 << WDE)
 				: "r0"
@@ -369,7 +376,7 @@ void wdt_disable (void)
 				"sts %[WDTREG],%[TEMP_WD]" "\n\t"
 				"out __SREG__,__tmp_reg__" "\n\t"
 				: /*no output */
-				: [CCPADDRESS] "I" (_SFR_IO_ADDR(CCP)),
+				: [CCPADDRESS] "I" (_SFR_IO_ADDR_WORKAROUND(CCP)),
 				[SIGNATURE] "r" ((uint8_t)0xD8),
 				[WDTREG] "n" (_SFR_MEM_ADDR(_WD_CONTROL_REG)),
 				[TEMP_WD] "d" (temp_wd),
@@ -390,9 +397,9 @@ void wdt_disable (void)
 				"out %[WDTREG],%[TEMP_WD]" "\n\t"
 				"out __SREG__,__tmp_reg__" "\n\t"
 				: /*no output */
-				: [CCPADDRESS] "I" (_SFR_IO_ADDR(CCP)),
+				: [CCPADDRESS] "I" (_SFR_IO_ADDR_WORKAROUND(CCP)),
 				[SIGNATURE] "r" ((uint8_t)0xD8),
-				[WDTREG] "I" (_SFR_IO_ADDR(_WD_CONTROL_REG)),
+				[WDTREG] "I" (_SFR_IO_ADDR_WORKAROUND(_WD_CONTROL_REG)),
 				[TEMP_WD] "d" (temp_wd),
 				[WDVALUE] "n" (1 << WDE)
 				: "r0"
@@ -416,7 +423,7 @@ void wdt_enable (const uint8_t value)
 				"out __SREG__,__tmp_reg__" "\n\t"
 				"out %0, %2" "\n \t"
 				: /* no outputs */
-				: "I" (_SFR_IO_ADDR(_WD_CONTROL_REG)),
+				: "I" (_SFR_IO_ADDR_WORKAROUND(_WD_CONTROL_REG)),
 				"r" ((uint8_t)(_BV(_WD_CHANGE_BIT) | _BV(WDE))),
 				"r" ((uint8_t) ((value & 0x08 ? _WD_PS3_MASK : 0x00) |
 						_BV(WDE) | (value & 0x07)) )
@@ -459,7 +466,7 @@ void wdt_disable (void)
 				"out %[WDTREG],__zero_reg__" "\n\t"
 				"out __SREG__,__tmp_reg__"   "\n\t"
 				: [TEMPREG] "=d" (temp_reg)
-				: [WDTREG]  "I"  (_SFR_IO_ADDR(_WD_CONTROL_REG)),
+				: [WDTREG]  "I"  (_SFR_IO_ADDR_WORKAROUND(_WD_CONTROL_REG)),
 				[WDCE_WDE]  "n"  ((uint8_t)(_BV(_WD_CHANGE_BIT) | _BV(WDE)))
 				: "r0"
 		);

Attachment: pgpfDkR1_UQOS.pgp
Description: OpenPGP digital signature

Reply via email to