Welcome, Guest. Please login or register.

Author Topic: GCC asm() warning suppression options?  (Read 19991 times)

Description:

0 Members and 1 Guest are viewing this topic.

Offline KarlosTopic starter

  • Sockologist
  • Global Moderator
  • Hero Member
  • *****
  • Join Date: Nov 2002
  • Posts: 16878
  • Country: gb
  • Thanked: 5 times
    • Show all replies
GCC asm() warning suppression options?
« on: June 27, 2009, 10:58:40 PM »
Hi,

I've pulled out my dusty coding hat and started reviewing years old code I was working on. I have a set of functions that perform bitwise rotate operations using various combinations of ROR/ROL/SWAP:

Code: [Select]

namespace Machine {

  // ...

  template<const uint32 N> inline uint32 rotRight32(uint32 val)
  {
    // N is shift size
    if (!(N&31)) {
      return val;
    }
    if (N<9) {
      asm(&quot;ror.l %1, %0&quot; : &quot;=d&quot;(val) : &quot;I&quot;(N), &quot;0&quot;(val) : &quot;cc&quot;);
    }
    else if (N==16) {
      asm(&quot;swap %0&quot; : &quot;=d&quot;(val) : &quot;0&quot;(val) : &quot;cc&quot;);
    }
    else if (N>23) {
      asm(&quot;rol.l %1, %0&quot; : &quot;=d&quot;(val) : &quot;I&quot;(32-N), &quot;0&quot;(val) : &quot;cc&quot;);
    }
    else {
      asm(&quot;ror.l %1, %0&quot; : &quot;=d&quot;(val) : &quot;d&quot;(N), &quot;0&quot;(val) : &quot;cc&quot;);
    }
    return val;
  }

  // ...

}



Now, without getting too much into it, the above template function is designed to emit optimal rotate code for compile-time known shift values. Despite the seemingly large chain of if/else, the fact the code is inline and N is constant means the basic assembler is emitted fine, for all values of N from 0-32. It goes without saying that the behaviour for N>32 is not well defined (actually it is the last branch of the code but that might not be the expected behaviour for all N), but as N is a compile time constant, that doesn't matter. If you need a variable rotate, there is a different non-template function.

Unfortunately, you do get a lot of warning diagnostics of the form "asm operand 0 probably doesn't match constraints" when compiling with gcc.

Does anybody know what option there is to suppress this particular warning or just warnings emitted from the use of asm() in general?
int p; // A
 

Offline KarlosTopic starter

  • Sockologist
  • Global Moderator
  • Hero Member
  • *****
  • Join Date: Nov 2002
  • Posts: 16878
  • Country: gb
  • Thanked: 5 times
    • Show all replies
Re: GCC asm() warning suppression options?
« Reply #1 on: June 28, 2009, 12:22:50 AM »
Quote from: Trev;513629
Is the warning thrown for all values of N or just values outside the range (0..31) of the I constraint? Can you use N%32? EDIT: A value outside 0..31 might throw "error: impossible constraint in `asm'" in addition to the warning, depending on your version of GCC.

I don't know if that particular warning can be suppressed. Does it show up when optimizations are disabled?


All legal values of N produce the warning, impossible values (>32) produce an error from the assembler stage. I have tried limiting N in the code by changing all instances to (N&31) (you can't do N &= 31 first since a constant literal isn't a legal lvalue, of course).

Turning off optimisations would somewhat defeat the purpose of this code. Dead code elimination at the very least is required to ensure the inlining only emits the one branch of the code.
int p; // A
 

Offline KarlosTopic starter

  • Sockologist
  • Global Moderator
  • Hero Member
  • *****
  • Join Date: Nov 2002
  • Posts: 16878
  • Country: gb
  • Thanked: 5 times
    • Show all replies
Re: GCC asm() warning suppression options?
« Reply #2 on: June 28, 2009, 12:31:51 AM »
Interestingly, at -O0, the template code doesn't compile at all :lol:
int p; // A
 

Offline KarlosTopic starter

  • Sockologist
  • Global Moderator
  • Hero Member
  • *****
  • Join Date: Nov 2002
  • Posts: 16878
  • Country: gb
  • Thanked: 5 times
    • Show all replies
Re: GCC asm() warning suppression options?
« Reply #3 on: June 28, 2009, 12:36:25 AM »
Quote from: Trev;513633
I was just thinking GCC might have a bug in the parser when optimizations are enabled. It's been noted on x86 and Alpha. What does the preproccesed output look like for each possible branch?

for the following test code
Code: [Select]
 uint32 x = 0xAABBCCDD, y=0;

  y = Machine::rotLeft32<0>(x);
  std::printf(&quot;%08X -> %08X\n&quot;, (unsigned)x, (unsigned)y);
  y = Machine::rotLeft32<4>(x);
  std::printf(&quot;%08X -> %08X\n&quot;, (unsigned)x, (unsigned)y);
  y = Machine::rotLeft32<8>(x);
  std::printf(&quot;%08X -> %08X\n&quot;, (unsigned)x, (unsigned)y);
  y = Machine::rotLeft32<12>(x);
  std::printf(&quot;%08X -> %08X\n&quot;, (unsigned)x, (unsigned)y);
  y = Machine::rotLeft32<16>(x);
  std::printf(&quot;%08X -> %08X\n&quot;, (unsigned)x, (unsigned)y);
  y = Machine::rotLeft32<20>(x);
  std::printf(&quot;%08X -> %08X\n&quot;, (unsigned)x, (unsigned)y);
  y = Machine::rotLeft32<24>(x);
  std::printf(&quot;%08X -> %08X\n&quot;, (unsigned)x, (unsigned)y);
  y = Machine::rotLeft32<28>(x);
  std::printf(&quot;%08X -> %08X\n&quot;, (unsigned)x, (unsigned)y);
  y = Machine::rotLeft32<32>(x);
  std::printf(&quot;%08X -> %08X\n&quot;, (unsigned)x, (unsigned)y);
  y = Machine::rotRight32<0>(x);
  std::printf(&quot;%08X -> %08X\n&quot;, (unsigned)x, (unsigned)y);
  y = Machine::rotRight32<4>(x);
  std::printf(&quot;%08X -> %08X\n&quot;, (unsigned)x, (unsigned)y);
  y = Machine::rotRight32<8>(x);
  std::printf(&quot;%08X -> %08X\n&quot;, (unsigned)x, (unsigned)y);
  y = Machine::rotRight32<12>(x);
  std::printf(&quot;%08X -> %08X\n&quot;, (unsigned)x, (unsigned)y);
  y = Machine::rotRight32<16>(x);
  std::printf(&quot;%08X -> %08X\n&quot;, (unsigned)x, (unsigned)y);
  y = Machine::rotRight32<20>(x);
  std::printf(&quot;%08X -> %08X\n&quot;, (unsigned)x, (unsigned)y);
  y = Machine::rotRight32<24>(x);
  std::printf(&quot;%08X -> %08X\n&quot;, (unsigned)x, (unsigned)y);
  y = Machine::rotRight32<28>(x);
  std::printf(&quot;%08X -> %08X\n&quot;, (unsigned)x, (unsigned)y);
  y = Machine::rotRight32<32>(x);
  std::printf(&quot;%08X -> %08X\n&quot;, (unsigned)x, (unsigned)y);

I get (at -O1 - the least level it works at)
Code: [Select]
movel #-1430532899,d4
[B] movel d4,sp@- <-   y = Machine::rotLeft32<0>(x);[/b]
movel d4,sp@-
pea LC5
lea _printf,a2
jbsr a2@
addw #12,sp
[B] movel d4,d0 <-   y = Machine::rotLeft32<4>(x);
#APP
rol.l #4, d0
#NO_APP[/B]
movel d0,sp@-
movel d4,sp@-
pea LC5
jbsr a2@
addw #12,sp
[B] movel d4,d0 <-   y = Machine::rotLeft32<8>(x);
#APP
rol.l #8, d0
#NO_APP[/B]
movel d0,sp@-
movel d4,sp@-
pea LC5
jbsr a2@
addw #12,sp
[B] moveq #12,d3 <-   y = Machine::rotLeft32<12>(x);
movel d4,d0
#APP
rol.l d3, d0
#NO_APP[/B]
movel d0,sp@-
movel d4,sp@-
pea LC5
jbsr a2@
addw #12,sp
[B] movel d4,d0 <-   y = Machine::rotLeft32<16>(x);
#APP
swap d0
#NO_APP[/B]
movel d0,sp@-
movel d4,sp@-
pea LC5
jbsr a2@
addw #12,sp
[B] moveq #20,d2 <-   y = Machine::rotLeft32<20>(x);
movel d4,d0
#APP
rol.l d2, d0
#NO_APP[/B]
movel d0,sp@-
movel d4,sp@-
pea LC5
jbsr a2@
addw #12,sp
[B] movel d4,d0 <-   y = Machine::rotLeft32<24>(x);
#APP
ror.l #8, d0
#NO_APP[/B]
movel d0,sp@-
movel d4,sp@-
pea LC5
jbsr a2@
addw #12,sp
[B] movel d4,d0 <-   y = Machine::rotLeft32<28>(x);
#APP
ror.l #4, d0
#NO_APP[/B]
movel d0,sp@-
movel d4,sp@-
pea LC5
jbsr a2@
addqw #8,sp
[B] movel d4,sp@ <-   y = Machine::rotLeft32<32>(x);[/b]
movel d4,sp@-
pea LC5
jbsr a2@
addqw #8,sp
[B] movel d4,sp@ <-   y = Machine::rotRight32<0>(x); [/b]
movel d4,sp@-
pea LC5
jbsr a2@
addw #12,sp
[B] movel d4,d0 <- y = Machine::rotRight32<4>(x);
#APP
ror.l #4, d0
#NO_APP[/B]
movel d0,sp@-
movel d4,sp@-
pea LC5
jbsr a2@
addw #12,sp
[B] movel d4,d0 <- y = Machine::rotRight32<8>(x);
#APP
ror.l #8, d0
#NO_APP[/B]
movel d0,sp@-
movel d4,sp@-
pea LC5
jbsr a2@
addw #12,sp
[B] movel d4,d0 <- y = Machine::rotRight32<12>(x);
#APP
ror.l d3, d0
#NO_APP[/B]
movel d0,sp@-
movel d4,sp@-
pea LC5
jbsr a2@
addw #12,sp
[B] movel d4,d0 <- y = Machine::rotRight32<16>(x);
#APP
swap d0
#NO_APP[/B]
movel d0,sp@-
movel d4,sp@-
pea LC5
jbsr a2@
addw #12,sp
[B] movel d4,d0 <- y = Machine::rotRight32<20>(x);
#APP
ror.l d2, d0
#NO_APP[/B]
movel d0,sp@-
movel d4,sp@-
pea LC5
jbsr a2@
addw #12,sp
[B] movel d4,d0 <- y = Machine::rotRight32<24>(x);
#APP
rol.l #8, d0
#NO_APP[/B]
movel d0,sp@-
movel d4,sp@-
pea LC5
jbsr a2@
addw #12,sp
[B] movel d4,d0 <- y = Machine::rotRight32<28>(x);
#APP
rol.l #4, d0
#NO_APP[/B]
movel d0,sp@-
movel d4,sp@-
pea LC5
jbsr a2@
addqw #8,sp
[B] movel d4,sp@ <- y = Machine::rotRight32<32>(x);[/b]
movel d4,sp@-
pea LC5
jbsr a2@
addw #12,sp
« Last Edit: June 28, 2009, 12:52:15 AM by Karlos »
int p; // A
 

Offline KarlosTopic starter

  • Sockologist
  • Global Moderator
  • Hero Member
  • *****
  • Join Date: Nov 2002
  • Posts: 16878
  • Country: gb
  • Thanked: 5 times
    • Show all replies
Re: GCC asm() warning suppression options?
« Reply #4 on: June 28, 2009, 12:50:58 AM »
I should point out it's always bugged me that C never had operators for integer rotate :lol:

It's pretty obvious that gcc is fretting because the value for N is clearly capable of exceeding the limit for the I (immediate) constraint, though the template code is designed to minimize that. If N is outside the range, this is considered a programming error and I'm happy for it to fail compilation.
« Last Edit: June 28, 2009, 12:57:53 AM by Karlos »
int p; // A
 

Offline KarlosTopic starter

  • Sockologist
  • Global Moderator
  • Hero Member
  • *****
  • Join Date: Nov 2002
  • Posts: 16878
  • Country: gb
  • Thanked: 5 times
    • Show all replies
Re: GCC asm() warning suppression options?
« Reply #5 on: June 28, 2009, 01:00:04 AM »
Quote from: Trev;513640
Shouldn't the bold line be the same as the N<9 branch, or is your intent to use a data register if N<9 || N>23? If so, don't you need to move.l the value first?

Yes, register use is intended for that final operation (which is not for N<9 | N>23 but for the intermediate ranges between 8 and 16 bit either direction) since it's probably faster than a pair of rotates. Moving the value to the register is performed automatically by the compiler thanks to the "d" constraint. See the output asm in my earlier post. The N = 9-15 and N = 17-23 are handled by a ROX.l dX, DY type operation, everything else is done with immediate rotates (or a swap in the N=16 case)
« Last Edit: June 28, 2009, 01:03:59 AM by Karlos »
int p; // A
 

Offline KarlosTopic starter

  • Sockologist
  • Global Moderator
  • Hero Member
  • *****
  • Join Date: Nov 2002
  • Posts: 16878
  • Country: gb
  • Thanked: 5 times
    • Show all replies
Re: GCC asm() warning suppression options?
« Reply #6 on: June 28, 2009, 01:10:00 AM »
Quote from: Trev;513642
Yeah, I think it's a common gripe. A lot of compilers implement rotation as intrinsic functions.



Good call.

Anyway, to cut the story short, as a construct it works just fine (save the -O0 case which needs a spot of investigation, not that I ever use -O0, even when debugging):

AABBCCDD -> AABBCCDD
AABBCCDD -> ABBCCDDA
AABBCCDD -> BBCCDDAA
AABBCCDD -> BCCDDAAB
AABBCCDD -> CCDDAABB
AABBCCDD -> CDDAABBC
AABBCCDD -> DDAABBCC
AABBCCDD -> DAABBCCD
AABBCCDD -> AABBCCDD

AABBCCDD -> AABBCCDD
AABBCCDD -> DAABBCCD
AABBCCDD -> DDAABBCC
AABBCCDD -> CDDAABBC
AABBCCDD -> CCDDAABB
AABBCCDD -> BCCDDAAB
AABBCCDD -> BBCCDDAA
AABBCCDD -> ABBCCDDA
AABBCCDD -> AABBCCDD


...but the warnings are a PITA. As the code is in a header and not a translation unit, it isn't as if I can just disable warnings for the one file.
« Last Edit: June 28, 2009, 01:13:25 AM by Karlos »
int p; // A
 

Offline KarlosTopic starter

  • Sockologist
  • Global Moderator
  • Hero Member
  • *****
  • Join Date: Nov 2002
  • Posts: 16878
  • Country: gb
  • Thanked: 5 times
    • Show all replies
Re: GCC asm() warning suppression options?
« Reply #7 on: June 28, 2009, 01:24:45 AM »
Quote from: Trev;513644
Which version of gcc are you using? Native or cross?


Native and pretty ancient (2.95.x)

Quote
You could also specialize your templates a bit to perhaps make things a bit clearer


Yeah, that certainly looks better but I wasn't about to push that old version to the point of template specialisations. It just about does template functions as it is ;)

BTW, I did actually try with a modulus applied to N as per your example above but it didn't really make a difference to the warning diagnostic.
int p; // A
 

Offline KarlosTopic starter

  • Sockologist
  • Global Moderator
  • Hero Member
  • *****
  • Join Date: Nov 2002
  • Posts: 16878
  • Country: gb
  • Thanked: 5 times
    • Show all replies
Re: GCC asm() warning suppression options?
« Reply #8 on: June 28, 2009, 01:26:55 AM »
Quote from: Trev;513645
Yeah, I'm not doing so well with the ranges today.

I'm just happy to have some miggy coding time at all. Perverting C++ (in ANSI mode) with inline assembler always felt good :lol: If you think this is bad, you should see the divide by zero exception trick ;)

All I need now is to find an -fno-worry-about-asm-immediate-sizes option...
« Last Edit: June 28, 2009, 01:31:37 AM by Karlos »
int p; // A
 

Offline KarlosTopic starter

  • Sockologist
  • Global Moderator
  • Hero Member
  • *****
  • Join Date: Nov 2002
  • Posts: 16878
  • Country: gb
  • Thanked: 5 times
    • Show all replies
Re: GCC asm() warning suppression options?
« Reply #9 on: June 28, 2009, 01:38:45 AM »
Quote from: Trev;513644

EDIT: For extra credit, let's do , too, i.e. rotRight32(-1) rotates left 1 bit. You could then generalize it as rotate(), where the sign of the operand determines the direction. Anyway....


LOL, not exactly in keeping with operators <> though, eh?
int p; // A
 

Offline KarlosTopic starter

  • Sockologist
  • Global Moderator
  • Hero Member
  • *****
  • Join Date: Nov 2002
  • Posts: 16878
  • Country: gb
  • Thanked: 5 times
    • Show all replies
Re: GCC asm() warning suppression options?
« Reply #10 on: June 28, 2009, 01:55:44 AM »
Quote from: x303;513653
Couldn't you do something like this ???

__inline static unsigned long SWAP32(unsigned long a )
{
   unsigned long b;

   __asm__ ("lwbrx %0,0,%1"
           :"=r"(b)
           :"r"(&a), "m"(a));

   return b;

}

x303 :D :D :D

For PowerPC, yeah, this is for M68K :)

BTW, these aren't byteswap functions, they are bitwise rotate functions. I have inlined asm for byteswapping anyway ;)
int p; // A
 

Offline KarlosTopic starter

  • Sockologist
  • Global Moderator
  • Hero Member
  • *****
  • Join Date: Nov 2002
  • Posts: 16878
  • Country: gb
  • Thanked: 5 times
    • Show all replies
Re: GCC asm() warning suppression options?
« Reply #11 on: June 28, 2009, 02:00:57 AM »
Quote from: x303;513656
Try:

__inline static unsigned long SWAP32(unsigned long a)
{

   __asm__ ("rol.w #8,%0;swap %0;rol.w #8,%0"
            :"=d"(a):"0"(a));

   return(a);
}

x303 :D :D :D


Sorry, you're missing the point. It isn't a byteswapping function, it's just a vanilla rotate. My 32-bit byteswap function looks pretty much like yours:

Code: [Select]

namespace Machine {
  // ...
  inline uint32 swap32(uint32 val)
  {
    asm(
      &quot;rol.w #8, %0\n\t&quot;
      &quot;swap %0\n\t&quot;
      &quot;rol.w #8, %0&quot;
      : &quot;=d&quot;(val)
      : &quot;0&quot;(val)
      : &quot;cc&quot;
    );
    return val;
  }
int p; // A
 

Offline KarlosTopic starter

  • Sockologist
  • Global Moderator
  • Hero Member
  • *****
  • Join Date: Nov 2002
  • Posts: 16878
  • Country: gb
  • Thanked: 5 times
    • Show all replies
Re: GCC asm() warning suppression options?
« Reply #12 on: June 28, 2009, 02:25:08 AM »
Quote from: Trev;513665
Looks like gcc 2.95 falls through to a default warning("asm operand %d probably doesn't match constraints", i) for constraints that it doesn't know how to test.

Yeah, they were hedging their bets there, eh? I need to install a higher version or use a cross compiler. 2.95 has a whole host of issues I have workarounds for that aren't actually needed on higher versions anyway.
int p; // A
 

Offline KarlosTopic starter

  • Sockologist
  • Global Moderator
  • Hero Member
  • *****
  • Join Date: Nov 2002
  • Posts: 16878
  • Country: gb
  • Thanked: 5 times
    • Show all replies
Re: GCC asm() warning suppression options?
« Reply #13 on: June 28, 2009, 11:52:29 AM »
Quote from: Trev;513668
And here's the snippet from gcc 2.95.3's recog.c in asm_operand_ok():

Code: [Select]

        case '0': case '1': case '2': case '3': case '4':
        case '5': case '6': case '7': case '8': case '9':
          /* For best results, our caller should have given us the
             proper matching constraint, but we can't actually fail
             the check if they didn't.  Indicate that results are
             inconclusive.  */
          result = -1;
          break;


So, it automatically falls through on matching constraints. What happens if you change "0" to "d0" to tell it operand 0 goes into a data register? EDIT: The point being that matched input operands aren't checked against the constraints previously specified for the output operand.


Well. I'll have a look but I was under the impression the "0" was the correct constraint here:

asm("rol.l %1, %0" : "=d"(val) : "I"(N), "0"(val) : "cc");

since you want the compiler to keep the output in the same register as the first input. Compare the above with the dynamic version:

Code: [Select]

inline uint32 rotRight32(uint32 bits, uint32 val)
{
  asm("ror.l %1, %0" : "=d"(val) : "d"(bits), "0"(val) : "cc");
  return val;
}


Using "d" rather than "d0" allows the compiler to choose which registers to use whenever the function is inlined. I figured this would be a lot better than forcing the use of any specific data register.
int p; // A
 

Offline KarlosTopic starter

  • Sockologist
  • Global Moderator
  • Hero Member
  • *****
  • Join Date: Nov 2002
  • Posts: 16878
  • Country: gb
  • Thanked: 5 times
    • Show all replies
Re: GCC asm() warning suppression options?
« Reply #14 on: June 28, 2009, 12:03:58 PM »
As I expected, changing "0" to "d0", breaks, since there's nothing forcing the input into d0 in the first place:

Code: [Select]

movel #-1430532899,d4 <- 0xAABBCCDD
[B] movel d4,sp@- <- no rotate done for zero bits, this is fine[/B]
movel d4,sp@-
pea LC5
lea _printf,a2
jbsr a2@
addw #12,sp
[B]#APP
rol.l #4, d0 <- where is d0 initialised?
#NO_APP[/B]
movel d0,sp@-
movel d4,sp@-
pea LC5
jbsr a2@
addw #12,sp


Whatever was already in d0 gets shifted and the compiler has to work around ensuring d0 is preserved around my asm() calls, despite the fact it never initialises it with the test data (which it decided to put in d4 here).
int p; // A