https://gcc.gnu.org/bugzilla/show_bug.cgi?id=81845
Bug ID: 81845 Summary: asm memory constraints are difficult and not well documented Product: gcc Version: 8.0 Status: UNCONFIRMED Severity: enhancement Priority: P3 Component: inline-asm Assignee: unassigned at gcc dot gnu.org Reporter: amodra at gmail dot com Target Milestone: --- gcc doesn't have a simple way to say that a pointer passed to an inline asm is used to address an array. "m" (*p) unfortunately only makes the asm depend on the first element of an array. The following came about from a discussion starting at https://gcc.gnu.org/ml/gcc/2017-08/msg00116.html Compiling with -DTRY=4 or -DTRY=6 give good results, and I believe are reasonable to extend to types other than char without falling foul of aliasing rules, but these tricks are a little contrived and not documented. -DTRY=5 is no doubt what should be used but this does not appear to be documented. /* -O3 */ #include <stdio.h> #if TRY == 1 int get_string_length (const char *p) { int count; /* Unfortunately the "m" here only makes the asm depend on p[0], so this is not safe. The initialization of buff in main can be partly moved past the get_string_length call. */ __asm__ ("repne scasb" : "=c" (count), "+D" (p) : "m" (*p), "0" (-1), "a" (0) ); return -2 - count; } #elif TRY == 2 /* No better than the above. */ int get_string_length (const char p[]) { int count; __asm__ ("repne scasb" : "=c" (count), "+D" (p) : "m" (*p), "0" (-1), "a" (0) ); return -2 - count; } #elif TRY == 3 int get_string_length (const char *p) { int count; /* Warns unfortunately, but works, at least on some versions of gcc. */ __asm__ ("repne scasb" : "=c" (count), "+D" (p) : "m" (*(const void *)p), "0" (-1), "a" (0) ); return -2 - count; } #elif TRY == 4 int get_string_length (const char *p) { int count; /* A way of saying that p points to an array of indeterminate length, near enough. */ __asm__ ("repne scasb" : "=c" (count), "+D" (p) : "m" (*(const struct {char a; char x[];} *) p), "0" (-1), "a" (0) ); return -2 - count; } #elif TRY == 5 int get_string_length (const char *p) { int count; /* The right way to say that p points to an array of char of indeterminate length. */ __asm__ ("repne scasb" : "=c" (count), "+D" (p) : "m" (*(const char (*)[]) p), "0" (-1), "a" (0) ); return -2 - count; } #elif TRY == 6 int get_string_length (const char *p) { int count; /* Make gcc lose detailed information regarding the memory pointed to by p. This, in conjuction with "m" (*p) as an input below will act similarly to a "memory" clobber, but is more precise in that it doesn't say all memory may be written. */ __asm__ ("#%0" : "+X" (p)); __asm__ ("repne scasb" : "=c" (count), "+D" (p) : "m" (*p), "0" (-1), "a" (0) ); return -2 - count; } #elif TRY == 7 int get_string_length (const char *p) { int count; /* As recommended by the gcc info doc. Suffers from needing to know the size of the array. */ __asm__ ("repne scasb" : "=c" (count), "+D" (p) : "m" (*(const struct {char x[48];} *) p), "0" (-1), "a" (0) ); return -2 - count; } #else int get_string_length (const char *p) { int count; /* Bog standard, but unfortunately says to gcc that memory might be written by the asm. As can be seen by inspecting the code produced, this means "expected" below must be read before calling get_string_length, using an extra register. This may result in less optimized code in real-world examples. */ __asm__ ("repne scasb" : "=c" (count), "+D" (p) : "0" (-1), "a" (0) : "memory" ); return -2 - count; } #endif int expected = 4; int main () { char buff[48] = "hello world"; buff[4] = 0; int b = expected; int a = get_string_length (buff); printf ("%s: %d %d\n", buff, a, b); return 0; }