This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
[RFA:] Take 2: emit error for overlap of asm-register-declared variable and asm clobber list
- From: Hans-Peter Nilsson <hans-peter dot nilsson at axis dot com>
- To: gcc-patches at gcc dot gnu dot org
- Date: Sat, 21 Sep 2002 03:38:19 +0200
- Subject: [RFA:] Take 2: emit error for overlap of asm-register-declared variable and asm clobber list
Adjusted according to RTH:s comments (thanks!) See
<URL:http://gcc.gnu.org/ml/gcc-patches/2002-09/msg01168.html>
for take one.
Bootstrapped && checked i686-pc-linux-gnu, no Ada. Built and
checked cross to cris-axis-elf, mmix-knuth-mmixware,
powerpc-eabisim, arm-elf, h8300-hms, no regressions, no Ada.
gcc:
* doc/extend.texi (Extended Asm): Clarify that overlap between
asm-declared register variables used in an asm and the asm clobber
list is not allowed.
* stmt.c (decl_conflicts_with_clobbers_p): New function.
(expand_asm_operands): Keep track of clobbered registers. Call
decl_conflicts_with_clobbers_p for each input and output operand.
If no conflicts found before, also do conflict sanity check when
emitting clobbers.
gcc/testsuite:
* gcc.dg/20020919-1.c: New test.
*** /dev/null Tue Jan 1 05:00:00 1980
--- gcc.dg/20020919-1.c Fri Sep 20 02:29:48 2002
***************
*** 0 ****
--- 1,245 ----
+ /* Copyright (C) 2002 Free Software Foundation.
+ by Hans-Peter Nilsson <hp@axis.com>
+
+ Making sure that asm clobbers conflicting with asm-declared input
+ operands are detected: ``You may not write a clobber description in a
+ way that overlaps with an input or output operand''.
+
+ You must be this tall ---> fit two long longs in asm-declared registers
+ to enter this amusement. */
+
+ /* { dg-do compile { target alpha-*-* cris-*-* i?86-*-* mmix-*-* powerpc-*-* rs6000-*-* } } */
+ /* { dg-options "-O2" } */
+
+ /* Constructed examples; input/output (same register), output, input, and
+ input and output (different registers). */
+
+ /* The long longs are used to test overlap overlap for multi-register
+ registers. REG2 and REG4 must be the second halves (defined as
+ higher-numbered parts) of REG1 and REG3 respectively when two registers
+ are needed. */
+
+ #if defined (__alpha__)
+ # define REG1 "$1"
+ # define REG2 "$2"
+ #elif defined (__CRIS__)
+ # define REG1 "r10"
+ # define REG2 "r11"
+ # define REG3 "r12"
+ # define REG4 "r13"
+ # define REG5 "r9"
+ #elif defined (__i386__)
+ # define REG1 "%eax"
+ # define REG2 "%edx"
+ # define REG3 "%ecx"
+ # define REG4 "%ebx"
+ #elif defined (__MMIX__)
+ # define REG1 "$8"
+ # define REG2 "$9"
+ #elif defined (__powerpc__) || defined (__PPC__) || defined (__ppc__) \
+ || defined (__POWERPC__) || defined (PPC) || defined (_IBMR2)
+ # define REG1 "6"
+ # define REG2 "7"
+ # define REG3 "8"
+ # define REG4 "9"
+ #endif
+
+ /* For readability of the tests. */
+ #ifdef REG3
+ # define REG1a REG1
+ # define REG1b REG2
+ # define REG2a REG3
+ # define REG2b REG4
+ #else
+ # define REG1a REG1
+ # define REG1b REG1
+ # define REG2a REG2
+ # define REG2b REG2
+ #endif
+
+ /* REG5 is just another reg if there is one; the difference to REG4 is to
+ keep the original set of registers for CRIS. */
+ #ifndef REG5
+ #define REG5 REG2b
+ #endif
+
+ void *
+ foo (void *p)
+ {
+ register void *q asm (REG1) = p;
+ asm ("foo1 %0" : "=r" (q) : "0" (q) : REG1); /* { dg-error "conflict" } */
+ return q;
+ }
+
+ void *
+ nfoo (void *p)
+ {
+ register void *q asm (REG1) = p;
+ asm ("foo1 %0" : "=r" (q) : "0" (q) : REG2);
+ return q;
+ }
+
+ long long
+ foolla (long long llp)
+ {
+ register long long ll asm (REG1a) = llp;
+ asm ("foo1a %0" : "=r" (ll) : "0" (ll) : REG1a); /* { dg-error "conflict" } */
+ return ll;
+ }
+
+ long long
+ nfoolla (long long llp)
+ {
+ register long long ll asm (REG1a) = llp;
+ asm ("foo1a %0" : "=r" (ll) : "0" (ll) : REG2a);
+ return ll;
+ }
+
+ long long
+ foollb (long long llp)
+ {
+ register long long ll asm (REG1a) = llp;
+ asm ("foo1b %0" : "=r" (ll) : "0" (ll) : REG1b); /* { dg-error "conflict" } */
+ return ll;
+ }
+
+ void *
+ bar (void *p)
+ {
+ register void *q asm (REG1);
+ register void *w asm (REG2) = p;
+ asm ("bar1 %1,%0" : "=r" (q) : "r" (w) : REG1); /* { dg-error "conflict" } */
+ return q;
+ }
+
+ long long
+ barlla (long long llp)
+ {
+ register long long ll asm (REG1a);
+ register long long mm asm (REG2a) = llp;
+ asm ("bar1a %1,%0" : "=r" (ll) : "r" (mm) : REG1b); /* { dg-error "conflict" } */
+ return ll;
+ }
+
+ long long
+ barllb (long long llp)
+ {
+ register long long ll asm (REG1a);
+ register long long mm asm (REG2a) = llp;
+ asm ("bar1b %1,%0" : "=r" (ll) : "r" (mm) : REG2b); /* { dg-error "conflict" } */
+ return ll;
+ }
+
+ void *
+ foobar (void *p)
+ {
+ register void *q asm (REG1);
+ register void *w asm (REG2) = p;
+ asm ("foobar1 %1,%0" : "=r" (q) : "r" (w) : REG2); /* { dg-error "conflict" } */
+ return q;
+ }
+
+ void *
+ nfoobar (void *p)
+ {
+ register void *q asm (REG1);
+ register void *w = p;
+ asm ("foobar1 %1,%0" : "=r" (q) : "r" (w) : REG2);
+ return q;
+ }
+
+ long long
+ foobarlla (long long llp)
+ {
+ register long long ll asm (REG1a);
+ register long long mm asm (REG2a) = llp;
+ asm ("foobar1a %1,%0" : "=r" (ll) : "r" (mm) : REG1b); /* { dg-error "conflict" } */
+ return ll;
+ }
+
+ long long
+ nfoobarlla (long long llp)
+ {
+ register long long ll asm (REG1a);
+ register long long mm = llp;
+ asm ("foobar1a %1,%0" : "=r" (ll) : "r" (mm) : REG2a);
+ return ll;
+ }
+
+ long long
+ foobarllb (long long llp)
+ {
+ register long long ll asm (REG1a);
+ register long long mm asm (REG2a) = llp;
+ asm ("foobar1b %1,%0" : "=r" (ll) : "r" (mm) : REG2b); /* { dg-error "conflict" } */
+ return ll;
+ }
+
+ long long
+ nfoobarllb (long long llp)
+ {
+ register long long ll asm (REG1a);
+ register long long mm = llp;
+ asm ("foobar1b %1,%0" : "=r" (ll) : "r" (mm) : REG2b);
+ return ll;
+ }
+
+ void *
+ baz (void *p)
+ {
+ register void *q asm (REG1);
+ register void *w asm (REG2) = p;
+ asm ("baz1 %1,%0" : "=r" (q) : "r" (w) : REG1, REG2); /* { dg-error "conflict" } */
+ return q;
+ }
+
+ void *
+ nbaz (void *p)
+ {
+ register void *q;
+ register void *w = p;
+ asm ("baz1 %1,%0" : "=r" (q) : "r" (w) : REG1, REG2);
+ return q;
+ }
+
+ void *
+ nbaz2 (void *p)
+ {
+ register void *q asm (REG1);
+ register void *w asm (REG2) = p;
+ asm ("baz1 %1,%0" : "=r" (q) : "r" (w));
+ return q;
+ }
+
+ long long
+ bazlla (long long llp)
+ {
+ register long long ll asm (REG1a);
+ register long long mm asm (REG2a) = llp;
+ asm ("baz1a %1,%0" : "=r" (ll) : "r" (mm) : REG1a, REG2a); /* { dg-error "conflict" } */
+ return ll;
+ }
+
+ long long
+ bazllb (long long llp)
+ {
+ register long long ll asm (REG1a);
+ register long long mm asm (REG2a) = llp;
+ asm ("baz2a %1,%0" : "=r" (ll) : "r" (mm) : REG1b, REG2b); /* { dg-error "conflict" } */
+ return ll;
+ }
+
+ /* Real-world example of bug. */
+
+ struct stat;
+ int
+ _dl_stat (const char *file_name, struct stat *buf)
+ {
+ register long a asm (REG1) = (long) file_name;
+ register long b asm (REG2) = (long) buf;
+
+ asm volatile ("movu.w %1,$r9\n\tbreak 13" : "=r" (a) : "g" (106), "0" (a), "r" (b) : REG1, REG5); /* { dg-error "conflict" } */
+ if (a >= 0)
+ return (int) a;
+ return (int) -1;
+ }
Index: extend.texi
===================================================================
RCS file: /cvs/gcc/gcc/gcc/doc/extend.texi,v
retrieving revision 1.102
diff -p -c -r1.102 extend.texi
*** extend.texi 17 Sep 2002 09:26:05 -0000 1.102
--- extend.texi 19 Sep 2002 21:08:13 -0000
*************** asm volatile ("movc3 %0,%1,%2"
*** 3700,3706 ****
You may not write a clobber description in a way that overlaps with an
input or output operand. For example, you may not have an operand
describing a register class with one member if you mention that register
! in the clobber list. There is no way for you to specify that an input
operand is modified without also specifying it as an output
operand. Note that if all the output operands you specify are for this
purpose (and hence unused), you will then also need to specify
--- 3702,3711 ----
You may not write a clobber description in a way that overlaps with an
input or output operand. For example, you may not have an operand
describing a register class with one member if you mention that register
! in the clobber list. Variables declared to live in specific registers
! (@pxref{Explicit Reg Vars}), and used as asm input or output operands must
! have no part mentioned in the clobber description.
! There is no way for you to specify that an input
operand is modified without also specifying it as an output
operand. Note that if all the output operands you specify are for this
purpose (and hence unused), you will then also need to specify
Index: stmt.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/stmt.c,v
retrieving revision 1.269
diff -p -c -r1.269 stmt.c
*** stmt.c 14 Aug 2002 10:04:48 -0000 1.269
--- stmt.c 19 Sep 2002 23:00:15 -0000
*************** static int n_occurrences PARAMS ((int,
*** 398,403 ****
--- 398,404 ----
static bool parse_input_constraint PARAMS ((const char **, int, int, int,
int, const char * const *,
bool *, bool *));
+ static bool decl_conflicts_with_clobbers_p PARAMS ((tree, const HARD_REG_SET));
static void expand_goto_internal PARAMS ((tree, rtx, rtx));
static int expand_fixup PARAMS ((tree, rtx, rtx));
static rtx expand_nl_handler_label PARAMS ((rtx, rtx));
*************** parse_input_constraint (constraint_p, in
*** 1400,1405 ****
--- 1401,1442 ----
return true;
}
+ /* Check for overlap between registers marked in CLOBBERED_REGS and
+ anything inappropriate in DECL. Emit error and return TRUE for error,
+ FALSE for ok. */
+
+ static bool
+ decl_conflicts_with_clobbers_p (decl, clobbered_regs)
+ tree decl;
+ const HARD_REG_SET clobbered_regs;
+ {
+ /* Conflicts between asm-declared register variables and the clobber
+ list are not allowed. */
+ if ((TREE_CODE (decl) == VAR_DECL || TREE_CODE (decl) == PARM_DECL)
+ && DECL_REGISTER (decl)
+ && REGNO (DECL_RTL (decl)) < FIRST_PSEUDO_REGISTER)
+ {
+ rtx reg = DECL_RTL (decl);
+ unsigned int regno;
+
+ for (regno = REGNO (reg);
+ regno < (REGNO (reg)
+ + HARD_REGNO_NREGS (REGNO (reg), GET_MODE (reg)));
+ regno++)
+ if (TEST_HARD_REG_BIT (clobbered_regs, regno))
+ {
+ error ("asm-specifier for variable `%s' conflicts with asm clobber list",
+ IDENTIFIER_POINTER (DECL_NAME (decl)));
+
+ /* Reset registerness to stop multiple errors emitted for a
+ single variable. */
+ DECL_REGISTER (decl) = 0;
+ return true;
+ }
+ }
+ return false;
+ }
+
/* Generate RTL for an asm statement with arguments.
STRING is the instruction template.
OUTPUTS is a list of output arguments (lvalues); INPUTS a list of inputs.
*************** expand_asm_operands (string, outputs, in
*** 1430,1435 ****
--- 1467,1474 ----
int noutputs = list_length (outputs);
int ninout;
int nclobbers;
+ HARD_REG_SET clobbered_regs;
+ int clobber_conflict_found = 0;
tree tail;
int i;
/* Vector of RTX's of evaluated output operands. */
*************** expand_asm_operands (string, outputs, in
*** 1467,1472 ****
--- 1506,1512 ----
/* Count the number of meaningful clobbered registers, ignoring what
we would ignore later. */
nclobbers = 0;
+ CLEAR_HARD_REG_SET (clobbered_regs);
for (tail = clobbers; tail; tail = TREE_CHAIN (tail))
{
const char *regname = TREE_STRING_POINTER (TREE_VALUE (tail));
*************** expand_asm_operands (string, outputs, in
*** 1476,1481 ****
--- 1516,1525 ----
++nclobbers;
else if (i == -2)
error ("unknown register name `%s' in `asm'", regname);
+
+ /* Mark clobbered registers. */
+ if (i >= 0)
+ SET_HARD_REG_BIT (clobbered_regs, i);
}
clear_last_expr ();
*************** expand_asm_operands (string, outputs, in
*** 1601,1606 ****
--- 1645,1653 ----
inout_mode[ninout] = TYPE_MODE (type);
inout_opnum[ninout++] = i;
}
+
+ if (decl_conflicts_with_clobbers_p (val, clobbered_regs))
+ clobber_conflict_found = 1;
}
/* Make vectors for the expression-rtx, constraint strings,
*************** expand_asm_operands (string, outputs, in
*** 1685,1690 ****
--- 1732,1740 ----
ASM_OPERANDS_INPUT_CONSTRAINT_EXP (body, i)
= gen_rtx_ASM_INPUT (TYPE_MODE (type), constraints[i + noutputs]);
+
+ if (decl_conflicts_with_clobbers_p (val, clobbered_regs))
+ clobber_conflict_found = 1;
}
/* Protect all the operands from the queue now that they have all been
*************** expand_asm_operands (string, outputs, in
*** 1769,1774 ****
--- 1819,1825 ----
{
const char *regname = TREE_STRING_POINTER (TREE_VALUE (tail));
int j = decode_reg_name (regname);
+ rtx clobbered_reg;
if (j < 0)
{
*************** expand_asm_operands (string, outputs, in
*** 1790,1797 ****
}
/* Use QImode since that's guaranteed to clobber just one reg. */
XVECEXP (body, 0, i++)
! = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (QImode, j));
}
insn = emit_insn (body);
--- 1841,1869 ----
}
/* Use QImode since that's guaranteed to clobber just one reg. */
+ clobbered_reg = gen_rtx_REG (QImode, j);
+
+ /* Do sanity check for overlap between clobbers and respectively
+ input and outputs that hasn't been handled. Such overlap
+ should have been detected and reported above. */
+ if (!clobber_conflict_found)
+ {
+ int opno;
+
+ /* We test the old body (obody) contents to avoid tripping
+ over the under-construction body. */
+ for (opno = 0; opno < noutputs; opno++)
+ if (reg_overlap_mentioned_p (clobbered_reg, output_rtx[opno]))
+ internal_error ("asm clobber conflict with output operand");
+
+ for (opno = 0; opno < ninputs - ninout; opno++)
+ if (reg_overlap_mentioned_p (clobbered_reg,
+ ASM_OPERANDS_INPUT (obody, opno)))
+ internal_error ("asm clobber conflict with input operand");
+ }
+
XVECEXP (body, 0, i++)
! = gen_rtx_CLOBBER (VOIDmode, clobbered_reg);
}
insn = emit_insn (body);
brgds, H-P