@@ -2966,44 +2966,6 @@ expand_asm_loc (tree string, int vol, location_t locus)
emit_insn (body);
}
-/* Return the number of times character C occurs in string S. */
-static int
-n_occurrences (int c, const char *s)
-{
- int n = 0;
- while (*s)
- n += (*s++ == c);
- return n;
-}
-
-/* A subroutine of expand_asm_operands. Check that all operands have
- the same number of alternatives. Return true if so. */
-
-static bool
-check_operand_nalternatives (const vec<const char *> &constraints)
-{
- unsigned len = constraints.length();
- if (len > 0)
- {
- int nalternatives = n_occurrences (',', constraints[0]);
-
- if (nalternatives + 1 > MAX_RECOG_ALTERNATIVES)
- {
- error ("too many alternatives in %<asm%>");
- return false;
- }
-
- for (unsigned i = 1; i < len; ++i)
- if (n_occurrences (',', constraints[i]) != nalternatives)
- {
- error ("operand constraints for %<asm%> differ "
- "in number of alternatives");
- return false;
- }
- }
- return true;
-}
-
/* Check for overlap between registers marked in CLOBBERED_REGS and
anything inappropriate in T. Emit error and return the register
variable definition for error, NULL_TREE for ok. */
@@ -3169,10 +3131,6 @@ expand_asm_stmt (gasm *stmt)
= TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (t)));
}
- /* ??? Diagnose during gimplification? */
- if (! check_operand_nalternatives (constraints))
- return;
-
/* Count the number of meaningful clobbered registers, ignoring what
we would ignore later. */
auto_vec<rtx> clobber_rvec;
@@ -70,6 +70,10 @@ along with GCC; see the file COPYING3. If not see
#include "omp-offload.h"
#include "context.h"
#include "tree-nested.h"
+#include "insn-config.h"
+#include "recog.h"
+#include "output.h"
+#include "gimplify_reg_info.h"
/* Identifier for a basic condition, mapping it to other basic conditions of
its Boolean expression. Basic conditions given the same uid (in the same
@@ -7009,6 +7013,42 @@ gimplify_addr_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p)
return ret;
}
+/* Return the number of times character C occurs in string S. */
+
+static int
+num_occurrences (int c, const char *s)
+{
+ int n = 0;
+ while (*s)
+ n += (*s++ == c);
+ return n;
+}
+
+/* A subroutine of gimplify_asm_expr. Check that all operands have
+ the same number of alternatives. Return -1 if this is violated. Otherwise
+ return the number of alternatives. */
+
+static int
+num_alternatives (const_tree link)
+{
+ if (link == nullptr)
+ return 0;
+
+ const char *constraint = TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (link)));
+ int num = num_occurrences (',', constraint);
+
+ if (num + 1 > MAX_RECOG_ALTERNATIVES)
+ return -1;
+
+ for (link = TREE_CHAIN (link); link; link = TREE_CHAIN (link))
+ {
+ constraint = TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (link)));
+ if (num_occurrences (',', constraint) != num)
+ return -1;
+ }
+ return num + 1;
+}
+
/* Gimplify the operands of an ASM_EXPR. Input operands should be a gimple
value; output operands should be a gimple lvalue. */
@@ -7039,6 +7079,20 @@ gimplify_asm_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p)
clobbers = NULL;
labels = NULL;
+ int num_alternatives_out = num_alternatives (ASM_OUTPUTS (expr));
+ int num_alternatives_in = num_alternatives (ASM_INPUTS (expr));
+ if (num_alternatives_out == -1 || num_alternatives_in == -1
+ || (num_alternatives_out > 0 && num_alternatives_in > 0
+ && num_alternatives_out != num_alternatives_in))
+ {
+ error ("operand constraints for %<asm%> differ "
+ "in number of alternatives");
+ return GS_ERROR;
+ }
+ int num_alternatives = MAX (num_alternatives_out, num_alternatives_in);
+
+ gimplify_reg_info reg_info (num_alternatives, noutputs);
+
ret = GS_ALL_DONE;
link_next = NULL_TREE;
for (i = 0, link = ASM_OUTPUTS (expr); link; ++i, link = link_next)
@@ -7055,8 +7109,9 @@ gimplify_asm_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p)
if (constraint_len == 0)
continue;
- ok = parse_output_constraint (&constraint, i, 0, 0,
- &allows_mem, &allows_reg, &is_inout);
+ reg_info.operand = TREE_VALUE (link);
+ ok = parse_output_constraint (&constraint, i, 0, 0, &allows_mem,
+ &allows_reg, &is_inout, ®_info);
if (!ok)
{
ret = GS_ERROR;
@@ -7227,12 +7282,20 @@ gimplify_asm_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p)
}
link_next = NULL_TREE;
- for (link = ASM_INPUTS (expr); link; ++i, link = link_next)
+ int input_num = 0;
+ for (link = ASM_INPUTS (expr); link; ++input_num, ++i, link = link_next)
{
link_next = TREE_CHAIN (link);
constraint = TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (link)));
- parse_input_constraint (&constraint, 0, 0, noutputs, 0,
- oconstraints, &allows_mem, &allows_reg);
+ reg_info.operand = TREE_VALUE (link);
+ bool ok = parse_input_constraint (&constraint, input_num, 0, noutputs, 0,
+ oconstraints, &allows_mem, &allows_reg,
+ ®_info);
+ if (!ok)
+ {
+ ret = GS_ERROR;
+ is_inout = false;
+ }
/* If we can't make copies, we can only accept memory. */
tree intype = TREE_TYPE (TREE_VALUE (link));
new file mode 100644
@@ -0,0 +1,130 @@
+/* gimplify_reg_info is used during gimplification while walking over
+ operands and their corresponding constraints of asm statements in order to
+ detect errors.
+
+ m_outputs is a mapping between output operands and their potentially used
+ registers, i.e., it describes the union of all register usages over all
+ alternatives and in case of a register asm operand also this one.
+
+ m_alt_out is a mapping describing which registers are potentially used
+ in which alternative over all outputs. Similarly for m_alt_in but over all
+ inputs.
+
+ m_early_clobbered_in_alt is a mapping describing which register is early
+ clobbered in which alternative over all outputs.
+
+ m_early_clobbered_in_output is the counter part to the prior one, i.e., it
+ is a mapping describing which register is early clobbered in which operand
+ over all alternatives. */
+
+#include "regs.h"
+
+class gimplify_reg_info
+{
+ HARD_REG_SET *m_buf;
+ HARD_REG_SET *m_outputs;
+ HARD_REG_SET *m_alt_out;
+ HARD_REG_SET *m_alt_in;
+ HARD_REG_SET *m_early_clobbered_in_alt;
+ HARD_REG_SET *m_early_clobbered_in_output;
+ const unsigned m_num_alternatives;
+ const unsigned m_num_outputs;
+
+public:
+ tree operand;
+
+ gimplify_reg_info (unsigned num_alternatives, unsigned num_outputs)
+ : m_num_alternatives{num_alternatives}, m_num_outputs{num_outputs}
+ {
+ /* If there are no alternatives, then there are no outputs/inputs and there
+ is nothing to do on our end. Thus, we are dealing with a basic asm. */
+ if (num_alternatives == 0)
+ return;
+
+ unsigned buf_size = num_alternatives * 3 + num_outputs * 2;
+ m_buf = new HARD_REG_SET[buf_size];
+ for (unsigned i = 0; i < buf_size; ++i)
+ CLEAR_HARD_REG_SET (m_buf[i]);
+ m_alt_out = &m_buf[0];
+ m_alt_in = &m_buf[num_alternatives];
+ m_early_clobbered_in_alt = &m_buf[num_alternatives * 2];
+ if (num_outputs > 0)
+ {
+ m_outputs = &m_buf[num_alternatives * 3];
+ m_early_clobbered_in_output = &m_buf[num_alternatives * 3 + num_outputs];
+ }
+ else
+ {
+ m_outputs = nullptr;
+ m_early_clobbered_in_output = nullptr;
+ }
+ }
+
+ ~gimplify_reg_info ()
+ {
+ if (m_num_alternatives > 0)
+ delete[] m_buf;
+ }
+
+ void set_out (unsigned alt, unsigned output, int regno)
+ {
+ machine_mode mode = TYPE_MODE (TREE_TYPE (operand));
+ add_to_hard_reg_set (&m_alt_out[alt], mode, regno);
+ add_to_hard_reg_set (&m_outputs[output], mode, regno);
+ }
+
+ void set_in (unsigned alt, int regno)
+ {
+ machine_mode mode = TYPE_MODE (TREE_TYPE (operand));
+ add_to_hard_reg_set (&m_alt_in[alt], mode, regno);
+ }
+
+ bool used_in_alt_out (unsigned alt, int regno) const
+ {
+ gcc_checking_assert (alt < m_num_alternatives);
+ return TEST_HARD_REG_BIT (m_alt_out[alt], regno);
+ }
+
+ bool used_in_alt_in (unsigned alt, int regno) const
+ {
+ gcc_checking_assert (alt < m_num_alternatives);
+ return TEST_HARD_REG_BIT (m_alt_in[alt], regno);
+ }
+
+ void set_early_clobbered (unsigned alt, unsigned output, int regno) const
+ {
+ gcc_checking_assert (alt < m_num_alternatives);
+ gcc_checking_assert (output < m_num_outputs);
+ machine_mode mode = TYPE_MODE (TREE_TYPE (operand));
+ add_to_hard_reg_set (&m_early_clobbered_in_alt[alt], mode, regno);
+ add_to_hard_reg_set (&m_early_clobbered_in_output[output], mode, regno);
+ }
+
+ bool is_early_clobbered_in_alt (unsigned alt, int regno) const
+ {
+ gcc_checking_assert (alt < m_num_alternatives);
+ return TEST_HARD_REG_BIT (m_early_clobbered_in_alt[alt], regno);
+ }
+
+ bool is_early_clobbered_in_any_output_unequal (unsigned operand,
+ int regno) const
+ {
+ gcc_checking_assert (operand < m_num_outputs);
+ for (unsigned op = 0; op < m_num_outputs; ++op)
+ if (op != operand
+ && TEST_HARD_REG_BIT (m_early_clobbered_in_output[op], regno))
+ return true;
+ return false;
+ }
+
+ bool output_coincides (unsigned output, int regno) const
+ {
+ gcc_checking_assert (output < m_num_outputs);
+ HARD_REG_SET singleton;
+ CLEAR_HARD_REG_SET (singleton);
+ machine_mode mode = TYPE_MODE (TREE_TYPE (operand));
+ add_to_hard_reg_set (&singleton, mode, regno);
+ return hard_reg_set_empty_p (m_outputs[output])
+ || m_outputs[output] == singleton;
+ }
+};
@@ -39,6 +39,8 @@ along with GCC; see the file COPYING3. If not see
#include "emit-rtl.h"
#include "pretty-print.h"
#include "diagnostic-core.h"
+#include "output.h"
+#include "gimplify_reg_info.h"
#include "fold-const.h"
#include "varasm.h"
@@ -201,6 +203,51 @@ decode_hard_reg_constraint (const char *begin)
return regno;
}
+static bool
+eliminable_regno_p (int regnum)
+{
+ static const struct
+ {
+ const int from;
+ const int to;
+ } eliminables[] = ELIMINABLE_REGS;
+ for (size_t i = 0; i < ARRAY_SIZE (eliminables); i++)
+ if (regnum == eliminables[i].from)
+ return true;
+ return false;
+}
+
+/* Perform a similar check as done in make_decl_rtl(). */
+
+static bool
+hardreg_ok_p (int reg_number, machine_mode mode, int operand_num)
+{
+ if (mode == BLKmode)
+ error ("data type isn%'t suitable for register %s of operand %i",
+ reg_names[reg_number], operand_num);
+ else if (!in_hard_reg_set_p (accessible_reg_set, mode, reg_number))
+ error ("register %s for operand %i cannot be accessed"
+ " by the current target", reg_names[reg_number], operand_num);
+ else if (!in_hard_reg_set_p (operand_reg_set, mode, reg_number))
+ error ("register %s for operand %i is not general enough"
+ " to be used as a register variable", reg_names[reg_number], operand_num);
+ else if (!targetm.hard_regno_mode_ok (reg_number, mode))
+ error ("register %s for operand %i isn%'t suitable for data type",
+ reg_names[reg_number], operand_num);
+ else if (reg_number != HARD_FRAME_POINTER_REGNUM
+ && (reg_number == FRAME_POINTER_REGNUM
+#ifdef RETURN_ADDRESS_POINTER_REGNUM
+ || reg_number == RETURN_ADDRESS_POINTER_REGNUM
+#endif
+ || reg_number == ARG_POINTER_REGNUM)
+ && eliminable_regno_p (reg_number))
+ error ("register for operand %i is an internal GCC "
+ "implementation detail", operand_num);
+ else
+ return true;
+ return false;
+}
+
/* Parse the output constraint pointed to by *CONSTRAINT_P. It is the
OPERAND_NUMth output operand, indexed from zero. There are NINPUTS
inputs and NOUTPUTS outputs to this extended-asm. Upon return,
@@ -217,7 +264,8 @@ decode_hard_reg_constraint (const char *begin)
bool
parse_output_constraint (const char **constraint_p, int operand_num,
int ninputs, int noutputs, bool *allows_mem,
- bool *allows_reg, bool *is_inout)
+ bool *allows_reg, bool *is_inout,
+ gimplify_reg_info *reg_info)
{
const char *constraint = *constraint_p;
const char *p;
@@ -271,6 +319,9 @@ parse_output_constraint (const char **constraint_p, int operand_num,
constraint = *constraint_p;
}
+ unsigned int alt = 0;
+ bool early_clobbered = false;
+
/* Loop through the constraint string. */
for (p = constraint + 1; *p; )
{
@@ -290,12 +341,21 @@ parse_output_constraint (const char **constraint_p, int operand_num,
}
break;
- case '?': case '!': case '*': case '&': case '#':
+ case '&':
+ early_clobbered = true;
+ break;
+
+ case '?': case '!': case '*': case '#':
case '$': case '^':
case 'E': case 'F': case 'G': case 'H':
case 's': case 'i': case 'n':
case 'I': case 'J': case 'K': case 'L': case 'M':
- case 'N': case 'O': case 'P': case ',':
+ case 'N': case 'O': case 'P':
+ break;
+
+ case ',':
+ ++alt;
+ early_clobbered = false;
break;
case '0': case '1': case '2': case '3': case '4':
@@ -318,6 +378,47 @@ parse_output_constraint (const char **constraint_p, int operand_num,
case '{':
{
+ if (!targetm.lra_p ())
+ {
+ error ("hard register constraints are only supported while using LRA");
+ return false;
+ }
+ if (reg_info)
+ {
+ int regno = decode_hard_reg_constraint (p);
+ if (regno < 0)
+ {
+ error ("invalid output constraint: %s", p);
+ return false;
+ }
+ if (reg_info->used_in_alt_out (alt, regno))
+ {
+ error ("multiple outputs to hard register: %s",
+ reg_names[regno]);
+ return false;
+ }
+ else
+ {
+ reg_info->set_out (alt, operand_num, regno);
+ if (early_clobbered)
+ reg_info->set_early_clobbered (alt, operand_num, regno);
+ }
+ if (VAR_P (reg_info->operand)
+ && DECL_HARD_REGISTER (reg_info->operand))
+ {
+ tree id = DECL_ASSEMBLER_NAME (reg_info->operand);
+ const char *asmspec = IDENTIFIER_POINTER (id) + 1;
+ int regno_op = decode_reg_name (asmspec);
+ if (regno != regno_op)
+ {
+ error ("constraint and register %<asm%> for output operand %i are unsatisfiable", operand_num);
+ return false;
+ }
+ }
+ machine_mode mode = TYPE_MODE (TREE_TYPE (reg_info->operand));
+ if (!hardreg_ok_p (regno, mode, operand_num))
+ return false;
+ }
*allows_reg = true;
break;
}
@@ -333,6 +434,34 @@ parse_output_constraint (const char **constraint_p, int operand_num,
*allows_mem = true;
else
insn_extra_constraint_allows_reg_mem (cn, allows_reg, allows_mem);
+ if (reg_info && *allows_reg
+ && VAR_P (reg_info->operand)
+ && DECL_HARD_REGISTER (reg_info->operand))
+ {
+ tree id = DECL_ASSEMBLER_NAME (reg_info->operand);
+ const char *asmspec = IDENTIFIER_POINTER (id) + 1;
+ int regno = decode_reg_name (asmspec);
+ if (regno < 0)
+ {
+ error ("invalid register name for %q+D", reg_info->operand);
+ return false;
+ }
+ if (reg_info->used_in_alt_out (alt, regno))
+ {
+ error ("multiple outputs to hard register: %s",
+ reg_names[regno]);
+ return false;
+ }
+ else
+ {
+ reg_info->set_out (alt, operand_num, regno);
+ if (early_clobbered)
+ reg_info->set_early_clobbered (alt, operand_num, regno);
+ }
+ machine_mode mode = TYPE_MODE (TREE_TYPE (reg_info->operand));
+ if (!hardreg_ok_p (regno, mode, operand_num))
+ return false;
+ }
break;
}
@@ -350,7 +479,8 @@ bool
parse_input_constraint (const char **constraint_p, int input_num,
int ninputs, int noutputs, int ninout,
const char * const * constraints,
- bool *allows_mem, bool *allows_reg)
+ bool *allows_mem, bool *allows_reg,
+ gimplify_reg_info *reg_info)
{
const char *constraint = *constraint_p;
const char *orig_constraint = constraint;
@@ -365,6 +495,9 @@ parse_input_constraint (const char **constraint_p, int input_num,
/* Make sure constraint has neither `=', `+', nor '&'. */
+ unsigned int alt = 0;
+ unsigned long match = 0;
+
for (j = 0; j < c_len; j += CONSTRAINT_LEN (constraint[j], constraint+j))
switch (constraint[j])
{
@@ -391,7 +524,11 @@ parse_input_constraint (const char **constraint_p, int input_num,
case 'E': case 'F': case 'G': case 'H':
case 's': case 'i': case 'n':
case 'I': case 'J': case 'K': case 'L': case 'M':
- case 'N': case 'O': case 'P': case ',':
+ case 'N': case 'O': case 'P':
+ break;
+
+ case ',':
+ ++alt;
break;
/* Whether or not a numeric constraint allows a register is
@@ -403,7 +540,6 @@ parse_input_constraint (const char **constraint_p, int input_num,
case '5': case '6': case '7': case '8': case '9':
{
char *end;
- unsigned long match;
saw_match = true;
@@ -443,6 +579,49 @@ parse_input_constraint (const char **constraint_p, int input_num,
case '{':
{
+ if (!targetm.lra_p ())
+ {
+ error ("hard register constraints are only supported while using LRA");
+ return false;
+ }
+ if (reg_info)
+ {
+ int regno = decode_hard_reg_constraint (constraint + j);
+ if (regno < 0)
+ {
+ error ("invalid input constraint: %s", constraint + j);
+ return false;
+ }
+ if (reg_info->used_in_alt_in (alt, regno))
+ {
+ error ("multiple inputs to hard register: %s",
+ reg_names[regno]);
+ return false;
+ }
+ else
+ reg_info->set_in (alt, regno);
+ if (constraint == orig_constraint
+ && reg_info->is_early_clobbered_in_alt (alt, regno))
+ {
+ error ("invalid hard register usage between earlyclobber operand and input operand");
+ return false;
+ }
+ if (VAR_P (reg_info->operand)
+ && DECL_HARD_REGISTER (reg_info->operand))
+ {
+ tree id = DECL_ASSEMBLER_NAME (reg_info->operand);
+ const char *asmspec = IDENTIFIER_POINTER (id) + 1;
+ int regno_op = decode_reg_name (asmspec);
+ if (regno != regno_op)
+ {
+ error ("constraint and register %<asm%> for input operand %i are unsatisfiable", input_num);
+ return false;
+ }
+ }
+ machine_mode mode = TYPE_MODE (TREE_TYPE (reg_info->operand));
+ if (!hardreg_ok_p (regno, mode, input_num))
+ return false;
+ }
*allows_reg = true;
break;
}
@@ -463,6 +642,44 @@ parse_input_constraint (const char **constraint_p, int input_num,
*allows_mem = true;
else
insn_extra_constraint_allows_reg_mem (cn, allows_reg, allows_mem);
+ if (reg_info && *allows_reg
+ && VAR_P (reg_info->operand)
+ && DECL_HARD_REGISTER (reg_info->operand))
+ {
+ tree id = DECL_ASSEMBLER_NAME (reg_info->operand);
+ const char *asmspec = IDENTIFIER_POINTER (id) + 1;
+ int regno = decode_reg_name (asmspec);
+ if (regno < 0)
+ {
+ error ("invalid register name for %q+D", reg_info->operand);
+ return false;
+ }
+ if (reg_info->used_in_alt_in (alt, regno))
+ {
+ error ("multiple inputs to hard register: %s",
+ reg_names[regno]);
+ return false;
+ }
+ else
+ reg_info->set_in (alt, regno);
+ if ((constraint == orig_constraint
+ && reg_info->is_early_clobbered_in_alt (alt, regno))
+ || (constraint != orig_constraint
+ && reg_info->is_early_clobbered_in_any_output_unequal
+ (match, regno)))
+ {
+ error ("invalid hard register usage between earlyclobber operand and input operand");
+ return false;
+ }
+ if (saw_match && !reg_info->output_coincides (match, regno))
+ {
+ error ("invalid hard register usage between output operand and matching constraint operand");
+ return false;
+ }
+ machine_mode mode = TYPE_MODE (TREE_TYPE (reg_info->operand));
+ if (!hardreg_ok_p (regno, mode, input_num))
+ return false;
+ }
break;
}
@@ -20,11 +20,15 @@ along with GCC; see the file COPYING3. If not see
#ifndef GCC_STMT_H
#define GCC_STMT_H
+class gimplify_reg_info;
+
extern void expand_label (tree);
extern bool parse_output_constraint (const char **, int, int, int,
- bool *, bool *, bool *);
+ bool *, bool *, bool *,
+ gimplify_reg_info * = nullptr);
extern bool parse_input_constraint (const char **, int, int, int, int,
- const char * const *, bool *, bool *);
+ const char * const *, bool *, bool *,
+ gimplify_reg_info * = nullptr);
extern int decode_hard_reg_constraint (const char *);
extern tree resolve_asm_operand_names (tree, tree, tree, tree);
#ifdef HARD_CONST
new file mode 100644
@@ -0,0 +1,83 @@
+/* { dg-do compile { target aarch64*-*-* arm*-*-* i?86-*-* powerpc*-*-* riscv*-*-* s390*-*-* x86_64-*-* } } */
+
+#if defined (__aarch64__)
+# define GPR1_RAW "x0"
+# define GPR2 "{x1}"
+# define GPR3 "{x2}"
+# define INVALID_GPR_A "{x31}"
+#elif defined (__arm__)
+# define GPR1_RAW "r0"
+# define GPR2 "{r1}"
+# define GPR3 "{r2}"
+# define INVALID_GPR_A "{r16}"
+#elif defined (__i386__)
+# define GPR1_RAW "%eax"
+# define GPR2 "{%ebx}"
+# define GPR3 "{%edx}"
+# define INVALID_GPR_A "{%eex}"
+#elif defined (__powerpc__) || defined (__POWERPC__)
+# define GPR1_RAW "r4"
+# define GPR2 "{r5}"
+# define GPR3 "{r6}"
+# define INVALID_GPR_A "{r33}"
+#elif defined (__riscv)
+# define GPR1_RAW "t4"
+# define GPR2 "{t5}"
+# define GPR3 "{t6}"
+# define INVALID_GPR_A "{t7}"
+#elif defined (__s390__)
+# define GPR1_RAW "r4"
+# define GPR2 "{r5}"
+# define GPR3 "{r6}"
+# define INVALID_GPR_A "{r17}"
+#elif defined (__x86_64__)
+# define GPR1_RAW "rax"
+# define GPR2 "{rbx}"
+# define GPR3 "{rcx}"
+# define INVALID_GPR_A "{rex}"
+#endif
+
+#define GPR1 "{"GPR1_RAW"}"
+#define INVALID_GPR_B "{"GPR1_RAW
+
+struct { int a[128]; } s = {0};
+
+void
+test (void)
+{
+ int x, y;
+ register int gpr1 __asm__ (GPR1_RAW) = 0;
+
+ __asm__ ("" :: "{}" (42)); /* { dg-error "invalid input constraint: \{\}" } */
+ __asm__ ("" :: INVALID_GPR_A (42)); /* { dg-error "invalid input constraint" } */
+ __asm__ ("" :: INVALID_GPR_B (42)); /* { dg-error "invalid input constraint" } */
+
+ __asm__ ("" :: GPR1 (s)); /* { dg-error "data type isn't suitable for register .* of operand 0" } */
+
+ __asm__ ("" :: "r" (gpr1), GPR1 (42)); /* { dg-error "multiple inputs to hard register" } */
+ __asm__ ("" :: GPR1 (42), "r" (gpr1)); /* { dg-error "multiple inputs to hard register" } */
+ __asm__ ("" :: GPR1 (42), GPR1 (42)); /* { dg-error "multiple inputs to hard register" } */
+ __asm__ ("" :: GPR1","GPR2 (42), GPR2","GPR3 (42));
+ __asm__ ("" :: GPR1","GPR2 (42), GPR3","GPR2 (42)); /* { dg-error "multiple inputs to hard register" } */
+ __asm__ ("" :: GPR1","GPR2 (42), GPR1","GPR3 (42)); /* { dg-error "multiple inputs to hard register" } */
+ __asm__ ("" :: GPR1 GPR2 (42), GPR2 (42)); /* { dg-error "multiple inputs to hard register" } */
+ __asm__ ("" : "+"GPR1 (x), "="GPR1 (y)); /* { dg-error "multiple outputs to hard register" } */
+ __asm__ ("" : "="GPR1 (y) : GPR1 (42), "0" (42)); /* { dg-error "multiple inputs to hard register" } */
+ __asm__ ("" : "+"GPR1 (x) : GPR1 (42)); /* { dg-error "multiple inputs to hard register" } */
+
+ __asm__ ("" : "="GPR1 (gpr1));
+ __asm__ ("" : "="GPR2 (gpr1)); /* { dg-error "constraint and register 'asm' for output operand 0 are unsatisfiable" } */
+ __asm__ ("" :: GPR2 (gpr1)); /* { dg-error "constraint and register 'asm' for input operand 0 are unsatisfiable" } */
+ __asm__ ("" : "="GPR1 (x) : "0" (gpr1));
+ __asm__ ("" : "="GPR1 GPR2 (x) : "0" (gpr1)); /* { dg-error "constraint and register 'asm' for input operand 0 are unsatisfiable" } */
+
+ __asm__ ("" : "=&"GPR1 (x) : "0" (gpr1));
+ __asm__ ("" : "=&"GPR1 (x) : "0" (42));
+ __asm__ ("" : "=&"GPR2","GPR1 (x) : "r,"GPR1 (42));
+ __asm__ ("" : "="GPR2",&"GPR1 (x) : "r,"GPR1 (42)); /* { dg-error "invalid hard register usage between earlyclobber operand and input operand" } */
+ __asm__ ("" : "=&"GPR1 (x) : GPR1 (42)); /* { dg-error "invalid hard register usage between earlyclobber operand and input operand" } */
+ __asm__ ("" : "=&"GPR2","GPR1 (x) : "r,r" (gpr1));
+ __asm__ ("" : "="GPR2",&"GPR1 (x) : "r,r" (gpr1)); /* { dg-error "invalid hard register usage between earlyclobber operand and input operand" } */
+ __asm__ ("" : "=&r" (gpr1) : GPR1 (42)); /* { dg-error "invalid hard register usage between earlyclobber operand and input operand" } */
+ __asm__ ("" : "=&"GPR1 (x), "=r" (y) : "1" (gpr1)); /* { dg-error "invalid hard register usage between earlyclobber operand and input operand" } */
+}
new file mode 100644
@@ -0,0 +1,20 @@
+/* { dg-do compile { target { { aarch64*-*-* s390x-*-* } && int128 } } } */
+/* { dg-options "-O2" } get rid of -ansi since we use __int128 */
+
+/* Test register pairs. */
+
+#if defined (__aarch64__)
+# define GPR1 "{x4}"
+# define GPR2 "{x5}"
+#elif defined (__s390__)
+# define GPR1 "{r4}"
+# define GPR2 "{r5}"
+#endif
+
+void
+test (void)
+{
+ __asm__ ("" :: GPR1 ((__int128) 42));
+ __asm__ ("" :: GPR2 ((__int128) 42)); /* { dg-error "register .* for operand 0 isn't suitable for data type" } */
+ __asm__ ("" :: GPR1 ((__int128) 42), GPR2 (42)); /* { dg-error "multiple inputs to hard register" } */
+}
new file mode 100644
@@ -0,0 +1,21 @@
+/* { dg-do compile { target arm*-*-* s390-*-* } } */
+/* { dg-options "-std=c99" } we need long long */
+/* { dg-additional-options "-march=armv8-a" { target arm*-*-* } } */
+
+/* Test register pairs. */
+
+#if defined (__arm__)
+# define GPR1 "{r4}"
+# define GPR2 "{r5}"
+#elif defined (__s390__)
+# define GPR1 "{r4}"
+# define GPR2 "{r5}"
+#endif
+
+void
+test (void)
+{
+ __asm__ ("" :: GPR1 (42ll));
+ __asm__ ("" :: GPR2 (42ll)); /* { dg-error "register .* for operand 0 isn't suitable for data type" } */
+ __asm__ ("" :: GPR1 (42ll), GPR2 (42)); /* { dg-error "multiple inputs to hard register" } */
+}
@@ -11,34 +11,6 @@ test0 (void)
{
register long var1 asm (REG1);
register long var2 asm (REG1);
- asm ("blah %0 %1" : "=r" (var1), "=r" (var2)); /* { dg-error "invalid hard register usage between output operands" } */
+ asm ("blah %0 %1" : "=r" (var1), "=r" (var2)); /* { dg-error "multiple outputs to hard register" } */
return var1;
}
-
-long
-test1 (void)
-{
- register long var1 asm (REG1);
- register long var2 asm (REG2);
- asm ("blah %0 %1" : "=r" (var1) : "0" (var2)); /* { dg-error "invalid hard register usage between output operand and matching constraint operand" } */
- return var1;
-}
-
-long
-test2 (void)
-{
- register long var1 asm (REG1);
- register long var2 asm (REG1);
- asm ("blah %0 %1" : "=&r" (var1) : "r" (var2)); /* { dg-error "invalid hard register usage between earlyclobber operand and input operand" } */
- return var1;
-}
-
-long
-test3 (void)
-{
- register long var1 asm (REG1);
- register long var2 asm (REG1);
- long var3;
- asm ("blah %0 %1" : "=&r" (var1), "=r" (var3) : "1" (var2)); /* { dg-error "invalid hard register usage between earlyclobber operand and input operand" } */
- return var1 + var3;
-}
new file mode 100644
@@ -0,0 +1,35 @@
+/* PR rtl-optimization/87600 */
+/* { dg-do compile { target aarch64*-*-* arm*-*-* i?86-*-* powerpc*-*-* s390*-*-* x86_64-*-* } } */
+/* { dg-options "-O2" } */
+
+#include "pr87600.h"
+
+/* The following are all invalid uses of local register variables. */
+
+long
+test1 (void)
+{
+ register long var1 asm (REG1);
+ register long var2 asm (REG2);
+ asm ("blah %0 %1" : "=r" (var1) : "0" (var2)); /* { dg-error "invalid hard register usage between output operand and matching constraint operand" } */
+ return var1;
+}
+
+long
+test2 (void)
+{
+ register long var1 asm (REG1);
+ register long var2 asm (REG1);
+ asm ("blah %0 %1" : "=&r" (var1) : "r" (var2)); /* { dg-error "invalid hard register usage between earlyclobber operand and input operand" } */
+ return var1;
+}
+
+long
+test3 (void)
+{
+ register long var1 asm (REG1);
+ register long var2 asm (REG1);
+ long var3;
+ asm ("blah %0 %1" : "=&r" (var1), "=r" (var3) : "1" (var2)); /* { dg-error "invalid hard register usage between earlyclobber operand and input operand" } */
+ return var1 + var3;
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,103 @@
+/* { dg-do compile { target { lp64 } } } */
+/* { dg-options "-O2 -march=z13 -mzarch" } */
+/* { dg-final { check-function-bodies "**" "" "" } } */
+
+/*
+** test_in_1:
+** foo %r2
+** br %r14
+*/
+
+int
+test_in_1 (int x)
+{
+ asm ("foo %0" :: "{r2}" (x));
+ return x;
+}
+
+/*
+** test_in_2:
+** lgr (%r[0-9]+),%r2
+** lr %r2,%r3
+** foo %r2
+** lgr %r2,\1
+** br %r14
+*/
+
+int
+test_in_2 (int x, int y)
+{
+ asm ("foo %0" :: "{r2}" (y));
+ return x;
+}
+
+/*
+** test_in_3:
+** stmg %r12,%r15,96\(%r15\)
+** lay %r15,-160\(%r15\)
+** lgr (%r[0-9]+),%r2
+** ahi %r2,1
+** lgfr %r2,%r2
+** brasl %r14,foo@PLT
+** lr %r3,%r2
+** lr %r2,\1
+** foo %r3,%r2
+** lgr %r2,\1
+** lmg %r12,%r15,256\(%r15\)
+** br %r14
+*/
+
+extern int foo (int);
+
+int
+test_in_3 (int x)
+{
+ asm ("foo %0,%1\n" :: "{r3}" (foo (x + 1)), "{r2}" (x));
+ return x;
+}
+
+/*
+** test_out_1:
+** foo %r3
+** lgfr %r2,%r3
+** br %r14
+*/
+
+int
+test_out_1 (void)
+{
+ int x;
+ asm ("foo %0" : "={r3}" (x));
+ return x;
+}
+
+/*
+** test_out_2:
+** lgr (%r[0-9]+),%r2
+** foo %r2
+** ark (%r[0-9]+),\1,%r2
+** lgfr %r2,\2
+** br %r14
+*/
+
+int
+test_out_2 (int x)
+{
+ int y;
+ asm ("foo %0" : "={r2}" (y));
+ return x + y;
+}
+
+/*
+** test_inout_1:
+** foo %r2
+** lgfr %r2,%r2
+** br %r14
+*/
+
+int
+test_inout_1 (int x)
+{
+ asm ("foo %0" : "+{r2}" (x));
+ return x;
+}
new file mode 100644
@@ -0,0 +1,43 @@
+/* { dg-do compile { target { lp64 } } } */
+/* { dg-options "-O2 -march=z13 -mzarch" } */
+/* { dg-final { check-function-bodies "**" "" "" } } */
+/* { dg-final { scan-assembler {\.LC0:\n\t\.long\t1078523331\n} } } */
+
+
+/*
+** test_float_into_gpr:
+** lrl %r4,.LC0
+** foo %r4
+** br %r14
+*/
+
+void
+test_float_into_gpr (void)
+{
+ // This is the counterpart to
+ // register float x asm ("r4") = 3.14f;
+ // asm ("foo %0" :: "r" (x));
+ // where the bit-pattern of 3.14f is loaded into GPR.
+ asm ("foo %0" :: "{r4}" (3.14f));
+}
+
+/*
+** test_float:
+** (
+** ldr %f4,%f0
+** ldr %f5,%f2
+** |
+** ldr %f5,%f2
+** ldr %f4,%f0
+** )
+** aebr %f5,%f4
+** ldr %f0,%f5
+** br %r14
+*/
+
+float
+test_float (float x, float y)
+{
+ asm ("aebr %0,%1" : "+{f5}" (y) : "{f4}" (x));
+ return y;
+}
new file mode 100644
@@ -0,0 +1,42 @@
+/* { dg-do compile { target lp64 } } */
+/* { dg-options "-O2 -march=z13 -mzarch" } */
+/* { dg-final { check-function-bodies "**" "" "" } } */
+/* { dg-final { scan-assembler {\.LC0:\n\t\.long\t1074339512\n\t\.long\t1374389535\n} } } */
+
+/*
+** test_double_into_gpr:
+** lgrl %r4,.LC0
+** foo %r4
+** br %r14
+*/
+
+void
+test_double_into_gpr (void)
+{
+ // This is the counterpart to
+ // register double x asm ("r4") = 3.14;
+ // asm ("foo %0" :: "r" (x));
+ // where the bit-pattern of 3.14 is loaded into GPR.
+ asm ("foo %0" :: "{r4}" (3.14));
+}
+
+/*
+** test_double:
+** (
+** ldr %f4,%f0
+** ldr %f5,%f2
+** |
+** ldr %f5,%f2
+** ldr %f4,%f0
+** )
+** adbr %f5,%f4
+** ldr %f0,%f5
+** br %r14
+*/
+
+double
+test_double (double x, double y)
+{
+ asm ("adbr %0,%1" : "+{f5}" (y) : "{f4}" (x));
+ return y;
+}
@@ -896,6 +896,10 @@ proc configure_check-function-bodies { config } {
set up_config(fluff) {^\s*(?://)}
} elseif { [istarget *-*-darwin*] } {
set up_config(fluff) {^\s*(?:\.|//|@)|^L[0-9ABCESV]}
+ } elseif { [istarget s390*-*-*] } {
+ # Additionally to the defaults skip lines beginning with a # resulting
+ # from inline asm.
+ set up_config(fluff) {^\s*(?:\.|//|@|$|#)}
} else {
# Skip lines beginning with labels ('.L[...]:') or other directives
# ('.align', '.cfi_startproc', '.quad [...]', '.text', etc.), '//' or