This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
[RFA:] 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: Thu, 19 Sep 2002 23:41:11 +0200
- Subject: [RFA:] Emit error for overlap of asm-register-declared variable and asm clobber list
See <URL:http://gcc.gnu.org/ml/gcc/2002-09/msg00741.html>.
Bootstrapped && checked i686-pc-linux-gnu (no Ada) no regressions.
Built and checked cross to cris-axis-elf and mmix-knuth-mmixware, no
regressions.
Note the extra sanity-check. I don't think expand_expr should be
trusted blindly (and if it works today, that's no promise for
tomorrow) particularly when testing correctness is cheap. It can't
possibly be that parsing of asm operands is on the hot path, so always
making *really* sure that there's no overlap introduced without
wrapping the test in #ifdef ENABLE_CHECKING seems best.
This *will* emit errors for marginally safe conditions where overlap
can't happen, for example when the constraint for the overlapping
operand must always be reloaded to another non-overlapping operand:
register void *q;
register void *w asm ("r11") = p;
...
asm ("frob %0" : "=r" (q) : "0" (w) : "r11");
Still I think a hard-and-fast rule is better. It's not hard to fix a
program where this happens. (Hint: use an extra temporary variable.)
Ok to commit somewhere? Hopefully main trunk; the bug it fixes is
that conflicting asm clobber overlap with operands isn't signalled
properly as an error, while gcc silently creates surprising results
when there's overlap.
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 Thu Sep 19 05:45:53 2002
***************
*** 0 ****
--- 1,132 ----
+ /* 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''. */
+
+ /* { dg-do compile { target cris-*-* } } */
+ /* { dg-options "-O2" } */
+
+ /* Constructed examples; input/output (same register), output, input, and
+ input and output (different registers). */
+
+ void *
+ foo (void *p)
+ {
+ register void *q asm ("r10") = p;
+ asm ("foo1 %0" : "=r" (q) : "0" (q) : "r10"); /* { dg-error "conflict" } */
+ return q;
+ }
+
+ long long
+ foolla (long long llp)
+ {
+ register long long ll asm ("r10") = llp;
+ asm ("foo1a %0" : "=r" (ll) : "0" (ll) : "r10"); /* { dg-error "conflict" } */
+ return ll;
+ }
+
+ long long
+ foollb (long long llp)
+ {
+ register long long ll asm ("r10") = llp;
+ asm ("foo1b %0" : "=r" (ll) : "0" (ll) : "r11"); /* { dg-error "conflict" } */
+ return ll;
+ }
+
+ void *
+ bar (void *p)
+ {
+ register void *q asm ("r10") = p;
+ register void *w asm ("r11") = p;
+ asm ("bar1 %1,%0" : "=r" (q) : "r" (w) : "r10"); /* { dg-error "conflict" } */
+ return q;
+ }
+
+ long long
+ barlla (long long llp)
+ {
+ register long long ll asm ("r10") = llp;
+ register long long mm asm ("r12") = llp;
+ asm ("bar1a %1,%0" : "=r" (ll) : "r" (mm) : "r11"); /* { dg-error "conflict" } */
+ return ll;
+ }
+
+ long long
+ barllb (long long llp)
+ {
+ register long long ll asm ("r10") = llp;
+ register long long mm asm ("r12") = llp;
+ asm ("bar1b %1,%0" : "=r" (ll) : "r" (mm) : "r13"); /* { dg-error "conflict" } */
+ return ll;
+ }
+
+ void *
+ foobar (void *p)
+ {
+ register void *q asm ("r10") = p;
+ register void *w asm ("r11") = p;
+ asm ("foobar1 %1,%0" : "=r" (q) : "r" (w) : "r11"); /* { dg-error "conflict" } */
+ return q;
+ }
+
+ long long
+ foobarlla (long long llp)
+ {
+ register long long ll asm ("r10") = llp;
+ register long long mm asm ("r12") = llp;
+ asm ("foobar1a %1,%0" : "=r" (ll) : "r" (mm) : "r11"); /* { dg-error "conflict" } */
+ return ll;
+ }
+
+ long long
+ foobarllb (long long llp)
+ {
+ register long long ll asm ("r10") = llp;
+ register long long mm asm ("r12") = llp;
+ asm ("foobar1b %1,%0" : "=r" (ll) : "r" (mm) : "r13"); /* { dg-error "conflict" } */
+ return ll;
+ }
+
+ void *
+ baz (void *p)
+ {
+ register void *q asm ("r10") = p;
+ register void *w asm ("r11") = p;
+ asm ("baz1 %1,%0" : "=r" (q) : "r" (w) : "r10", "r11"); /* { dg-error "conflict" } */
+ return q;
+ }
+
+ long long
+ bazlla (long long llp)
+ {
+ register long long ll asm ("r10") = llp;
+ register long long mm asm ("r12") = llp;
+ asm ("baz1a %1,%0" : "=r" (ll) : "r" (mm) : "r10", "r12"); /* { dg-error "conflict" } */
+ return ll;
+ }
+
+ long long
+ bazllb (long long llp)
+ {
+ register long long ll asm ("r10") = llp;
+ register long long mm asm ("r12") = llp;
+ asm ("baz2a %1,%0" : "=r" (ll) : "r" (mm) : "r11", "r13"); /* { 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 ("r10") = (long) file_name;
+ register long b asm ("r11") = (long) buf;
+
+ asm volatile ("movu.w %1,$r9\n\tbreak 13" : "=r" (a) : "g" (106), "0" (a), "r" (b) : "r10", "r9"); /* { 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 14:17:12 -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 int *));
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 int *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 (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;
+ int *clobbered_regs = (int *) alloca (FIRST_PSEUDO_REGISTER * sizeof (int));
+ 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;
+ memset (clobbered_regs, 0, sizeof (int) * FIRST_PSEUDO_REGISTER);
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)
+ clobbered_regs[i] = 1;
}
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