This is the mail archive of the
gcc-bugs@gcc.gnu.org
mailing list for the GCC project.
[Bug inline-asm/81845] New: asm memory constraints are difficult and not well documented
- From: "amodra at gmail dot com" <gcc-bugzilla at gcc dot gnu dot org>
- To: gcc-bugs at gcc dot gnu dot org
- Date: Mon, 14 Aug 2017 09:20:55 +0000
- Subject: [Bug inline-asm/81845] New: asm memory constraints are difficult and not well documented
- Auto-submitted: auto-generated
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;
}