Why is this simple c program with gcc (clang) inline assembly exhibiting undefined behaviour?

时间:2015-07-28 23:47:25

标签: gcc x86 inline-assembly gas

I'm trying to do a very simple thing with gcc assembler extension:

  • load an unsigned int variable into a register
  • add 1 to it
  • output the result

While compiling my solution:

v <- rep(v, 5e4)
microbenchmark(
  rangeWhich = rangeWhich(v),
  range_find = range_find(v),
  richwhich = {w <- which(v)
               w[c(1L, length(w))]},
  match = c(match(T,v),length(v)-match(T,rev(v))+1)
)
Unit: microseconds
       expr       min         lq        mean    median         uq        max neval
 rangeWhich     1.284     3.2090    16.50914    20.211    26.7875     29.836   100
 range_find     9.945    21.4945    32.02652    26.948    34.1660    144.042   100
  richwhich  2941.756  3022.5975  3243.02081  3130.227  3247.6405   5403.911   100
      match 45696.329 46771.8175 50662.45708 47359.526 48718.6055 131439.661   100

with the following switches:

#include <stdio.h>
#define inf_int volatile unsigned long long

int main(int argc, char *argv[]){
   inf_int zero = 0;
   inf_int one = 1;
   inf_int infinity = ~0;
   printf("value of zero, one, infinity = %llu, %llu, %llu\n", zero, one, infinity);
   __asm__ volatile (
      "addq $1, %0"
      : "=r" (infinity)
   );
   __asm__ volatile (
      "addq $1, %0"
      : "=r" (zero)
   );
   __asm__ volatile (
      "addq $1, %0"
      : "=r" (one)
   );
   printf("value of zero, one, infinity = %llu, %llu, %llu\n", zero, one, infinity);
   return 0;
}

I'd expect the following result from running gcc -std=c99 --pedantic -Wall -c main.c -o main.o gcc -std=c99 --pedantic -Wall main.o -o main :

value of zero, one, infinity = 0, 1, 18446744073709551615

value of zero, one, infinity = 1, 2, 0

but the result I get is this:

value of zero, one, infinity = 0, 1, 18446744073709551615

value of zero, one, infinity = 60, 61, 59

Interestingly, if I add a single char to the first main I get the following, off-by-one, output:

value of zerao, one, infinity = 0, 1, 18446744073709551615

value of zero, one, infinity = 61, 62, 60

Even more interestingly, I can fix the behaviour by adding (optional) output registers. But this would be wasteful because of using 2*more registers, and doesn't help me understand why the previous piece exhibits undefined behaviour.

printf

edit

compiling with clang with the same options gives undefined behaviour as well:

value of zerao, one, infinity = 0, 1, 18446744073709551615

value of zero, one, infinity = 2147483590, 2147483591, 2147483592

edit 2

as suggested by Olaf, I've tried with #include <stdio.h> #define inf_int volatile unsigned long long int main(int argc, char *argv[]){ inf_int zero = 0; inf_int one = 1; inf_int infinity = ~0; printf("value of zerao, one, infinity = %llu, %llu, %llu\n", zero, one, infinity); __asm__ volatile ( "addq $1, %0 \n\t" "movq %0, %1" : "=r" (zero) : "r" (zero) ); __asm__ volatile ( "addq $1, %0 \n\t" "movq %0, %1" : "=r" (one) : "r" (one) ); __asm__ volatile ( "addq $1, %0 \n\t" "movq %0, %1" : "=r" (infinity) : "r" (infinity) ); printf("value of zero, one, infinity = %llu, %llu, %llu\n", zero, one, infinity); return 0; } from uint64_t. The result of running the program is still undefined.

stdint.h

2 个答案:

答案 0 :(得分:7)

Your first code does not specify any inputs to the asm statements so the chosen register has an undefined value (which in this case was initially the return value of OrderInputFilter). The second example repeats the error of using an undefined value and adds further undefined behaviour by overwriting the input register with the output.

You could use two registers like:

printf

You could use an input/output argument:

__asm__ (
   "movq %1, %0 \n\t"
   "addq $1, %0"
   : "=r" (zero)
   : "r" (zero)
);

Which can be in memory as well as a register:

__asm__ (
   "addq $1, %0"
   : "+r" (zero)
);

Or you could tie the input to the output:

__asm__ (
   "addq $1, %0"
   : "+rm" (zero)
);

And finally there is no need for any of the __asm__ ( "addq $1, %0" : "=rm" (zero) : "0" (zero) ); modifiers.

答案 1 :(得分:1)

To wrap it all up:

inline assembly is not the part of C standard, it's an extension so portability (even across compilers on the same hardware) is not guaranteed.

one good way to write it is following:

select u.*
from users u 
where not exists (select 1
                  from addresses a
                  where a.username = u.username and
                        a.city = 'Peoria'
                 );