Subject: [patch] <asm/io.h> troubles - [[email-masked]: Inlining bug on MIPS - both 2.95.3 and 3.0 branches]

From: Daniel Jacobowitz <[email-masked]>
Date: Wed, 14 Mar 2001 08:57:37 -0500

I submitted the following GCC bug report a couple of days ago, and had
a discussion with Geoff Keating about asm constraints.  It looks as if
the problem I was seeing is a (very) longstanding bug in GCC's reload
pass, and is very unlikely to get fixed.

How does this workaround patch look?  I changed the constant-using
functions from io.h into macros.  I'm not thrilled by it, but it does
seem to be correct.  I have a suspicion we could change the confusing
"i#*X" constraints back to simply "i" - they were part of my effort to
make the inline functions work, before I was defeated by reload.  It
should be correct either way, though. 

----- Forwarded message from Daniel Jacobowitz <[email-masked]> -----

Date: Fri, 9 Mar 2001 18:10:02 -0500
From: Daniel Jacobowitz <[email-masked]>
Subject: Inlining bug on MIPS - both 2.95.3 and 3.0 branches
To: [email-masked]
Mail-Followup-To: [email-masked]

I've put together a testcase for a bug exposed by (accidentally) trying to
build a mipsel-linux kernel with VGA console enabled.

The problem seems to be that we lift a constant term out of a loop into a
spare register.  Of course, __builtin_constant_p is still true for it, so in
the original case we chose to use an inline function which required that
argument to be constant.  We inline the function using the scratch register
instead of the constant, and we lose.

The inline function seems to me to be doing something dodgy - it specifies
the operand with the constraint "ir", implying that a register would be
acceptable.  We're lying to the compiler, so it's not that startling that it
bites us.  On the other hand, there's no need to waste a register on this,
so I'm not sure why the constant gets stored in a temporary.

Is the invalid result a compiler bug?  I'd say that there is at least a
small optimization bug here, but the "ir" constraint might mean that the
compiler's doing everything as best it can.  There doesn't seem to be a way
to use an inline function safely and specify a constant constraint on one
argument to the function - does this need to be a macro?

Dan

----- End forwarded message -----

-- 
Daniel Jacobowitz                           Debian GNU/Linux Developer
Monta Vista Software                              Debian Security Team
                         "I am croutons!"
Version 1.64  (Mon Mar 12 2001 drow <[email-masked]>)
    Use macros instead of inline functions for constant io macros, and
    change "ir" to "i#*X" on Geoff's advice.

# This is a BitKeeper generated patch for the following project:
# Project Name: HHL 2.4.2 kernel sources, based on linux-2.4.2
# This patch format is intended for GNU patch command version 2.5 or higher.
# This patch includes the following deltas:
#	linux/include/asm-mips/io.h	1.2     -> 1.3    
diff -Nru a/linux/include/asm-mips/io.h b/linux/include/asm-mips/io.h
--- a/linux/include/asm-mips/io.h	Mon Mar 12 16:52:50 2001
+++ b/linux/include/asm-mips/io.h	Mon Mar 12 16:52:50 2001
@@ -248,12 +248,21 @@
 
 #define __OUT(m,s,w) \
 __OUT1(s) __OUT2(m) : : "r" (__ioswab##w(value)), "i" (0), "r" (mips_io_port_base+port)); } \
-__OUT1(s##c) __OUT2(m) : : "r" (__ioswab##w(value)), "ir" (port), "r" (mips_io_port_base)); } \
 __OUT1(s##_p) __OUT2(m) : : "r" (__ioswab##w(value)), "i" (0), "r" (mips_io_port_base+port)); \
-	SLOW_DOWN_IO; } \
-__OUT1(s##c_p) __OUT2(m) : : "r" (__ioswab##w(value)), "ir" (port), "r" (mips_io_port_base)); \
 	SLOW_DOWN_IO; }
 
+#define __OUTMAC(m,w,value,port) ({ __OUT2(m) : : "r" (__ioswab##w(value)), "i#*X" (port), \
+		"r" (mips_io_port_base)); })
+#define __OUTMAC_P(m,w,value,port) ({ __OUT2(m) : : "r" (__ioswab##w(value)), "i#*X" (port), \
+		"r" (mips_io_port_base)); SLOW_DOWN_IO; })
+
+#define __outbc(value,port) __OUTMAC(b,8,value,port)
+#define __outwc(value,port) __OUTMAC(h,16,value,port)
+#define __outlc(value,port) __OUTMAC(w,32,value,port)
+#define __outbc_p(value,port) __OUTMAC_P(b,8,value,port)
+#define __outwc_p(value,port) __OUTMAC_P(h,16,value,port)
+#define __outlc_p(value,port) __OUTMAC_P(w,32,value,port)
+
 #define __IN1(t,s) \
 extern __inline__ t __in##s(unsigned int port) { t _v;
 
@@ -265,14 +274,24 @@
 
 #define __IN(t,m,s,w) \
 __IN1(t,s) __IN2(m) : "=r" (_v) : "i" (0), "r" (mips_io_port_base+port)); return __ioswab##w(_v); } \
-__IN1(t,s##c) __IN2(m) : "=r" (_v) : "ir" (port), "r" (mips_io_port_base)); return __ioswab##w(_v); } \
-__IN1(t,s##_p) __IN2(m) : "=r" (_v) : "i" (0), "r" (mips_io_port_base+port)); SLOW_DOWN_IO; return __ioswab##w(_v); } \
-__IN1(t,s##c_p) __IN2(m) : "=r" (_v) : "ir" (port), "r" (mips_io_port_base)); SLOW_DOWN_IO; return __ioswab##w(_v); }
+__IN1(t,s##_p) __IN2(m) : "=r" (_v) : "i" (0), "r" (mips_io_port_base+port)); SLOW_DOWN_IO; return __ioswab##w(_v); }
+
+#define __INMAC(t,m,w,port) ({ t _v; __IN2(m) : "=r" (_v) : "i#*X" (port), \
+		"r" (mips_io_port_base)); __ioswab##w(_v); })
+#define __INMAC_P(t,m,w,port) ({ t _v; __IN2(m) : "=r" (_v) : "i#*X" (port), \
+		"r" (mips_io_port_base)); SLOW_DOWN_IO; __ioswab##w(_v); })
+
+#define __inbc(port) __INMAC(unsigned char,b,8,port)
+#define __inwc(port) __INMAC(unsigned short,h,16,port)
+#define __inlc(port) __INMAC(unsigned int,w,32,port)
+#define __inbc_p(port) __INMAC_P(unsigned char,b,8,port)
+#define __inwc_p(port) __INMAC_P(unsigned short,h,16,port)
+#define __inlc_p(port) __INMAC_P(unsigned int,w,32,port)
 
 #define __INS1(s) \
 extern inline void __ins##s(unsigned int port, void * addr, unsigned long count) {
 
-#define __INS2(m) \
+#define __INS2(m,count) \
 if (count) \
 __asm__ __volatile__ ( \
 	".set\tnoreorder\n\t" \
@@ -286,21 +305,26 @@
 	".set\treorder"
 
 #define __INS(m,s,i) \
-__INS1(s) __INS2(m) \
+__INS1(s) __INS2(m,count) \
 	: "=r" (addr), "=r" (count) \
 	: "0" (addr), "1" (count), "i" (0), \
 	  "r" (mips_io_port_base+port), "I" (i) \
-	: "$1");} \
-__INS1(s##c) __INS2(m) \
-	: "=r" (addr), "=r" (count) \
-	: "0" (addr), "1" (count), "ir" (port), \
-	  "r" (mips_io_port_base), "I" (i) \
 	: "$1");}
 
+#define __INSMAC(m,i,port,addr,count) ({ void *_a = (addr); unsigned long _c = (count); \
+	__INS2(m,_c) \
+	: "=r" (_a), "=r" (_c) \
+	: "0" (_a), "1" (_c), "i#*X" (port), \
+	  "r" (mips_io_port_base), "I" (i) \
+	: "$1"); })
+#define __insbc(port,addr,count) __INSMAC(b,1,port,addr,count)
+#define __inswc(port,addr,count) __INSMAC(h,2,port,addr,count)
+#define __inslc(port,addr,count) __INSMAC(w,4,port,addr,count)
+
 #define __OUTS1(s) \
 extern inline void __outs##s(unsigned int port, const void * addr, unsigned long count) {
 
-#define __OUTS2(m) \
+#define __OUTS2(m,count) \
 if (count) \
 __asm__ __volatile__ ( \
         ".set\tnoreorder\n\t" \
@@ -314,14 +338,19 @@
         ".set\treorder"
 
 #define __OUTS(m,s,i) \
-__OUTS1(s) __OUTS2(m) \
+__OUTS1(s) __OUTS2(m,count) \
 	: "=r" (addr), "=r" (count) \
 	: "0" (addr), "1" (count), "i" (0), "r" (mips_io_port_base+port), "I" (i) \
-	: "$1");} \
-__OUTS1(s##c) __OUTS2(m) \
-	: "=r" (addr), "=r" (count) \
-	: "0" (addr), "1" (count), "ir" (port), "r" (mips_io_port_base), "I" (i) \
 	: "$1");}
+
+#define __OUTSMAC(m,i,port,addr,count) ({ void *_a = (addr); unsigned long _c = (count); \
+	__OUTS2(m,_c) \
+	: "=r" (_a), "=r" (_c) \
+	: "0" (_a), "1" (_c), "i#*X" (port), "r" (mips_io_port_base), "I" (i) \
+	: "$1"); })
+#define __outsbc(port,addr,count) __OUTSMAC(b,1,port,addr,count)
+#define __outswc(port,addr,count) __OUTSMAC(h,2,port,addr,count)
+#define __outslc(port,addr,count) __OUTSMAC(w,4,port,addr,count)
 
 __IN(unsigned char,b,b,8)
 __IN(unsigned short,h,w,16)