This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
[PATCH v2 6/6] i386: Implement asm flag outputs
- From: Richard Henderson <rth at redhat dot com>
- To: gcc-patches at gcc dot gnu dot org
- Cc: hpa at zytor dot com, law at redhat dot com, torvalds at linux-foundation dot org
- Date: Fri, 15 May 2015 08:37:15 -0700
- Subject: [PATCH v2 6/6] i386: Implement asm flag outputs
- Authentication-results: sourceware.org; auth=none
- References: <1431034740-5375-7-git-send-email-rth at redhat dot com>
Version 2 includes proper test cases and documentation.
Hopefully the documentation even makes sense. Suggestions
and improvements there gratefully appreciated.
r~
---
gcc/config/i386/constraints.md | 5 ++
gcc/config/i386/i386.c | 137 +++++++++++++++++++++++++++--
gcc/doc/extend.texi | 76 ++++++++++++++++
gcc/testsuite/gcc.target/i386/asm-flag-0.c | 15 ++++
gcc/testsuite/gcc.target/i386/asm-flag-1.c | 18 ++++
gcc/testsuite/gcc.target/i386/asm-flag-2.c | 16 ++++
gcc/testsuite/gcc.target/i386/asm-flag-3.c | 22 +++++
gcc/testsuite/gcc.target/i386/asm-flag-4.c | 20 +++++
gcc/testsuite/gcc.target/i386/asm-flag-5.c | 19 ++++
9 files changed, 321 insertions(+), 7 deletions(-)
create mode 100644 gcc/testsuite/gcc.target/i386/asm-flag-0.c
create mode 100644 gcc/testsuite/gcc.target/i386/asm-flag-1.c
create mode 100644 gcc/testsuite/gcc.target/i386/asm-flag-2.c
create mode 100644 gcc/testsuite/gcc.target/i386/asm-flag-3.c
create mode 100644 gcc/testsuite/gcc.target/i386/asm-flag-4.c
create mode 100644 gcc/testsuite/gcc.target/i386/asm-flag-5.c
diff --git a/gcc/config/i386/constraints.md b/gcc/config/i386/constraints.md
index 2271bd1..d16e728 100644
--- a/gcc/config/i386/constraints.md
+++ b/gcc/config/i386/constraints.md
@@ -146,10 +146,15 @@
"@internal Lower SSE register when avoiding REX prefix and all SSE registers otherwise.")
;; We use the B prefix to denote any number of internal operands:
+;; f FLAGS_REG
;; s Sibcall memory operand, not valid for TARGET_X32
;; w Call memory operand, not valid for TARGET_X32
;; z Constant call address operand.
+(define_constraint "Bf"
+ "@internal Flags register operand."
+ (match_operand 0 "flags_reg_operand"))
+
(define_constraint "Bs"
"@internal Sibcall memory operand."
(and (not (match_test "TARGET_X32"))
diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
index de333d8..868316a 100644
--- a/gcc/config/i386/i386.c
+++ b/gcc/config/i386/i386.c
@@ -45447,21 +45447,144 @@ ix86_c_mode_for_suffix (char suffix)
/* Worker function for TARGET_MD_ASM_ADJUST.
- We do this in the new i386 backend to maintain source compatibility
+ We implement asm flag outputs, and maintain source compatibility
with the old cc0-based compiler. */
static rtx_insn *
-ix86_md_asm_adjust (vec<rtx> &/*outputs*/, vec<rtx> &/*inputs*/,
- vec<const char *> &/*constraints*/,
+ix86_md_asm_adjust (vec<rtx> &outputs, vec<rtx> &/*inputs*/,
+ vec<const char *> &constraints,
vec<rtx> &clobbers, HARD_REG_SET &clobbered_regs)
{
- clobbers.safe_push (gen_rtx_REG (CCmode, FLAGS_REG));
clobbers.safe_push (gen_rtx_REG (CCFPmode, FPSR_REG));
-
- SET_HARD_REG_BIT (clobbered_regs, FLAGS_REG);
SET_HARD_REG_BIT (clobbered_regs, FPSR_REG);
- return NULL;
+ bool saw_asm_flag = false;
+
+ start_sequence ();
+ for (unsigned i = 0, n = outputs.length (); i < n; ++i)
+ {
+ const char *con = constraints[i];
+ if (strncmp (con, "=@cc", 4) != 0)
+ continue;
+ con += 4;
+ if (strchr (con, ',') != NULL)
+ {
+ error ("alternatives not allowed in asm flag output");
+ continue;
+ }
+
+ bool invert = false;
+ if (con[0] == 'n')
+ invert = true, con++;
+
+ machine_mode mode = CCmode;
+ rtx_code code = UNKNOWN;
+
+ switch (con[0])
+ {
+ case 'a':
+ if (con[1] == 0)
+ mode = CCAmode, code = EQ;
+ else if (con[1] == 'e' && con[2] == 0)
+ mode = CCCmode, code = EQ;
+ break;
+ case 'b':
+ if (con[1] == 0)
+ mode = CCCmode, code = EQ;
+ else if (con[1] == 'e' && con[2] == 0)
+ mode = CCAmode, code = NE;
+ break;
+ case 'c':
+ if (con[1] == 0)
+ mode = CCCmode, code = EQ;
+ break;
+ case 'e':
+ if (con[1] == 0)
+ mode = CCZmode, code = EQ;
+ break;
+ case 'g':
+ if (con[1] == 0)
+ mode = CCGCmode, code = GT;
+ else if (con[1] == 'e' && con[2] == 0)
+ mode = CCGCmode, code = GE;
+ break;
+ case 'l':
+ if (con[1] == 0)
+ mode = CCGCmode, code = LT;
+ else if (con[1] == 'e' && con[2] == 0)
+ mode = CCGCmode, code = LE;
+ break;
+ case 'o':
+ if (con[1] == 0)
+ mode = CCOmode, code = EQ;
+ break;
+ case 'p':
+ if (con[1] == 0)
+ mode = CCPmode, code = EQ;
+ break;
+ case 's':
+ if (con[1] == 0)
+ mode = CCSmode, code = EQ;
+ break;
+ case 'z':
+ if (con[1] == 0)
+ mode = CCZmode, code = EQ;
+ break;
+ }
+ if (code == UNKNOWN)
+ {
+ error ("unknown asm flag output %qs", constraints[i]);
+ continue;
+ }
+ if (invert)
+ code = reverse_condition (code);
+
+ rtx dest = outputs[i];
+ if (!saw_asm_flag)
+ {
+ /* This is the first asm flag output. Here we put the flags
+ register in as the real output and adjust the condition to
+ allow it. */
+ constraints[i] = "=Bf";
+ outputs[i] = gen_rtx_REG (CCmode, FLAGS_REG);
+ saw_asm_flag = true;
+ }
+ else
+ {
+ /* We don't need the flags register as output twice. */
+ constraints[i] = "=X";
+ outputs[i] = gen_rtx_SCRATCH (SImode);
+ }
+
+ rtx x = gen_rtx_REG (mode, FLAGS_REG);
+ x = gen_rtx_fmt_ee (code, QImode, x, const0_rtx);
+
+ machine_mode dest_mode = GET_MODE (dest);
+ if (!SCALAR_INT_MODE_P (dest_mode))
+ {
+ error ("invalid type for asm flag output");
+ continue;
+ }
+ if (dest_mode != QImode)
+ {
+ rtx destqi = gen_reg_rtx (QImode);
+ emit_insn (gen_rtx_SET (destqi, x));
+ x = gen_rtx_ZERO_EXTEND (dest_mode, destqi);
+ }
+ emit_insn (gen_rtx_SET (dest, x));
+ }
+ rtx_insn *seq = get_insns ();
+ end_sequence ();
+
+ if (saw_asm_flag)
+ return seq;
+ else
+ {
+ /* If we had no asm flag outputs, clobber the flags. */
+ clobbers.safe_push (gen_rtx_REG (CCmode, FLAGS_REG));
+ SET_HARD_REG_BIT (clobbered_regs, FLAGS_REG);
+ return NULL;
+ }
}
/* Implements target vector targetm.asm.encode_section_info. */
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 6004681..4221634 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -7578,6 +7578,82 @@ pointed to by @code{e}
in a register, you can enable it to choose the best location
for @code{d} by specifying both constraints.
+@anchor{FlagOutputOperands}
+@subsection Flag Output Operands
+@cindex @code{asm} flag output operands
+
+Some targets have a special register that holds the ``flags'' for the
+result of an operation or comparison. Normally, the contents of that
+register are either unmodifed by the asm, or the asm is considered to
+clobber the contents.
+
+On some targets, a special form of output operand exists by which
+conditions in the flags register may be outputs of the asm. The set of
+conditions supported are target specific, but the general rule is that
+the output variable must be a scalar integer, and the value will be boolean.
+
+Because of the special nature of the flag output operands, the constraint
+may not include alternatives.
+
+Most often, the target has only one flags register, and thus is an implied
+operand of many instructions. In this case, the operand should not be
+referenced within the assembler template via @code{%0} etc, as there's
+no corresponding text in the assembly language.
+
+@table @asis
+@item x86 family
+The flag output constraints for the x86 family are of the form
+@samp{=@@cc@var{cond}} where @var{cond} is one of the standard
+conditions defined in the ISA manual for @code{j@var{cc}} or
+@code{set@var{cc}}.
+
+@table @code
+@item a
+``above'' or unsigned greater than
+@item ae
+``above or equal'' or unsigned greater than or equal
+@item b
+``below'' or unsigned less than
+@item be
+``below or equal'' or unsigned less than or equal
+@item c
+carry flag set
+@item e
+@itemx z
+``equal'' or zero flag set
+@item g
+signed greater than
+@item ge
+signed greater than or equal
+@item l
+signed less than
+@item le
+signed less than or equal
+@item o
+overflow flag set
+@item p
+parity flag set
+@item s
+sign flag set
+@item na
+@itemx nae
+@itemx nb
+@itemx nbe
+@itemx nc
+@itemx ne
+@itemx ng
+@itemx nge
+@itemx nl
+@itemx nle
+@itemx no
+@itemx np
+@itemx ns
+@itemx nz
+``not'' @var{flag}, or inverted versions of those above
+@end table
+
+@end table
+
@anchor{InputOperands}
@subsubsection Input Operands
@cindex @code{asm} input operands
diff --git a/gcc/testsuite/gcc.target/i386/asm-flag-0.c b/gcc/testsuite/gcc.target/i386/asm-flag-0.c
new file mode 100644
index 0000000..b0c0523
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/asm-flag-0.c
@@ -0,0 +1,15 @@
+/* Test error conditions of asm flag outputs. */
+/* { dg-do compile } */
+/* { dg-options "" } */
+
+void a(void)
+{
+ char x;
+ asm("" : "=@cca,@ccc"(x)); /* { dg-error "alternatives not allowed" } */
+}
+
+void b(void)
+{
+ char x;
+ asm("" : "=@ccbad"(x)); /* { dg-error "unknown asm flag output" } */
+}
diff --git a/gcc/testsuite/gcc.target/i386/asm-flag-1.c b/gcc/testsuite/gcc.target/i386/asm-flag-1.c
new file mode 100644
index 0000000..bcc4952
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/asm-flag-1.c
@@ -0,0 +1,18 @@
+/* Test some of the valid @cc<cc> asm flag outputs. */
+/* { dg-do compile } */
+/* { dg-options "-O" } */
+
+void f(char *out)
+{
+ asm("" : "=@cca"(out[0]), "=@ccc"(out[1]), "=@cce"(out[2]),
+ "=@ccg"(out[3]), "=@cco"(out[4]), "=@ccp"(out[5]),
+ "=@ccs"(out[6]));
+}
+
+/* { dg-final { scan-assembler "seta" } } */
+/* { dg-final { scan-assembler "setc" } } */
+/* { dg-final { scan-assembler "sete" } } */
+/* { dg-final { scan-assembler "setg" } } */
+/* { dg-final { scan-assembler "seto" } } */
+/* { dg-final { scan-assembler "setp" } } */
+/* { dg-final { scan-assembler "sets" } } */
diff --git a/gcc/testsuite/gcc.target/i386/asm-flag-2.c b/gcc/testsuite/gcc.target/i386/asm-flag-2.c
new file mode 100644
index 0000000..5f8fa13
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/asm-flag-2.c
@@ -0,0 +1,16 @@
+/* Test some of the valid @cc<cc> asm flag outputs. */
+/* { dg-do compile } */
+/* { dg-options "-O" } */
+
+void f(char *out)
+{
+ asm("" : "=@ccb"(out[0]), "=@ccl"(out[1]), "=@ccz"(out[2]),
+ "=@ccbe"(out[4]), "=@ccge"(out[5]), "=@ccle"(out[6]));
+}
+
+/* { dg-final { scan-assembler "setc" } } */
+/* { dg-final { scan-assembler "setl" } } */
+/* { dg-final { scan-assembler "sete" } } */
+/* { dg-final { scan-assembler "setna" } } */
+/* { dg-final { scan-assembler "setge" } } */
+/* { dg-final { scan-assembler "setle" } } */
diff --git a/gcc/testsuite/gcc.target/i386/asm-flag-3.c b/gcc/testsuite/gcc.target/i386/asm-flag-3.c
new file mode 100644
index 0000000..220c07c
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/asm-flag-3.c
@@ -0,0 +1,22 @@
+/* Test some of the valid @cc<cc> asm flag outputs. */
+/* { dg-do compile } */
+/* { dg-options "-O" } */
+
+#define DO(C) \
+void f##C(int *y) { char x; asm("" : "=@cc"#C(x)); if (!x) *y = 0; }
+
+DO(a)
+DO(c)
+DO(e)
+DO(g)
+DO(o)
+DO(p)
+DO(s)
+
+/* { dg-final { scan-assembler "ja" } } */
+/* { dg-final { scan-assembler "jc" } } */
+/* { dg-final { scan-assembler "je" } } */
+/* { dg-final { scan-assembler "jg" } } */
+/* { dg-final { scan-assembler "jo" } } */
+/* { dg-final { scan-assembler "jp" } } */
+/* { dg-final { scan-assembler "js" } } */
diff --git a/gcc/testsuite/gcc.target/i386/asm-flag-4.c b/gcc/testsuite/gcc.target/i386/asm-flag-4.c
new file mode 100644
index 0000000..b84b7df
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/asm-flag-4.c
@@ -0,0 +1,20 @@
+/* Test some of the valid @cc<cc> asm flag outputs. */
+/* { dg-do compile } */
+/* { dg-options "-O" } */
+
+#define DO(C) \
+void f##C(int *y) { char x; asm("" : "=@cc"#C(x)); if (!x) *y = 0; }
+
+DO(b)
+DO(l)
+DO(z)
+DO(be)
+DO(ge)
+DO(le)
+
+/* { dg-final { scan-assembler "jc" } } */
+/* { dg-final { scan-assembler "jl" } } */
+/* { dg-final { scan-assembler "je" } } */
+/* { dg-final { scan-assembler "jna" } } */
+/* { dg-final { scan-assembler "jge" } } */
+/* { dg-final { scan-assembler "jle" } } */
diff --git a/gcc/testsuite/gcc.target/i386/asm-flag-5.c b/gcc/testsuite/gcc.target/i386/asm-flag-5.c
new file mode 100644
index 0000000..59fb72a
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/asm-flag-5.c
@@ -0,0 +1,19 @@
+/* Test error conditions of asm flag outputs. */
+/* { dg-do compile } */
+/* { dg-options "" } */
+
+#define DO(T) \
+void f_##T(void) { T x; asm("" : "=@ccc"(x)); }
+
+DO(_Bool)
+DO(char)
+DO(short)
+DO(int)
+DO(long)
+
+DO(float) /* { dg-error invalid type } */
+DO(double) /* { dg-error invalid type } */
+
+typedef struct { int x[3]; } S;
+
+DO(S) /* { dg-error invalid type } */
--
2.1.0